From 7b4791395eafeb502065b8808e119786e491c807 Mon Sep 17 00:00:00 2001 From: Drath Date: Thu, 11 Sep 2025 17:56:40 +0200 Subject: [PATCH] yuge overhaul, threads, hass, rewrote display, hwmons, bunc of shit --- Makefile | 11 +- include/LCDDisplay.hpp | 14 ++- include/cpu.hpp | 33 +++++ include/gpu.hpp | 50 +++++--- include/sys.hpp | 12 ++ include/utils.hpp | 25 +++- main.cpp | 278 ++++++++++++++++------------------------- src/LCDDisplay.cpp | 28 ++++- src/cpu.cpp | 45 +++++++ src/gpu.cpp | 58 ++++++--- src/sys.cpp | 26 ++++ src/utils.cpp | 86 ++++++++----- 12 files changed, 411 insertions(+), 255 deletions(-) create mode 100644 include/cpu.hpp create mode 100644 include/sys.hpp create mode 100644 src/cpu.cpp create mode 100644 src/sys.cpp diff --git a/Makefile b/Makefile index bf2d9e6..1ae5808 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ MAKEFLAGS := --jobs=16 # MAKEFLAGS += --output-sync=target # compiling with C++26 -CXXFLAGS := -std=c++2c -Ofast -fdiagnostics-color=always -Iinclude -lwebsockets +CXXFLAGS := -std=c++2c -Ofast -fdiagnostics-color=always -Iinclude -lwebsockets -D_GLIBCXX_USE_CXX11_ABI=0 CXXFLAGS += -MMD -MP SOURCES := main.cpp $(wildcard src/*.cpp) @@ -36,12 +36,3 @@ install_service: systemctl --user daemon-reload systemctl --user enable linuxSmartie systemctl --user start linuxSmartie - - -# build: -# g++ -c $(BUILD_OPTS) -o LCDDisplay.o -Iinclude include/LCDDisplay.cpp -# g++ -c $(BUILD_OPTS) -o gpu.o -Iinclude include/gpu.cpp -# g++ -c $(BUILD_OPTS) -o utils.o -Iinclude include/utils.cpp -# g++ -c $(BUILD_OPTS) -o main.o main.cpp - -# g++ $(BUILD_OPTS) -o linuxSmartie *.o # linking diff --git a/include/LCDDisplay.hpp b/include/LCDDisplay.hpp index e5e6380..2a3a08a 100644 --- a/include/LCDDisplay.hpp +++ b/include/LCDDisplay.hpp @@ -1,3 +1,6 @@ +#ifndef LCDDISPLAY_HPP +#define LCDDISPLAY_HPP + #include #include #include @@ -6,10 +9,12 @@ class LCDDisplay { private: uint8_t _lcd; - void writeStr(std::string str); + char linebuf[4][41]{}; + void writeStr(std::string str); // do not use + void writeLines(std::array lines); // do not use public: - LCDDisplay(std::string dev, uint32_t baud = 0010002); // from termios-baud.h, B115200 + LCDDisplay(std::string dev, uint32_t baud = 0010002); // from termios-baud.h, B115200 ~LCDDisplay(); // custom char array @@ -19,8 +24,11 @@ class LCDDisplay { void setContrast(uint8_t contrast); void setBacklight(bool enabled); void setCursor(uint8_t row, uint8_t col); - void writeLines(std::array lines); + void writeBuf(uint8_t row, uint8_t col, std::string str); + void flushBuffer(); void writeSStream(std::stringstream& ss); void setCustomChar(uint8_t index, std::array cchar); char bar(uint8_t char_id, double value); }; + +#endif \ No newline at end of file diff --git a/include/cpu.hpp b/include/cpu.hpp new file mode 100644 index 0000000..90d710e --- /dev/null +++ b/include/cpu.hpp @@ -0,0 +1,33 @@ +#ifndef CPU_HPP +#define CPU_HPP + +#include +#include +#include +#include + +#include + +#define N_CPU 16 + +struct cpu_hwmon_t { + std::string path = ""; + bool zenpower = false; +}; + +struct cpu_t { + int tdie = 0; + int tctl = 0; + int tccd1 = 0; + double pcore = 0.0; + double psoc = 0.0; + double ptotal = 0.0; + double vcore = 0.0; + double vsoc = 0.0; + uint32_t avg_freq = 0; +}; + +cpu_t getCpu(cpu_hwmon_t hw); +cpu_hwmon_t detectCpuHwmon(std::string pci); + +#endif \ No newline at end of file diff --git a/include/gpu.hpp b/include/gpu.hpp index 85219da..ef73e6c 100644 --- a/include/gpu.hpp +++ b/include/gpu.hpp @@ -1,25 +1,32 @@ +#ifndef GPU_HPP +#define GPU_HPP + +#include +#include +#include #include +#include struct gpu_t { - uint16_t fan_speed; - uint16_t core_clock; - uint16_t mem_clock; - double vcore; - uint16_t power_avg; - uint16_t power_cap; - uint16_t power_max; - uint8_t fan_pwm; - uint8_t edge_temp; - uint8_t hotspot_temp; - uint8_t mem_temp; - uint8_t gfx_vrm_temp; - uint8_t soc_vrm_temp; - uint8_t mem_vrm_temp; - uint64_t vram_used; - uint64_t vram_total; - uint8_t core_usage; - uint8_t media_usage; - uint8_t umc_usage; + uint16_t fan_speed = 0; + uint16_t core_clock = 0; + uint16_t mem_clock = 0; + double vcore = 0; + uint16_t power_avg = 0; + uint16_t power_cap = 0; + uint16_t power_max = 0; + uint8_t fan_pwm = 0; + uint8_t edge_temp = 0; + uint8_t hotspot_temp = 0; + uint8_t mem_temp = 0; + uint8_t gfx_vrm_temp = 0; + uint8_t soc_vrm_temp = 0; + uint8_t mem_vrm_temp = 0; + uint64_t vram_used = 0; + uint64_t vram_total = 0; + uint8_t core_usage = 0; + uint8_t media_usage = 0; + uint8_t umc_usage = 0; }; struct GpuMetricsHeader_t { @@ -28,6 +35,9 @@ struct GpuMetricsHeader_t { uint8_t content_revision = 0; }; +std::string detectGpuHwmon(std::string pci); gpu_t getGpu(GpuMetricsHeader_t header, std::string hw); gpu_t getGpu(std::string hw); -GpuMetricsHeader_t getGpuMetricsHeader(std::string path); \ No newline at end of file +GpuMetricsHeader_t getGpuMetricsHeader(std::string path); + +#endif \ No newline at end of file diff --git a/include/sys.hpp b/include/sys.hpp new file mode 100644 index 0000000..6019945 --- /dev/null +++ b/include/sys.hpp @@ -0,0 +1,12 @@ +#ifndef SYS_HPP +#define SYS_HPP + +#include +#include +#include +#include + +std::string getAvgs(); +std::array getTimeDate(); + +#endif \ No newline at end of file diff --git a/include/utils.hpp b/include/utils.hpp index 7b7df72..b450a3a 100644 --- a/include/utils.hpp +++ b/include/utils.hpp @@ -1,6 +1,27 @@ +#ifndef UTILS_HPP +#define UTILS_HPP + +#include +#include +#include +#include + #include +#include +#include +#include +#include +#include +#include #include -std::string readFile(std::string path, uint8_t trim = 0); +std::string pNum(double num, uint8_t decs = 0); +std::string pNum(float num, uint8_t decs = 0); +std::string pBytes(double bytes, bool isKbytes = false); +std::string readFile(std::string path, size_t trim = 4096); +ssize_t readFile2(std::string path, char *buffer, size_t maxlen); +ssize_t readFile2(const char *path, char *buffer, size_t maxlen); int get_val(const std::string &target, const std::string &content); -std::string findHwmonPath(std::string module); \ No newline at end of file +std::string getFirstDir(std::string path); + +#endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 68818c3..5f8eeea 100644 --- a/main.cpp +++ b/main.cpp @@ -5,56 +5,45 @@ #include #include #include -#include -#include -#include -// #include +#include #include #include +#include #include #include - -#include "include/LCDDisplay.hpp" -#include "include/gpu.hpp" -#include "include/utils.hpp" +// +#include +#include +#include +#include +#include +#include #define LCD_PORT "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00" // tis a symlink, can also be just /dev/ttyACM0 #define LCD_INTERVAL_MS 1000 #define LCD_CONTRAST 215 #define FILL_CHAR ' ' -#define N_CPU 16 + +std::chrono::milliseconds interval(LCD_INTERVAL_MS); +std::stop_source sigint; struct mem_usage_t { - uint64_t free; - uint64_t used; - uint64_t total; + uint64_t free = 0; + uint64_t used = 0; + uint64_t total = 0; }; -bool daemon_mode = false; +struct sensors_t { + std::array td; + std::string loadavg; + mem_usage_t ram; + mem_usage_t home; + gpu_t gpu; + cpu_t cpu; + float hass_state = 0; +}; /* DISPLAY FUNCS */ -// converts number to string with given decimal places -std::string pNum(auto num, uint8_t decs = 0) { - std::stringstream ret; - ret << std::fixed << std::setprecision(decs) << num; - return ret.str(); -} - -// converts large number in (k)bytes to human readable numer with suffix and sane decimal -std::string pBytes(double bytes, bool isKbytes = false) { - std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'}; - for (auto &suffix : suffix_list) { - if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes - bytes /= 1024.0; - if (bytes < 1024) { - uint8_t decs = 2; - if (bytes > 10) decs = 1; - if (bytes > 100) decs = 0; - return pNum(bytes, decs) + suffix; - } - } - return "++++"; -} // pads given string to length std::string pad(std::string s, uint8_t len, bool ljust = false) { @@ -69,30 +58,6 @@ std::string fill(uint8_t n) { } /* GETTERS */ -std::string getAvgs() { - std::string ret; - double avgs[3]; - getloadavg(avgs, 3); - for (uint8_t i = 0; i < 3; i++) - ret += pNum(avgs[i], avgs[i] > 10 ? 1 : 2) + " "; - ret.pop_back(); // remove trailing space - return ret; -} - -std::array getTimeDate() { - std::array months = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"}; - std::array wdays = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"}; - // std::array wdays = {"Ni", "Pn", "Wt", "Sr", "Cz", "Pi", "So"}; - auto now = std::time(nullptr); - auto tm = *std::localtime(&now); - std::stringstream time, date; - time << std::setfill('0'); - time << std::setw(2) << tm.tm_hour << ":"; - time << std::setw(2) << tm.tm_min << ":"; - time << std::setw(2) << tm.tm_sec; - date << wdays[tm.tm_wday] << ", " << tm.tm_mday << " " << months[tm.tm_mon]; - return {time.str(), date.str()}; -} mem_usage_t getRam() { mem_usage_t mem; @@ -118,127 +83,94 @@ mem_usage_t getDisk(std::string mount) { return disk; } -auto getCpuTemp(std::string hw) { - struct { - double tdie; - double tctl; - double tccd1; - double pcore = 0.0; - double psoc = 0.0; - double ptotal = 0.0; - double vcore = 0.0; - double vsoc = 0.0; - uint32_t avg_freq = 0; - } cpu; - - // commented out unused values so its faster - if (readFile(hw + "/name") == "zenpower") { - // cpu.tdie = (double)(std::stoul(readFile(hw + "/temp1_input")) / 1000.0); - cpu.tctl = (double)(std::stoul(readFile(hw + "/temp2_input")) / 1000.0); - cpu.tccd1 = (double)(std::stoul(readFile(hw + "/temp3_input")) / 1000.0); - cpu.pcore = (double)(std::stoul(readFile(hw + "/power1_input")) / 1000000.0); - cpu.psoc = (double)(std::stoul(readFile(hw + "/power2_input")) / 1000000.0); - cpu.ptotal = cpu.pcore + cpu.psoc; - cpu.vcore = (double)(std::stoul(readFile(hw + "/in1_input")) / 1000.0); - // cpu.vsoc = (double)(std::stoul(readFile(hw + "/in2_input")) / 1000.0); - } else { - cpu.tctl = (double)(std::stoul(readFile(hw + "/temp1_input")) / 1000.0); - cpu.tccd1 = (double)(std::stoul(readFile(hw + "/temp3_input")) / 1000.0); - } - - // lots of reads but still faster than reading /proc/cpuinfo - for (uint8_t i = 0; i < N_CPU; i++) - cpu.avg_freq += (long)(std::stol(readFile("/sys/devices/system/cpu/cpu" + std::to_string(i) + "/cpufreq/cpuinfo_avg_freq", 3))); - cpu.avg_freq /= N_CPU; - - return cpu; +void sighandler(int s) { + std::println("[Signal] got SIGINT, exiting..."); + sigint.request_stop(); } -void sighandler(int s) { - daemon_mode = false; - std::println("[Signal] deamon_mode = false, exiting on next loop"); +void hass_thread(sensors_t *s, std::stop_token st) { + std::println("[Thread] Starting hass_thread, pid {}", gettid()); + hass_connect(); + while (!st.stop_requested()) { + hass_poll(); + s->hass_state = hass_get(); + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + hass_disconnect(); +} + +void sensors_thread(sensors_t *s, std::stop_token st) { + std::println("[Thread] Starting sensors_thread, pid {}", gettid()); + /* find hwmon paths from pci addresses */ + cpu_hwmon_t cpu_hwmon = detectCpuHwmon("0000:00:18.3"); + std::string gpu_hwmon = detectGpuHwmon("0000:08:00.0"); + + while (!st.stop_requested()) { + auto start = std::chrono::steady_clock::now(); + s->td = getTimeDate(); + s->loadavg = getAvgs(); + s->ram = getRam(); + s->home = getDisk("/home/drath"); + s->cpu = getCpu(cpu_hwmon); + s->gpu = getGpu(gpu_hwmon); + + // std::print("[Sensors] took {} microseconds \r", std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count()); + // std::cout << std::flush; + std::this_thread::sleep_until(start + interval); + } +} + +void display_thread(sensors_t *s, std::stop_token st) { + std::println("[Thread] Starting display_thread, pid {}", gettid()); + LCDDisplay lcd(LCD_PORT); + lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius + + std::println("[Chrono] Running in daemon mode, {} ms interval", LCD_INTERVAL_MS); + lcd.setContrast(LCD_CONTRAST); + lcd.setBacklight(true); + lcd.clear(); + + while (!st.stop_requested()) { + auto start = std::chrono::steady_clock::now(); + std::array lines; + + auto gpu_temps = (s->gpu.hotspot_temp) ? "/" + std::to_string(s->gpu.hotspot_temp) : ""; + gpu_temps += (s->gpu.mem_temp) ? "/" + std::to_string(s->gpu.mem_temp) : ""; + + lcd.writeBuf(0, 0, pad(s->td[1], 11, true) + " " + s->td[0] + lcd.bar(1, s->gpu.core_usage / 100.0) + pad(s->loadavg, 17)); + + lcd.writeBuf(1, 0, pad(std::to_string(s->cpu.tctl) + "/" + std::to_string(s->cpu.tccd1) + lcd.cc[0], 8, true)); + lcd.writeBuf(1, 11, pad(pBytes(s->ram.used, true) + "B", 7, true)); + lcd.writeBuf(1, 22, pad(pBytes(s->gpu.vram_used) + "B", 7)); + lcd.writeBuf(1, 29, pad(std::to_string(s->gpu.edge_temp) + gpu_temps + lcd.cc[0], 11)); + + lcd.writeBuf(2, 0, pad(pNum(s->cpu.ptotal, 1) + "W", 8, true)); + lcd.writeBuf(2, 10, pad(pNum(s->hass_state) + "W", 4)); + lcd.writeBuf(2, 15, pad("h:" + pBytes(s->home.free), 8)); + + lcd.writeBuf(2, 25, pad(s->gpu.fan_speed + "R", 5)); + std::println("{}", s->gpu.fan_speed); + lcd.writeBuf(2, 31, pad(std::to_string(s->gpu.power_avg) + "/" + std::to_string(s->gpu.power_cap) + "W", 9)); + + lcd.writeBuf(3, 0, pad(pNum(s->cpu.vcore, 3) + "V", 7, true)); + lcd.writeBuf(3, 7, std::to_string(s->cpu.avg_freq) + "MHz"); + lcd.writeBuf(3, 21, pad(std::to_string(s->gpu.core_clock) + "/" + std::to_string(s->gpu.mem_clock) + "MHz ", 13)); + lcd.writeBuf(3, 34, pad(pNum(s->gpu.vcore, 3) + "V", 5)); + + lcd.flushBuffer(); + + std::this_thread::sleep_until(start + interval); + } } int main(int argc, char *argv[]) { signal(SIGINT, sighandler); - daemon_mode = (argc > 1 && std::string(argv[1]) == "-d"); - /* init lcd and chars, set contrast and backlight */ - LCDDisplay lcd(LCD_PORT); - std::array lines; - lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius + sensors_t sensors; - /* find correct cpu module */ - auto cpu_hwmon = findHwmonPath("k10temp"); - if (cpu_hwmon.size() == 0) { - std::println("[Hwmon] k10temp not found, trying zenpower"); - cpu_hwmon = findHwmonPath("zenpower"); - } + std::jthread producer(sensors_thread, &sensors, sigint.get_token()); + std::jthread hass(hass_thread, &sensors, sigint.get_token()); - /* figure out gpu metrics path */ - std::string gpu_hwmon; - GpuMetricsHeader_t gpu_mtr_header; - for (uint8_t c = 0; c < 10; c++) { - gpu_hwmon = "/sys/class/drm/card" + std::to_string(c) + "/device"; - gpu_mtr_header = getGpuMetricsHeader(gpu_hwmon + "/gpu_metrics"); - if (readFile(gpu_hwmon + "/board_info").length() != 0) - break; - } - - /* loop setup */ - std::chrono::milliseconds interval(LCD_INTERVAL_MS); - auto start = std::chrono::steady_clock::now(); - if (daemon_mode) { - std::println("[Chrono] Running in daemon mode, {} ms interval", LCD_INTERVAL_MS); - lcd.setContrast(LCD_CONTRAST); - lcd.setBacklight(true); - } - - /* main loop */ - while (true) { - auto td = getTimeDate(); - auto ram = getRam(); - // auto anime = getDisk("/mnt/anime"); - auto home = getDisk("/home/drath"); - // auto gpu = (gpu_mtr_header.structure_size != 0) ? getGpu(gpu_mtr_header, gpu_hwmon) : getGpu("/sys/class/drm/card1/device"); - auto gpu = getGpu(gpu_mtr_header, gpu_hwmon); - - auto cpu = getCpuTemp(cpu_hwmon); - - lines[0] = pad(td[1], 11, true) + " " + td[0] + lcd.bar(1, gpu.core_usage / 100.0) + pad(getAvgs(), 17); - - lines[1] = pad(pNum(cpu.tctl) + "/" + pNum(cpu.tccd1) + lcd.cc[0], 8, true); - lines[1] += pad(pBytes(ram.used, true) + "B", 7, true); - lines[1] += fill(7); - lines[1] += pad(pBytes(gpu.vram_used) + "B", 7); - - auto gpu_temps = (gpu.hotspot_temp) ? "/" + std::to_string(gpu.hotspot_temp) : ""; - gpu_temps += (gpu.mem_temp) ? "/" + std::to_string(gpu.mem_temp) : ""; - lines[1] += pad(std::to_string(gpu.edge_temp) + gpu_temps + lcd.cc[0], 11); - - lines[2] = pad(pNum(cpu.ptotal, 1) + "W", 8, true); - lines[2] += pad("home: " + pBytes(home.free), 12); - lines[2] += fill(6); - lines[2] += pad(pNum(gpu.fan_speed) + "R", 5); - lines[2] += pad(std::to_string(gpu.power_avg) + "/" + std::to_string(gpu.power_cap) + "W", 9); - - lines[3] = pad(pNum(cpu.vcore, 3) + "V", 7, true); - lines[3] += std::to_string(cpu.avg_freq) + "MHz"; - lines[3] += fill(7); - lines[3] += pad(pNum(gpu.core_clock) + "/" + pNum(gpu.mem_clock) + "MHz ", 13); - lines[3] += pad(pNum(gpu.vcore, 3) + "V", 5); - - lcd.writeLines(lines); - - if (daemon_mode) { - std::this_thread::sleep_until(start + interval); - start += interval; - } else { - std::println("[Chrono] took {}ms", std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count() / 1000.0); - return 0; - } - } - - /* should exit in loop, if it doesnt something went wrong */ - return 1; + display_thread(&sensors, sigint.get_token()); + return 0; } diff --git a/src/LCDDisplay.cpp b/src/LCDDisplay.cpp index d0da302..19b27d2 100644 --- a/src/LCDDisplay.cpp +++ b/src/LCDDisplay.cpp @@ -4,6 +4,8 @@ #include #include +#include +#include #include #include @@ -27,6 +29,7 @@ LCDDisplay::LCDDisplay(std::string dev, uint32_t baud) { tcflush(lcd, TCIFLUSH); tcsetattr(lcd, TCSANOW, &term); _lcd = lcd; + std::memset(linebuf, ' ', sizeof(linebuf)); // init linebuf to spaces } LCDDisplay::~LCDDisplay() { @@ -46,7 +49,7 @@ void LCDDisplay::setContrast(uint8_t contrast) { } void LCDDisplay::setBacklight(bool enabled) { - std::println("[LCDDisplay] Set brightness to {}", enabled); + std::println("[LCDDisplay] Set backlight to {}", enabled); uint8_t cmdbuf[] = {254, (uint8_t)(enabled ? 66 : 70), 255}; write(_lcd, &cmdbuf, enabled ? 3 : 2); } @@ -75,6 +78,29 @@ void LCDDisplay::writeLines(std::array lines) { writeStr(lines[2] + lines[3]); } +/* writes string to line buffer at given pos*/ +void LCDDisplay::writeBuf(uint8_t row, uint8_t col, std::string str) { + // size_t max_copy = std::min(str.size(), sizeof(linebuf[row]) - col - 1); + std::memcpy(&linebuf[row][col], str.c_str(), str.size()); // will overflow +} + +/* hyper optimized buffer flush command, writes whole disp with a single write */ +void LCDDisplay::flushBuffer() { + char writebuf[168]; // 4x40 chars + 2x4 setcursor commands + 1 null terminator + uint8_t setDisp[] = {254, 71, 1}; // skeleton set cursor command + + std::memcpy(&writebuf[0], setDisp, 3); // copy template set cursor command + writebuf[3] = 1; // actual set cursor param, set to 1st row + std::memcpy(&writebuf[4], linebuf[0], 40); // copy 1st line + std::memcpy(&writebuf[44], linebuf[1], 40); // copy 2nd line + std::memcpy(&writebuf[84], setDisp, 3); // copy template set cursor command + writebuf[87] = 3; // set cursor to 3rd line + std::memcpy(&writebuf[88], linebuf[2], 40); // copy 3rd line + std::memcpy(&writebuf[128], linebuf[3], 40); // copy 4th line + + write(_lcd, writebuf, 168); +} + void LCDDisplay::writeSStream(std::stringstream& ss) { std::string s = ss.str(); if (s.size() != 160) diff --git a/src/cpu.cpp b/src/cpu.cpp new file mode 100644 index 0000000..db7b5c5 --- /dev/null +++ b/src/cpu.cpp @@ -0,0 +1,45 @@ +#include + +cpu_hwmon_t detectCpuHwmon(std::string pci) { + cpu_hwmon_t hw; + std::string path = "/sys/bus/pci/devices/" + pci + "/hwmon"; + try { + for (const auto& entry : std::filesystem::directory_iterator(path)) + hw.path = getFirstDir(path); + } catch (...) { + std::println("[CpuHwmon] NO CPU HWMON KERNEL MODULE DETECTED"); + return hw; + } + hw.zenpower = readFile((hw.path + "/name"), 128) == "zenpower"; + std::println("[CpuHwmon] Detected module \"{}\" in path \"{}\"", hw.zenpower ? "zenpower" : "k10temp", hw.path); + return hw; +} + +cpu_t getCpu(cpu_hwmon_t hw) { + cpu_t cpu; + // commented out unused values so its faster + if (hw.path == "") return cpu; // break if no module + if (hw.zenpower) { + // cpu.tdie = std::stoi(readFile(hw.path + "/temp1_input", 2)); + cpu.tctl = std::stoi(readFile(hw.path + "/temp2_input", 2)); + cpu.tccd1 = std::stoi(readFile(hw.path + "/temp3_input", 2)); + cpu.pcore = (double)(std::stoi(readFile(hw.path + "/power1_input", 3)) / 100.0); + cpu.psoc = (double)(std::stoi(readFile(hw.path + "/power2_input", 3)) / 100.0); + cpu.ptotal = cpu.pcore + cpu.psoc; + cpu.vcore = (double)(std::stoul(readFile(hw.path + "/in1_input", 4)) / 1000.0); + // cpu.vsoc = (double)(std::stoul(readFile(hw.path + "/in2_input")) / 1000.0); + } else { + cpu.tctl = std::stoi(readFile(hw.path + "/temp1_input", 2)); + cpu.tccd1 = std::stoi(readFile(hw.path + "/temp3_input", 2)); + } + + // lots of reads but still faster than reading /proc/cpuinfo + char buf[5]; + for (uint8_t i = 0; i < N_CPU; i++) { + readFile2("/sys/devices/system/cpu/cpu1/cpufreq/scaling_cur_freq", buf, sizeof(buf)); + cpu.avg_freq += strtol(buf, NULL, 10); + } + cpu.avg_freq /= N_CPU; + + return cpu; +} \ No newline at end of file diff --git a/src/gpu.cpp b/src/gpu.cpp index c8d848b..6348cfc 100644 --- a/src/gpu.cpp +++ b/src/gpu.cpp @@ -1,9 +1,4 @@ -#include -#include #include -#include -#include -#include struct GpuMetrics_v1_1_t { uint16_t temperature_edge; @@ -84,6 +79,12 @@ struct GpuMetrics_v1_3_t { uint8_t padding1; }; +std::string detectGpuHwmon(std::string pci) { + std::string path = getFirstDir("/sys/bus/pci/devices/" + pci + "/hwmon"); + std::println("[GpuHwmon] Detected path \"{}\"", path); + return path; +} + GpuMetricsHeader_t getGpuMetricsHeader(std::string path) { GpuMetricsHeader_t header; std::ifstream f(path, std::ios::binary); @@ -111,7 +112,7 @@ auto getGpuMetrics(GpuMetricsHeader_t h, std::string path) { f.read(reinterpret_cast(&metrics), sizeof(metrics)); f.close(); } - } break; + } break; } return metrics; @@ -123,10 +124,10 @@ gpu_t getGpu(GpuMetricsHeader_t header, std::string hw) { uint8_t hwmon = 3; // these are always read from fs - gpu.fan_speed = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/fan1_input")); - gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/in0_input")) / 1000.0; - gpu.power_cap = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_cap", 6)); - gpu.power_max = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_cap_max", 6)); + gpu.fan_speed = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/fan1_input")); + gpu.vcore = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/in0_input")) / 1000.0; + gpu.power_cap = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_cap", 3)); + gpu.power_max = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_cap_max", 3)); // gpu.fan_pwm = std::stoi(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/pwm1")); // not super useful gpu.vram_used = std::stoul(readFile(hw + "/mem_info_vram_used")); gpu.vram_total = std::stoul(readFile(hw + "/mem_info_vram_total")); @@ -148,16 +149,41 @@ gpu_t getGpu(GpuMetricsHeader_t header, std::string hw) { gpu.mem_vrm_temp = metrics.temperature_vrmem; gpu.power_avg = metrics.average_socket_power; } else { - gpu.core_clock = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/freq1_input", 6)); - gpu.mem_clock = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/freq2_input", 6)); - gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/in0_input")) / 1000.0; - gpu.power_avg = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/power1_input", 6)); - gpu.edge_temp = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/temp1_input", 3)); + gpu.core_clock = std::stoll(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/freq1_input", 16)) / 1000000; + gpu.mem_clock = std::stoll(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/freq2_input", 16)) / 1000000; + gpu.vcore = std::stod(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/in0_input", 8)) / 1000.0; + gpu.power_avg = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/power1_input", 3)); + gpu.edge_temp = std::stol(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon) + "/temp1_input", 2)); gpu.core_usage = std::stoi(readFile(hw + "/gpu_busy_percent")); } - if(gpu.fan_speed > 9999) + if (gpu.fan_speed > 9999) gpu.fan_speed = 9999; return gpu; } + +gpu_t getGpu(std::string hwmon) { + gpu_t gpu; + + // these are always read from fs + gpu.fan_speed = std::stol(readFile(hwmon + "/fan1_input", 4)); + gpu.vcore = std::stol(readFile(hwmon + "/in0_input", 4)) / 1000.0; + gpu.power_cap = std::stol(readFile(hwmon + "/power1_cap", 16)) / 1000000; + // gpu.power_max = std::stol(readFile(hwmon + "/power1_cap_max", 16)) / 1000000; + // gpu.fan_pwm = std::stoi(readFile(hw + "/hwmon/hwmon" + std::to_string(hwmon)+ "/pwm1")); // not super useful + gpu.vram_used = std::stoul(readFile(hwmon + "/../../mem_info_vram_used", 16)); + // gpu.vram_total = std::stoul(readFile(hwmon + "/../../mem_info_vram_total", 16)); + + // these also exist in gpu_metrics? + gpu.core_clock = std::stoll(readFile(hwmon + "/freq1_input", 16)) / 1000000; + gpu.mem_clock = std::stoll(readFile(hwmon + "/freq2_input", 16)) / 1000000; + gpu.power_avg = std::stol(readFile(hwmon + "/power1_input", 16)) / 1000000; + gpu.edge_temp = std::stol(readFile(hwmon + "/temp1_input", 2)); + gpu.core_usage = std::stoi(readFile(hwmon + "/../../gpu_busy_percent")); + + if (gpu.fan_speed > 9999) + gpu.fan_speed = 9999; + + return gpu; +} \ No newline at end of file diff --git a/src/sys.cpp b/src/sys.cpp new file mode 100644 index 0000000..cec1524 --- /dev/null +++ b/src/sys.cpp @@ -0,0 +1,26 @@ +#include + +std::string getAvgs() { + std::string ret; + double avgs[3]; + getloadavg(avgs, 3); + for (uint8_t i = 0; i < 3; i++) + ret += pNum(avgs[i], avgs[i] > 10 ? 1 : 2) + " "; + ret.pop_back(); // remove trailing space + return ret; +} + +std::array getTimeDate() { + std::array months = {"sty", "lut", "mar", "kwi", "maj", "cze", "lip", "sie", "wrz", "paz", "lis", "gru"}; + std::array wdays = {"Nie", "Pon", "Wto", "Sro", "Czw", "Pia", "Sob"}; + // std::array wdays = {"Ni", "Pn", "Wt", "Sr", "Cz", "Pi", "So"}; + auto now = std::time(nullptr); + auto tm = *std::localtime(&now); + std::stringstream time, date; + time << std::setfill('0'); + time << std::setw(2) << tm.tm_hour << ":"; + time << std::setw(2) << tm.tm_min << ":"; + time << std::setw(2) << tm.tm_sec; + date << wdays[tm.tm_wday] << ", " << tm.tm_mday << " " << months[tm.tm_mon]; + return {time.str(), date.str()}; +} diff --git a/src/utils.cpp b/src/utils.cpp index 2d10473..8a66f24 100644 --- a/src/utils.cpp +++ b/src/utils.cpp @@ -1,27 +1,58 @@ -#include -#include -#include -#include -#include #include -/* SENSOR GETTERS + UTILS*/ -std::string readFile(std::string path, uint8_t trim) { - std::string ret; - std::stringstream ss; - std::ifstream fs(path); - if (!fs.good()) { - std::println("[readFile] Error reading {}", path); - return ""; - } - ss << fs.rdbuf(); - fs.close(); - ret = ss.str(); - if (ret.size() < trim) return ret; - return ret.substr(0, ret.size() - trim - 1); +// converts number to string with given decimal places +std::string pNum(double num, uint8_t decs) { + std::stringstream ret; + ret << std::fixed << std::setprecision(decs) << num; + return ret.str(); } -int get_val(const std::string &target, const std::string &content) { +std::string pNum(float num, uint8_t decs) { + return pNum((double)num, decs); +} + + +// converts large number in (k)bytes to human readable numer with suffix and sane decimal +std::string pBytes(double bytes, bool isKbytes) { + std::array suffix_list = {'K', 'M', 'G', 'T', 'P', 'E', 'Z'}; + for (auto& suffix : suffix_list) { + if (suffix == 'K' && isKbytes) continue; // skip first loop if already in Kbytes + bytes /= 1024.0; + if (bytes < 1024) { + uint8_t decs = 2; + if (bytes > 10) decs = 1; + if (bytes > 100) decs = 0; + return pNum(bytes, decs) + suffix; + } + } + return "++++"; +} + +/* SENSOR GETTERS + UTILS*/ +ssize_t readFile2(const char* path, char* buffer, size_t maxlen) { + int fd = open(path, O_RDONLY | O_CLOEXEC); + if (fd < 0) { + std::println("[readFile] Error reading {}", path); + return 0; + } + ssize_t len = read(fd, buffer, maxlen - 1); + if (len >= 0) buffer[len] = '\0'; + close(fd); + return len; +} + +ssize_t readFile2(std::string path, char* buffer, size_t maxlen) { + return readFile2(path.c_str(), buffer, maxlen); +} + +std::string readFile(std::string path, size_t trim) { + char buf[trim + 2]; + ssize_t len = readFile2(path, buf, trim + 2); + std::string out(buf); + return out.substr(0, len - 1); +} + +int get_val(const std::string& target, const std::string& content) { int result = -1; std::size_t start = content.find(target); if (start != std::string::npos) { @@ -33,14 +64,9 @@ int get_val(const std::string &target, const std::string &content) { return result; } -std::string findHwmonPath(std::string module) { - for (uint8_t id = 0; id < 10; id++) { - auto f = readFile("/sys/class/hwmon/hwmon" + std::to_string(id) + "/name"); - if (f.size() == 0) break; - if (f == module) { - std::println("[Hwmon] Detected {} path is /sys/class/hwmon/hwmon{}", module, id); - return "/sys/class/hwmon/hwmon" + std::to_string(id); - } - } +std::string getFirstDir(std::string path) { + for (const auto& entry : std::filesystem::directory_iterator(path)) + return entry.path(); return ""; -} \ No newline at end of file +} +