#pragma once #include #include #include #include "board.hpp" // Temperature histogram: one uint32 bucket per C 0..99, sampled once a // second, written back to EEPROM every 30 minutes (update() only touches // changed bytes). namespace app { class statistics { static constexpr std::uint8_t range = 100; static constexpr std::uint32_t sample_delay_ms = 1'000; static constexpr std::uint32_t writeback_delay_ms = 1'800'000; // What an erased cell reads back as, so a bucket nobody has written yet // counts as no samples rather than four billion. static constexpr std::uint32_t never_written = ~std::uint32_t{0}; using stored = avr::eeprom::var, 0>; static inline std::array m_histogram{}; static inline std::uint64_t m_last_sample = 0; static inline std::uint64_t m_last_writeback = 0; static constexpr std::uint8_t clamp(std::int8_t t) { return t < 0 ? 0 : (t >= range ? range - 1 : static_cast(t)); } public: static constexpr auto claims = stored::claims; static void init() { m_histogram = stored::read(); for (auto &bucket : m_histogram) { if (bucket == never_written) { bucket = 0; } } } static void record(std::int8_t celsius) { auto now = uptime::millis(); if (now >= m_last_sample + sample_delay_ms) { ++m_histogram[clamp(celsius)]; m_last_sample = now; } if (now >= m_last_writeback + writeback_delay_ms) { save(); m_last_writeback = now; } } static void save() { stored::update(m_histogram); } static void reset() { m_histogram = {}; stored::update(m_histogram); } static std::uint8_t min_temperature() { for (std::uint8_t i = 0; i < range; ++i) { if (m_histogram[i]) { return i; } } return range; } static std::uint8_t max_temperature() { for (std::uint8_t i = range; i > 0; --i) { if (m_histogram[i - 1]) { return i - 1; } } return 0; } static std::uint64_t total_samples() { std::uint64_t total = 0; for (auto bucket : m_histogram) { total += bucket; } return total; } static std::uint32_t highest_bucket() { std::uint32_t highest = 0; for (auto bucket : m_histogram) { if (bucket > highest) { highest = bucket; } } return highest; } static std::uint32_t bucket(std::uint8_t celsius) { return m_histogram[clamp(static_cast(celsius))]; } }; } // namespace app