-
-
Save crgimenes/76d1eba64016decfe53eda029f1f2fe8 to your computer and use it in GitHub Desktop.
Improved devterm automatic gearbox
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Compile like this: g++ fullauto.cpp --std=c++17 -o fullauto | |
// Run like this: sudo fullauto & | |
#include <thread> | |
#include <chrono> | |
#include <iostream> | |
#include <fstream> | |
#include <sstream> | |
#include <vector> | |
using namespace std; | |
using namespace chrono; | |
using namespace chrono_literals; | |
ifstream stat{"/proc/stat"}; | |
ifstream ac{"/sys/class/power_supply/axp22x-ac/present"}; | |
ifstream cpuTemp{"/sys/class/thermal/thermal_zone0/temp"}; | |
milliseconds batteryPollRate = 500ms, acPollRate = 500ms; | |
const string& read(ifstream& stream) { | |
static stringstream buf; | |
static string out; | |
buf.str(""); | |
stream.seekg(0); | |
buf << stream.rdbuf(); | |
out = buf.str(); | |
return out; | |
} | |
bool hasACPower() { | |
return read(ac)[0] == '1'; | |
} | |
float getCPUTemp() { | |
return atof(read(cpuTemp).c_str()) * 0.001f; | |
} | |
class CPU { | |
static inline int powerUpFreq = 0; | |
static inline const char* gears[] = { | |
"⦻", | |
"◌", | |
"○", | |
"◎", | |
"◉", | |
"●", | |
"◙" | |
}; | |
int id; | |
vector<int> frequencies; | |
int online = 0; | |
int currentFrequency = 0; | |
int busy = 0, idle = 0; | |
int percent = -1; | |
int needs = 0; | |
fstream onlineStream; | |
string freqFile; | |
public: | |
CPU(int id) : id{id} { | |
auto prefix = "/sys/devices/system/cpu/cpu" + std::to_string(id); | |
freqFile = prefix + "/cpufreq/scaling_max_freq"; | |
if (id > 0) | |
frequencies.push_back(0); | |
{ | |
int freq; | |
ifstream stream {prefix + "/cpufreq/scaling_available_frequencies"}; | |
stream >> freq; // ignore first entry | |
while (stream >> freq) | |
frequencies.push_back(freq); | |
if (id == 0) | |
powerUpFreq = freq; | |
} | |
onlineStream.open(prefix + "/online", ios::in | ios::out); | |
onlineStream >> online; | |
if (online) { | |
ifstream{freqFile} >> currentFrequency; | |
} | |
cout << "cpu " << id << " freq:" << currentFrequency << endl; | |
} | |
const char* gear() { | |
if (!online) | |
return gears[0]; | |
int g = id == 0; | |
for (int i = 0; i < frequencies.size(); ++i) { | |
if (frequencies[i] >= currentFrequency) { | |
g += i; | |
break; | |
} | |
} | |
return gears[std::min<int>(g, sizeof(gears)/sizeof(gears[0]) - 1)]; | |
} | |
int getLoad() const { | |
return percent; | |
} | |
void update(int busy, int idle) { | |
if (this->busy) { | |
auto busyDelta = busy - this->busy; | |
auto idleDelta = idle - this->idle; | |
percent = (busyDelta * 100) / (busyDelta + idleDelta); | |
} else { | |
percent = -1; | |
} | |
this->busy = busy; | |
this->idle = idle; | |
if (percent == -1) { | |
needs = 0; | |
} else if (percent < 20) { | |
needs = -1; | |
} else if (percent > 90) { | |
needs = 1; | |
} else { | |
needs = 0; | |
} | |
} | |
static int update(CPU* cpus, int cpuCount) { | |
stat.seekg(0); | |
string word; | |
for (int i = 0; i < cpuCount; ++i) { | |
cpus[i].percent = -1; | |
cpus[i].needs = 0; | |
} | |
while (stat >> word) { | |
if (string_view{word}.substr(0, 3) != "cpu") { | |
if (word[0] >= '0' && word[0] <= '9') | |
continue; | |
break; | |
} | |
if (word.size() <= 3) | |
continue; | |
auto cpuid = atoi(word.c_str() + 3); | |
if (cpuid >= cpuCount) | |
continue; | |
int user, nice, system, idle; | |
stat >> user >> nice >> system >> idle; | |
cpus[cpuid].update(user + nice + system, idle); | |
} | |
int needs = 0; | |
for (int i = 0; i < cpuCount; ++i) { | |
needs += cpus[i].needs; | |
// cout << cpus[i].needs << "x" << cpus[i].percent << " "; | |
} | |
// cout << endl; | |
return needs; | |
} | |
void setFrequency(int freq) { | |
if (freq == currentFrequency) | |
return; | |
// cout << "cpu " << id << " "; | |
currentFrequency = freq; | |
bool online = freq > 0; | |
if (online != this->online) { | |
this->online = online; | |
// cout << (online ? " power on" : " shutdown"); | |
onlineStream.seekp(0); | |
onlineStream << this->online; | |
onlineStream.flush(); | |
if (online) | |
currentFrequency = powerUpFreq; | |
} | |
if (freq) { | |
// cout << " freq:" << freq; | |
ofstream{freqFile} << currentFrequency; | |
} | |
// cout << endl; | |
} | |
void gearUp(int& c, int profile) { | |
if (id >= 2 + ((profile & 1) + (profile >> 1)) * 2) { | |
needs = -1; | |
} | |
if (needs < 0) { | |
c = -1; | |
return; | |
} | |
int cf = currentFrequency; | |
for (auto freq : frequencies) { | |
if (freq > currentFrequency) { | |
cf = freq; | |
c--; | |
if (!c) | |
break; | |
} | |
} | |
if (cf != currentFrequency) { | |
setFrequency(cf); | |
c = 0; | |
} | |
} | |
void gearDown(int& c, int profile) { | |
if (needs > 0 || (id == 0 && profile == 3)) { | |
c = 0; | |
return; | |
} | |
int cf = currentFrequency; | |
for (int i = frequencies.size() - 1; i >= 0 && c < -needs; --i) { | |
auto freq = frequencies[i]; | |
if (freq < cf) { | |
cf = freq; | |
++c; | |
if (!c) | |
break; | |
} | |
} | |
setFrequency(cf); | |
} | |
} cpu[] = { | |
{0}, | |
{1}, | |
{2}, | |
{3}, | |
{4}, | |
{5}, | |
}; | |
int getCPUContention() { | |
return CPU::update(cpu, sizeof(cpu)/sizeof(cpu[0])); | |
} | |
void run() { | |
fstream{"/tmp/gears", ios::out}; | |
fstream gearFile{"/tmp/gears", ios::in | ios::out}; | |
int currentProfile = -1; | |
bool busy = false; | |
bool hot = false; | |
int skip = 1; | |
while (1) { | |
auto isAC = hasACPower(); | |
auto rate = isAC ? acPollRate : batteryPollRate; | |
this_thread::sleep_for(rate); | |
auto cont = getCPUContention(); | |
this_thread::sleep_for(rate); | |
cont = getCPUContention(); | |
bool isHot = getCPUTemp() > (hot ? 70 : 80); | |
hot = isHot; | |
int profile = (int(!isHot) << 1) + int(isAC); | |
for (int i = 0; i < 6 && cont > 0; ++i) | |
cpu[i].gearUp(cont, profile); | |
for (int i = 5; i >= 0 && cont < 0; --i) | |
cpu[i].gearDown(cont, profile); | |
if (gearFile && !--skip) { | |
skip = 4; | |
gearFile.seekg(0); | |
gearFile | |
<< cpu[0].gear() | |
<< cpu[1].gear() | |
<< cpu[2].gear() | |
<< cpu[3].gear() | |
<< cpu[4].gear() | |
<< cpu[5].gear() | |
<< 2 + ((profile & 1) + (profile >> 1)) * 2; | |
gearFile.flush(); | |
} | |
} | |
} | |
int main() { | |
if (!stat) { | |
cout << "Not OK" << endl; | |
return 1; | |
} | |
run(); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment