diff --git a/include/app.hpp b/include/app.hpp index 8b811dd..327bfa2 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -1,14 +1,36 @@ #ifndef APP_HPP #define APP_HPP +#include #include #include #include #include #include #include +#include +#include class Application { + +public: + struct Config { + uint16_t lcd_interval_ms = 500; + uint8_t lcd_contrast = 215; + + std::string lcd_port; + std::string cpu_pciaddr = "0000:00:18.3"; + std::string gpu_pciaddr = "0000:0b:00.0"; + std::string sys_sensor = "nct6775.656"; + std::string sys_ifname = "enp4s0f0"; + std::string home_dir = "/home/drath"; + }; + + explicit Application(const Config& config); + ~Application(); + void Run(); + +private: struct Sensors { sys::TimeDate td; double avg[3] = {0.0f}; @@ -22,29 +44,21 @@ class Application { std::mutex mtx; // making sure the struct isnt accessed by multiple threads at once }; - struct Semaphores { - inline static std::binary_semaphore display{0}; - inline static std::binary_semaphore main{0}; + struct Control { + std::binary_semaphore display{0}; + std::binary_semaphore main{0}; + std::atomic_bool quit{false}; + std::stop_source stop; }; - std::string lcd_port; - uint16_t lcd_interval_ms = 500; - uint8_t lcd_contrast = 215; - - std::string cpu_pciaddr = "0000:00:18.3"; - std::string gpu_pciaddr = "0000:0b:00.0"; - std::string sys_sensor = "nct6775.656"; - std::string sys_ifname = "enp4s0f0"; - std::string home_dir = "/home/drath"; + Config m_config; // m_ is member variable + Control m_control; + static Application* s_instance; static void signalHandler(int); static void hassThread(const std::stop_token& st, Sensors& s); - static void sensorsThread(const std::stop_token& st, Sensors& s); - static void displayThread(const std::stop_token& st, Sensors& s); - - public: - explicit Application(const std::string& display_port); - void Run(); + void sensorsThread(const std::stop_token& st, Sensors& s); + void displayThread(const std::stop_token& st, Sensors& s); }; #endif \ No newline at end of file diff --git a/main.cpp b/main.cpp index 3eb68ca..badc200 100644 --- a/main.cpp +++ b/main.cpp @@ -1,7 +1,18 @@ #include -// put app in a class w a constructor for all the basic params + int main() { - Application app("/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00"); // tis a symlink, can also be just /dev/ttyACM0) + Application::Config config; + + config.lcd_port = "/dev/serial/by-id/usb-Raspberry_Pi_Pico_E6616407E37AA12F-if00"; + config.lcd_interval_ms = 500; + config.lcd_contrast = 215; + config.cpu_pciaddr = "0000:00:18.3"; + config.gpu_pciaddr = "0000:0b:00.0"; + config.sys_sensor = "nct6775.656"; + config.sys_ifname = "enp4s0f0"; + config.home_dir = "/home/drath"; + + Application app(config); // tis a symlink, can also be just /dev/ttyACM0) app.Run(); return 0; } diff --git a/src/app.cpp b/src/app.cpp index 611744c..813a964 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -15,13 +15,12 @@ #include #include -// std::binary_semaphore Application::Semaphores::main{0}; -// std::binary_semaphore Application::Semaphores::display{0}; +Application* Application::s_instance = nullptr; void Application::signalHandler(int) { applog(LOG::WRN, "got SIGINT/SIGTERM, exiting..."); - this->Semaphores::main.release(); - this->Semaphores::display.release(); + if (s_instance) + s_instance->m_control.quit.store(true, std::memory_order_relaxed); } void Application::hassThread(const std::stop_token& st, Sensors& s) { @@ -40,11 +39,11 @@ void Application::hassThread(const std::stop_token& st, Sensors& s) { } void Application::sensorsThread(const std::stop_token& st, Sensors& s) { - applog(LOG::START, "Thread started, poll interval {} ms", lcd_interval_ms); - cpu::detectHwmon(&s.cpu, cpu_pciaddr); - gpu::detectHwmon(&s.gpu, gpu_pciaddr); - sys::detectHwmon(&s.sys, sys_sensor); - s.home.mountpoint = home_dir; + applog(LOG::START, "Thread started, poll interval {} ms", m_config.lcd_interval_ms); + cpu::detectHwmon(&s.cpu, m_config.cpu_pciaddr); + gpu::detectHwmon(&s.gpu, m_config.gpu_pciaddr); + sys::detectHwmon(&s.sys, m_config.sys_sensor); + s.home.mountpoint = m_config.home_dir; while (!st.stop_requested()) { auto start = std::chrono::steady_clock::now(); @@ -55,25 +54,27 @@ void Application::sensorsThread(const std::stop_token& st, Sensors& s) { getloadavg(s.avg, 3); sys::getRam(&s.ram); sys::getDisk(&s.home); - sys::getNetwork(&s.net, sys_ifname); + sys::getNetwork(&s.net, m_config.sys_ifname); sys::updateSensors(&s.sys); cpu::update(&s.cpu); gpu::update(&s.gpu); } // log(LOG::DBG, "took {} microseconds", std::chrono::duration_cast(std::chrono::steady_clock::now() - start).count()); - this->Semaphores::display.release(); // unlocks display loop - std::this_thread::sleep_until(start + std::chrono::milliseconds(lcd_interval_ms)); + m_control.display.release(); // unlocks display loop + std::this_thread::sleep_until(start + std::chrono::milliseconds(m_config.lcd_interval_ms)); } + applog(LOG::INF, "Stopped thread"); } void Application::displayThread(const std::stop_token& st, Sensors& s) { + std::stop_callback cb(st, [&] { m_control.display.release(); }); applog(LOG::START, "Thread started"); // init internal variables of LCDDisplay class - LCDDisplay lcd(lcd_port); + LCDDisplay lcd(m_config.lcd_port); // basic lcd setup - lcd.setContrast(lcd_contrast); + 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 @@ -88,7 +89,8 @@ void Application::displayThread(const std::stop_token& st, Sensors& s) { // actual main display loop while (!st.stop_requested()) { - this->Semaphores::display.acquire(); // holds the loop until sensor data is ready + m_control.display.try_acquire_for(std::chrono::milliseconds(static_cast(m_config.lcd_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(s.mtx); @@ -137,26 +139,37 @@ void Application::displayThread(const std::stop_token& st, Sensors& s) { lcd.flushBuf(); } -Application::Application(const std::string& display_port) { - this->lcd_port = display_port; +Application::Application(const Config& config) : m_config(config) { +} + +Application::~Application() { + applog(LOG::START, "Application stopped"); } void Application::Run() { applog(LOG::START, "Application started"); - std::signal(SIGINT, signalHandler); - std::signal(SIGTERM, signalHandler); + s_instance = this; + std::signal(SIGINT, &Application::signalHandler); + std::signal(SIGTERM, &Application::signalHandler); { Sensors sensors; std::vector threads; - for (std::function funcs[] = {displayThread, sensorsThread, hassThread}; - auto& f : funcs) - { + std::function funcs[] = { + [this](const std::stop_token& st, Sensors& s) { displayThread(st, s); }, + [this](const std::stop_token& st, Sensors& s) { sensorsThread(st, s); }, + [this](const std::stop_token& st, Sensors& s) { hassThread(st, s); }, + }; + + // start threads in order + for (auto& f : funcs) { threads.emplace_back(f, std::ref(sensors)); // params: function, stop_token (implied), pointer to sensor struct std::this_thread::sleep_for(std::chrono::milliseconds(10)); // start threads with delay so displayThread can quit the app if theres no lcd } - this->Semaphores::main.acquire(); + // hold until signal recieved + while (!m_control.quit.load(std::memory_order_relaxed)) + std::this_thread::sleep_for(std::chrono::milliseconds(10)); // ugly } -} +} \ No newline at end of file