My Custom Fan App for PH16-71
This commit is contained in:
commit
36eab3868f
1 changed files with 344 additions and 0 deletions
325
main.cpp
Normal file
325
main.cpp
Normal file
|
|
@ -0,0 +1,325 @@
|
|||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <chrono>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <iomanip>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <cstring>
|
||||
|
||||
#include <QObject>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QString>
|
||||
#include <QCoreApplication>
|
||||
#include <QProcess>
|
||||
#include <QFile>
|
||||
#include <QTimer>
|
||||
|
||||
|
||||
|
||||
std::string get_current_time() {
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
std::time_t ctime_now = std::chrono::system_clock::to_time_t(now);
|
||||
std::stringstream ss;
|
||||
ss << "[" << std::put_time(std::localtime(&ctime_now), "%d-%m-%y %H:%M:%S") << "] ";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
std::string get_platform_profile() {
|
||||
std::fstream platform_profile("/sys/firmware/acpi/platform_profile", std::fstream::in);
|
||||
std::string temp;
|
||||
platform_profile >> temp;
|
||||
return temp;
|
||||
}
|
||||
|
||||
int read_fans() {
|
||||
std::fstream dev_reader("/dev/acer-fan-8", std::fstream::in);
|
||||
std::string current_value;
|
||||
std::string data;
|
||||
int fan_value = -1;
|
||||
char ch[2];
|
||||
dev_reader.read(ch, 1);
|
||||
ch[1] = '\0';
|
||||
current_value = ch;
|
||||
std::stringstream s2;
|
||||
s2 << current_value;
|
||||
s2.flush();
|
||||
s2 >> fan_value;
|
||||
s2.flush();
|
||||
return fan_value;
|
||||
}
|
||||
|
||||
void deactivate_fans() {
|
||||
const char *ab = "0";
|
||||
auto file = open("/dev/acer-fan-8", O_WRONLY);
|
||||
if (file < 0) {
|
||||
std::cout << get_current_time() << "Open failed" << std::endl;
|
||||
fprintf(stderr, "errno = %d (%s)\n", errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
write(file, ab, strlen(ab));
|
||||
close(file);
|
||||
std::cout << get_current_time() << "Fans slowed." << std::endl;
|
||||
}
|
||||
|
||||
void activate_fans() {
|
||||
const char *ab = "1";
|
||||
auto file = open("/dev/acer-fan-8", O_WRONLY);
|
||||
if (file < 0) {
|
||||
std::cout << "Open failed" << std::endl;
|
||||
fprintf(stderr, "errno = %d (%s)\n", errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
write(file, ab, strlen(ab));
|
||||
close(file);
|
||||
std::cout << get_current_time() << "Fans activated." << std::endl;
|
||||
}
|
||||
|
||||
std::vector<int> read_cpu_temp(std::vector<std::string> paths) {
|
||||
std::vector<int> cpu_temps;
|
||||
for(auto &i: paths) {
|
||||
std::filesystem::path file_path = std::filesystem::path(i) / "temp";
|
||||
std::fstream filein(file_path, std::fstream::in);
|
||||
if(!filein.is_open()) {
|
||||
std::cout << "Failed to open file: " << file_path << std::endl;
|
||||
}
|
||||
std::stringstream ss;
|
||||
std::string temp;
|
||||
int current_temp;
|
||||
while(std::getline(filein, temp)) {
|
||||
ss << temp;
|
||||
}
|
||||
filein.flush();
|
||||
filein.close();
|
||||
ss >> current_temp;
|
||||
ss.flush();
|
||||
cpu_temps.push_back(current_temp);
|
||||
}
|
||||
return cpu_temps;
|
||||
}
|
||||
|
||||
int read_gpu_temp() {
|
||||
QProcess *proc = new QProcess();
|
||||
QStringList args;
|
||||
args << "--query-gpu=temperature.gpu" << "--format=csv,noheader" << "-f" << "nvidia-temp.txt";
|
||||
proc->start("nvidia-smi", args);
|
||||
proc->waitForFinished();
|
||||
delete proc;
|
||||
std::fstream temp_in("nvidia-temp.txt", std::fstream::in);
|
||||
if(!temp_in.is_open()) {
|
||||
std::cout << "Could not read temperature info from nvidia smi exiting..." << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
std::string string_temperature;
|
||||
std::string temp;
|
||||
int int_temperature = -1;
|
||||
while(std::getline(temp_in, temp)) {
|
||||
string_temperature += temp;
|
||||
}
|
||||
temp_in.close();
|
||||
std::stringstream ss;
|
||||
ss << string_temperature;
|
||||
ss.flush();
|
||||
ss >> int_temperature;
|
||||
ss.flush();
|
||||
return int_temperature;
|
||||
}
|
||||
|
||||
void write_cpu(std::filesystem::path path, std::string text) {
|
||||
auto file = open(path.c_str(), O_WRONLY);
|
||||
if (file < 0) {
|
||||
std::cout << "Open failed" << std::endl;
|
||||
fprintf(stderr, "errno = %d (%s)\n", errno, strerror(errno));
|
||||
return;
|
||||
}
|
||||
write(file, text.c_str(), strlen(text.c_str()));
|
||||
close(file);
|
||||
}
|
||||
|
||||
bool slow_cpus() {
|
||||
int low = 16;
|
||||
int high = 31;
|
||||
std::string cpu_base_freq_path = "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_max_freq";
|
||||
std::string cpu_base_gov_path = "/sys/devices/system/cpu/cpu{}/cpufreq/energy_performance_preference";
|
||||
for(int i=low;i<=high;i++)
|
||||
{
|
||||
std::string cpu_freq_path = std::vformat(cpu_base_freq_path, std::make_format_args(i));
|
||||
std::string cpu_gov_path = std::vformat(cpu_base_gov_path, std::make_format_args(i));
|
||||
write_cpu(cpu_freq_path, "2400000");
|
||||
write_cpu(cpu_gov_path, "balance_power");
|
||||
std::cout << get_current_time() << std::vformat("Changed CPU {} to 2.4GHz and cpu gov to balance power.", std::make_format_args(i)) << std::endl;;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool reactivate_slowed_cpus() {
|
||||
int low = 16;
|
||||
int high = 31;
|
||||
std::string cpu_base_freq_path = "/sys/devices/system/cpu/cpu{}/cpufreq/scaling_max_freq";
|
||||
std::string cpu_base_gov_path = "/sys/devices/system/cpu/cpu{}/cpufreq/energy_performance_preference";
|
||||
for(int i=low;i<=high;i++)
|
||||
{
|
||||
std::string cpu_freq_path = std::vformat(cpu_base_freq_path, std::make_format_args(i));
|
||||
std::string cpu_gov_path = std::vformat(cpu_base_gov_path, std::make_format_args(i));
|
||||
write_cpu(cpu_freq_path, "3900000");
|
||||
write_cpu(cpu_gov_path, "performance");
|
||||
std::cout << get_current_time() << std::vformat("Changed CPU {} to 3.9GHz and cpu gov to performance.", std::make_format_args(i)) << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
|
||||
std::stringstream sr;
|
||||
sr << argv[0];
|
||||
sr.flush();
|
||||
std::string log_path = "/home/ud/logs/CustomFanApp.log";
|
||||
std::cout << "Log path: " << log_path << std::endl;
|
||||
uintmax_t log_size = 0;
|
||||
uintmax_t max_size = 10 * 1024 * 1024; // MB
|
||||
int cpu_slow_threshold = 70; // CPU Temperature at which to reduce clock speed of E-Cores and change them to power-save
|
||||
int cpu_fast_when_slowed_threshold = 62;
|
||||
if(argc >= 3) {
|
||||
std::stringstream ss;
|
||||
ss << argv[2];
|
||||
ss >> cpu_slow_threshold;
|
||||
ss.flush();
|
||||
ss.clear();
|
||||
}
|
||||
if(argc >= 4) {
|
||||
std::stringstream ss;
|
||||
ss << argv[3];
|
||||
ss >> cpu_fast_when_slowed_threshold;
|
||||
ss.flush();
|
||||
ss.clear();
|
||||
}
|
||||
|
||||
bool cpu_slowed = false;
|
||||
|
||||
if(std::filesystem::exists(log_path)) {
|
||||
log_size = std::filesystem::file_size(log_path);
|
||||
std::cout << "Current log file size: " << log_size << std::endl;
|
||||
}
|
||||
std::fstream log_out;
|
||||
if(log_size > max_size)
|
||||
{
|
||||
log_out.open(log_path, std::fstream::out);
|
||||
if(!log_out.is_open()) {
|
||||
std::cerr << "Could not open log file!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout.rdbuf(log_out.rdbuf());
|
||||
std::cout << get_current_time() << "---- Truncating log file size > 10 Mb. ----" << std::endl;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_out.open(log_path, std::fstream::app);
|
||||
if(!log_out.is_open()) {
|
||||
std::cerr << "Could not open log file!" << std::endl;
|
||||
}
|
||||
else {
|
||||
std::cout.rdbuf(log_out.rdbuf());
|
||||
}
|
||||
}
|
||||
|
||||
QCoreApplication app(argc, argv);
|
||||
QTimer log_flusher;
|
||||
log_flusher.setSingleShot(false);
|
||||
log_flusher.setInterval(5000);
|
||||
QObject::connect(&log_flusher, &QTimer::timeout, [&](){
|
||||
log_out.flush();
|
||||
std::cout.flush();
|
||||
});
|
||||
|
||||
log_flusher.start();
|
||||
QObject::connect(&app, &QCoreApplication::aboutToQuit, [&log_out](){
|
||||
log_out << "-------- Exiting --------" << std::endl;
|
||||
log_out.close();
|
||||
});
|
||||
|
||||
std::cout << "-------- Starting app --------" << std::endl;
|
||||
std::cout << "Current path: " << std::filesystem::current_path() << "\n";
|
||||
using namespace std::chrono_literals;
|
||||
auto polling_interval = 1s;
|
||||
int threshold;
|
||||
if(argc < 2) {
|
||||
threshold = 60;
|
||||
}
|
||||
else {
|
||||
std::stringstream ss;
|
||||
ss << argv[1];
|
||||
ss >> threshold;
|
||||
ss.flush();
|
||||
ss.clear();
|
||||
}
|
||||
std::cout << "App threshold set at: " << threshold << std::endl;
|
||||
std::vector<std::string> thermal_zones_paths;
|
||||
|
||||
std::filesystem::path base_path = "/sys/class/thermal/";
|
||||
|
||||
std::filesystem::directory_iterator iter(base_path);
|
||||
|
||||
for(auto &i: iter) {
|
||||
if (i.path().filename().string().find("thermal_zone") == 0) {
|
||||
thermal_zones_paths.push_back(i.path().string());
|
||||
}
|
||||
}
|
||||
for(int i=0;i<argc;i++) {
|
||||
std::cout << "Args: " << argv[i] << std::endl;
|
||||
}
|
||||
std::cout << "CPU total thermal zones: " << thermal_zones_paths.size() << std::endl;
|
||||
std::cout << "CPU Slow threshold: " << cpu_slow_threshold << " Resume threshold: " << cpu_fast_when_slowed_threshold << std::endl;
|
||||
std::cout << "Initial State must be in reactivated state." << std::endl;
|
||||
reactivate_slowed_cpus();
|
||||
while(true) {
|
||||
// Get GPU Temperature
|
||||
int gpu_temp = read_gpu_temp();
|
||||
// Get average current CPU Temp
|
||||
std::vector<int> cpu_temps = read_cpu_temp(thermal_zones_paths);
|
||||
float average_cpu_temp = std::accumulate(cpu_temps.begin(), cpu_temps.end(), 0) / cpu_temps.size();
|
||||
int cpu_temp = std::ceil(average_cpu_temp) / 1000;
|
||||
// Get platform profile
|
||||
std::string profile = get_platform_profile();
|
||||
std::cout << get_current_time() << "GPU temp: " << gpu_temp << " CPU temp: " << cpu_temp << " Performace profile: " << profile << std::endl;
|
||||
if((gpu_temp >= threshold || cpu_temp >= threshold) && (profile == "performance" || profile == "" )) {
|
||||
int fan_value = read_fans();
|
||||
if(fan_value == 0) {
|
||||
activate_fans();
|
||||
}
|
||||
}
|
||||
else {
|
||||
int fan_value = read_fans();
|
||||
if(fan_value == 1) {
|
||||
deactivate_fans();
|
||||
}
|
||||
}
|
||||
if(cpu_temp >= cpu_slow_threshold) {
|
||||
if(!cpu_slowed) {
|
||||
cpu_slowed = slow_cpus();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(cpu_slowed && cpu_temp <= cpu_fast_when_slowed_threshold) {
|
||||
cpu_slowed = reactivate_slowed_cpus();
|
||||
}
|
||||
}
|
||||
if(profile != "performance")
|
||||
std::cout << get_current_time() << "Fans will only be activated to max speed in performace profile." << std::endl;
|
||||
QCoreApplication::processEvents();
|
||||
std::this_thread::sleep_for(polling_interval);
|
||||
}
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
Loading…
Reference in a new issue