212 lines
8.6 KiB
C++
212 lines
8.6 KiB
C++
#include <LCDDisplay.hpp>
|
|
#include <app.hpp>
|
|
#include <chrono>
|
|
#include <cpu.hpp>
|
|
#include <csignal>
|
|
#include <functional>
|
|
#include <gpu.hpp>
|
|
#include <hass.hpp>
|
|
#include <log.hpp>
|
|
#include <mutex>
|
|
#include <stop_token>
|
|
#include <string>
|
|
#include <sys.hpp>
|
|
#include <thread>
|
|
#include <utility>
|
|
#include <utils.hpp>
|
|
#include <vector>
|
|
|
|
Application::Application(Config config) {
|
|
m_config = std::move(config);
|
|
// set up signals
|
|
sigemptyset(&m_control.sigset);
|
|
sigaddset(&m_control.sigset, SIGINT);
|
|
sigaddset(&m_control.sigset, SIGTERM);
|
|
applog(LOG::START, "Application initialized");
|
|
}
|
|
|
|
Application::~Application() {
|
|
auto level = LOG::START;
|
|
if (m_ret < 0) level = LOG::WRN;
|
|
applog(level, "Application destroyed");
|
|
}
|
|
|
|
int Application::Run() {
|
|
int sig;
|
|
m_mainPid = getpid();
|
|
pthread_sigmask(SIG_BLOCK, &m_control.sigset, nullptr);
|
|
|
|
{
|
|
std::vector<std::jthread> threads;
|
|
|
|
std::function<void(std::stop_token)> funcs[] = {
|
|
[this](const std::stop_token& st) { displayThread(st); },
|
|
[this](const std::stop_token& st) { sensorsThread(st); },
|
|
[this](const std::stop_token& st) { hassThread(st); },
|
|
};
|
|
|
|
// start threads in order
|
|
for (auto& f : funcs) {
|
|
threads.emplace_back(f);
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10)); // start threads with delay so displayThread can quit the app if theres no lcd
|
|
}
|
|
|
|
// waits for signal, the threads are stopped when we go out of scope
|
|
sigwait(&m_control.sigset, &sig);
|
|
applog(LOG::WRN, "got stop signal, shutting down...");
|
|
}
|
|
return m_ret;
|
|
}
|
|
|
|
void Application::Crash(const int ret) {
|
|
m_ret = ret;
|
|
kill(m_mainPid, SIGINT);
|
|
}
|
|
|
|
|
|
void Application::hassThread(const std::stop_token& st) { //NOLINT
|
|
applog(LOG::START, "Thread started");
|
|
#ifdef ENABLE_HOME_ASSISTANT
|
|
static double power;
|
|
HomeAssistant::Config config;
|
|
config.address = m_config.hass.url;
|
|
config.entity = m_config.hass.entity;
|
|
config.token = m_config.hass.token;
|
|
HomeAssistant pc_power(config);
|
|
|
|
pc_power.connect();
|
|
while (!st.stop_requested()) {
|
|
pc_power.service();
|
|
if (power != pc_power.state) {
|
|
power = pc_power.state;
|
|
{
|
|
std::lock_guard lock(m_control.sensors);
|
|
m_sensors.hass_state = power;
|
|
}
|
|
}
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
|
}
|
|
#else
|
|
(void)st;
|
|
applog(LOG::WRN, "feature disabled at compile time, probably no libwebsockets on host. exiting thread");
|
|
{
|
|
std::lock_guard lock(m_control.sensors);
|
|
m_sensors.hass_state = -1.0;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void Application::sensorsThread(const std::stop_token& st) {
|
|
applog(LOG::START, "Thread started, poll interval {} ms", m_config.lcd.refresh_interval_ms);
|
|
int ret = cpu::detect(&m_sensors.cpu, m_config.cpu_pciaddr);
|
|
ret += gpu::detect(&m_sensors.gpu, m_config.gpu_pciaddr);
|
|
ret += sys::detect(&m_sensors.sys, m_config.sys_sensor);
|
|
m_sensors.home.mountpoint = m_config.home_dir;
|
|
if (ret < 0) {
|
|
applog(LOG::ERR, "Failed to detect some sensors");
|
|
this->Crash(ret);
|
|
return;
|
|
}
|
|
|
|
while (!st.stop_requested()) {
|
|
auto start = std::chrono::steady_clock::now();
|
|
{
|
|
std::lock_guard lock(m_control.sensors);
|
|
|
|
sys::getTimeDate(&m_sensors.td);
|
|
getloadavg(m_sensors.loadavg, 3);
|
|
sys::getRam(&m_sensors.ram);
|
|
sys::getDisk(&m_sensors.home);
|
|
sys::getNetwork(&m_sensors.net, m_config.sys_ifname);
|
|
sys::updateSensors(&m_sensors.sys);
|
|
cpu::update(&m_sensors.cpu);
|
|
gpu::update(&m_sensors.gpu);
|
|
}
|
|
|
|
// log(LOG::DBG, "took {} microseconds", std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now() - start).count());
|
|
m_control.display.release(); // unlocks display loop
|
|
std::this_thread::sleep_until(start + std::chrono::milliseconds(m_config.lcd.refresh_interval_ms));
|
|
}
|
|
applog(LOG::INF, "Thread stopped");
|
|
}
|
|
|
|
void Application::displayThread(const std::stop_token& st) {
|
|
std::stop_callback cb(st, [&] { m_control.display.release(); });
|
|
applog(LOG::START, "Thread started");
|
|
// init internal variables of LCDDisplay class
|
|
LCDDisplay lcd(m_config.lcd.port);
|
|
if (const int ret = lcd.Init(); ret < 0) {
|
|
applog(LOG::ERR, "Failed to initialize LCDDisplay");
|
|
this->Crash(ret);
|
|
return;
|
|
}
|
|
|
|
// basic lcd setup
|
|
lcd.setContrast(m_config.lcd.contrast);
|
|
lcd.setBacklight(true);
|
|
lcd.setCustomChar(0, {16, 6, 9, 8, 8, 9, 6, 0}); // celcius
|
|
lcd.setCustomChar(1, {0, 0, 4, 10, 10, 17, 31, 0}); // delta
|
|
lcd.setCustomChar(2, {0, 0, 8, 21, 21, 2, 0, 0}); // home (tilde)
|
|
lcd.setCustomChar(3, {4, 14, 21, 4, 4, 4, 0}); // up arrow
|
|
lcd.setCustomChar(4, {4, 4, 4, 21, 14, 4, 0}); // down arrow
|
|
|
|
// welcome msg
|
|
lcd.writeBuf(2, 0, utils::center("Waiting for sensors...", 40));
|
|
lcd.flushBuf();
|
|
lcd.clearBuf();
|
|
|
|
// actual main display loop
|
|
while (!st.stop_requested()) {
|
|
m_control.display.try_acquire_for(std::chrono::milliseconds(static_cast<int>(m_config.lcd.refresh_interval_ms * 1.25))); // holds the loop until sensor data is ready, max 1.25x of the loop time
|
|
if (st.stop_requested()) break; // needed, otherwise it wont quit correctly
|
|
{
|
|
std::lock_guard lock(m_control.sensors);
|
|
|
|
// prepare some tricky sensors
|
|
std::string gpu_temps = std::to_string(m_sensors.gpu.temp_edge);
|
|
if (m_sensors.gpu.temp_hotspot) {
|
|
const auto delta = m_sensors.gpu.temp_hotspot - m_sensors.gpu.temp_edge;
|
|
gpu_temps.append("/" + std::to_string(m_sensors.gpu.temp_hotspot));
|
|
gpu_temps.insert(0, LCDDisplay::cc(1) + std::to_string(delta) + LCDDisplay::cc(0) + " ");
|
|
}
|
|
|
|
uint16_t cpu_fan = 0;
|
|
for (const auto& fan : m_sensors.sys.fan_speed)
|
|
cpu_fan += fan;
|
|
cpu_fan /= 2;
|
|
|
|
// write to display buffer
|
|
lcd.writeBuf(0, 0, utils::pad(m_sensors.td.date, 11, utils::just::left) + " " + m_sensors.td.time + lcd.bar(7, m_sensors.gpu.usage_core / 100.0));
|
|
lcd.writeBuf(0, 26, utils::pNum(m_sensors.loadavg[0], m_sensors.loadavg[0] > 10 ? 1 : 2));
|
|
lcd.writeBuf(0, 31, utils::pNum(m_sensors.loadavg[1], m_sensors.loadavg[1] > 10 ? 1 : 2));
|
|
lcd.writeBuf(0, 36, utils::pNum(m_sensors.loadavg[2], m_sensors.loadavg[2] > 10 ? 1 : 2));
|
|
|
|
lcd.writeBuf(1, 0, utils::pad(std::to_string(m_sensors.cpu.tctl) + "/" + std::to_string(m_sensors.cpu.tccd1) + LCDDisplay::cc(0), 8, utils::just::left));
|
|
lcd.writeBuf(1, 7, utils::pad(utils::pBytes(m_sensors.ram.used, true), 6, utils::just::left));
|
|
if (m_sensors.hass_state >= 0)
|
|
lcd.writeBuf(1, 13, utils::pad(utils::pNum(m_sensors.hass_state) + "W", 4));
|
|
lcd.writeBuf(1, 19, std::to_string(m_sensors.sys.t_sensor) + LCDDisplay::cc(0)); // t_sensor temp
|
|
lcd.writeBuf(1, 32 - gpu_temps.size(), utils::pad(utils::pBytes(m_sensors.gpu.vram_used), 6));
|
|
lcd.writeBuf(1, 39 - gpu_temps.size(), gpu_temps + LCDDisplay::cc(0));
|
|
|
|
lcd.writeBuf(2, 0, utils::pad(std::to_string(static_cast<int>(m_sensors.cpu.ptotal)) + "W", 8, utils::just::left));
|
|
lcd.writeBuf(2, m_sensors.cpu.ptotal < 100 ? 4 : 5, std::to_string(cpu_fan) + "R"); // avg fan speed
|
|
lcd.writeBuf(2, 12, LCDDisplay::cc(3) + utils::pBytes(m_sensors.net.upload));
|
|
lcd.writeBuf(2, 19, LCDDisplay::cc(4) + utils::pBytes(m_sensors.net.download));
|
|
lcd.writeBuf(2, 26, utils::pad(std::to_string(m_sensors.gpu.fan_speed) + "R", 5));
|
|
lcd.writeBuf(2, 31, utils::pad(std::to_string(m_sensors.gpu.power_avg) + "/" + std::to_string(m_sensors.gpu.power_cap) + "W", 9));
|
|
|
|
lcd.writeBuf(3, 0, std::to_string(m_sensors.cpu.avg_freq) + "MHz");
|
|
lcd.writeBuf(3, 14, utils::pad(LCDDisplay::cc(2) + utils::pBytes(m_sensors.home.usage.free), 6));
|
|
lcd.writeBuf(3, 21, utils::pad(std::to_string(m_sensors.gpu.clock_core) + "/" + std::to_string(m_sensors.gpu.clock_mem) + "MHz ", 13));
|
|
lcd.writeBuf(3, 34, utils::pad(utils::pNum(m_sensors.gpu.vcore, 3) + "V", 5));
|
|
}
|
|
|
|
lcd.flushBuf();
|
|
lcd.clearBuf();
|
|
}
|
|
|
|
// goodbye msg
|
|
lcd.writeBuf(2, 0, utils::center("Goodbye", 40));
|
|
lcd.flushBuf();
|
|
} |