Implement eeprom stored persistent histogram

This commit is contained in:
BlackMark 2020-04-08 02:17:29 +02:00
parent cae18b98e7
commit b8a40aed17
4 changed files with 100 additions and 42 deletions

@ -1 +1 @@
Subproject commit 33a4d55f03451f487ace046a6f2328351fda69ee Subproject commit 3bcba0a1919906d7cb358d3d8185a064bc1aa705

View File

@ -8,59 +8,95 @@
#include "clock.hpp" #include "clock.hpp"
#include "controller.hpp" #include "controller.hpp"
uint64_t Statistics::m_lastTempSave = 0; uint64_t Statistics::m_lastTemperatureWriteback = 0;
double Statistics::m_minTemp; uint64_t Statistics::m_lastTemperatureSample = 0;
double Statistics::m_maxTemp; uint32_t Statistics::m_temperatureHistogram[TEMPERATURE_RANGE];
namespace { namespace {
// Must be in translation unit and cannot be forward declared // Must be in translation unit and cannot be forward declared
EEVAR(double, e_minTemp); EEARRAY(uint32_t, e_temperatureHistogram, 100);
EEVAR(double, e_maxTemp);
// Could be declared in every function that uses it, but that would be code duplication // Could be declared in every function that uses it, but that would be code duplication
Eeprom<e_minTemp, true> g_eepMinTemp; EepromArray<e_temperatureHistogram, EEARRAY_SIZE(e_temperatureHistogram), true> g_eepTemperatureHistogram;
Eeprom<e_maxTemp, true> g_eepMaxTemp;
} // namespace } // namespace
void Statistics::init() void Statistics::init()
{ {
m_minTemp = getMinTemp(); for (uint8_t i = 0; i < g_eepTemperatureHistogram.size(); ++i) {
m_maxTemp = getMaxTemp(); m_temperatureHistogram[i] = g_eepTemperatureHistogram[i];
using temperature_t = type::decay_t<decltype(m_temperatureHistogram[0])>;
if (isnan(m_minTemp)) { if (m_temperatureHistogram[i] == type::numeric_limits<temperature_t>::max()) {
m_minTemp = type::numeric_limits<double>::max(); m_temperatureHistogram[i] = 0;
} }
if (isnan(m_maxTemp)) {
m_maxTemp = type::numeric_limits<double>::lowest();
} }
} }
void Statistics::callback() void Statistics::callback()
{ {
if (Controller::m_dataAvailable) { if (Controller::m_dataAvailable) {
if (Controller::m_temperature < m_minTemp) { if (clk::millis() >= m_lastTemperatureSample + TEMPERATURE_SAMPLE_DELAY) {
m_minTemp = Controller::m_temperature; const auto temperature = clampTemperature(static_cast<int8_t>(round(Controller::m_temperature)));
} ++m_temperatureHistogram[temperature];
if (Controller::m_temperature > m_maxTemp) { m_lastTemperatureSample = clk::millis();
m_maxTemp = Controller::m_temperature;
} }
if (clk::millis() >= m_lastTempSave + TEMP_SAVE_DELAY) { if (clk::millis() >= m_lastTemperatureWriteback + TEMPERATURE_WRITEBACK_DELAY) {
g_eepMinTemp = m_minTemp; writeTemperatureHistogram();
g_eepMaxTemp = m_maxTemp; m_lastTemperatureWriteback = clk::millis();
m_lastTempSave = clk::millis();
} }
} }
} }
double Statistics::getMinTemp() uint8_t Statistics::getMinTemp()
{ {
return g_eepMinTemp; for (uint8_t i = 0; i < TEMPERATURE_RANGE; ++i) {
if (m_temperatureHistogram[i] > 0)
return i;
} }
double Statistics::getMaxTemp() return TEMPERATURE_RANGE;
{ }
return g_eepMaxTemp;
uint8_t Statistics::getMaxTemp()
{
for (int8_t i = TEMPERATURE_RANGE - 1; i >= 0; --i) {
if (m_temperatureHistogram[i] > 0)
return i;
}
return TEMPERATURE_RANGE;
}
uint32_t Statistics::getMaximumHistogramSamples()
{
uint32_t max = 1; // Avoid division by zero
for (uint8_t i = 0; i < TEMPERATURE_RANGE; ++i) {
if (m_temperatureHistogram[i] > max) {
max = m_temperatureHistogram[i];
}
}
return max;
}
uint8_t Statistics::getHistogram(const int8_t &temperature, const uint32_t &normalizationFactor)
{
return static_cast<uint8_t>(m_temperatureHistogram[clampTemperature(temperature)] / normalizationFactor);
}
uint8_t Statistics::clampTemperature(const int8_t &temperature)
{
if (temperature < 0)
return 0;
if (temperature >= TEMPERATURE_RANGE)
return TEMPERATURE_RANGE - 1;
return static_cast<uint8_t>(temperature);
}
void Statistics::writeTemperatureHistogram()
{
for (uint8_t i = 0; i < g_eepTemperatureHistogram.size(); ++i) {
g_eepTemperatureHistogram[i] = m_temperatureHistogram[i];
}
} }

View File

@ -7,14 +7,21 @@ class Statistics {
static void init(); static void init();
static void callback(); static void callback();
static double getMinTemp(); static uint8_t getMinTemp();
static double getMaxTemp(); static uint8_t getMaxTemp();
static uint32_t getMaximumHistogramSamples();
static uint8_t getHistogram(const int8_t &temperature, const uint32_t &normalizationFactor);
private: private:
static constexpr auto TEMP_SAVE_DELAY = 60000; static constexpr auto TEMPERATURE_WRITEBACK_DELAY = 1'800'000;
static constexpr auto TEMPERATURE_SAMPLE_DELAY = 1'000;
static constexpr auto TEMPERATURE_RANGE = 100;
static uint64_t m_lastTempSave; static uint64_t m_lastTemperatureWriteback;
static uint64_t m_lastTemperatureSample;
static uint32_t m_temperatureHistogram[TEMPERATURE_RANGE];
static double m_minTemp; static uint8_t clampTemperature(const int8_t &temperature);
static double m_maxTemp; static void writeTemperatureHistogram();
}; };

View File

@ -22,9 +22,10 @@ GF(MONITOR_CMD, "monitor");
GF(BOOTLOADER_CMD, "bootloader"); GF(BOOTLOADER_CMD, "bootloader");
GF(UPTIME_CMD, "uptime"); GF(UPTIME_CMD, "uptime");
GF(STATISTICS_CMD, "statistics"); GF(STATISTICS_CMD, "statistics");
GF(HISTOGRAM_CMD, "histogram");
GF(VERSION_CMD, "version"); GF(VERSION_CMD, "version");
GF(VERSION, "1.4"); GF(VERSION, "1.5");
static inline bool substringEquals(const char *str, const ::detail::FlashString *flashStr, const size_t &size) static inline bool substringEquals(const char *str, const ::detail::FlashString *flashStr, const size_t &size)
{ {
@ -146,6 +147,8 @@ class Terminal {
printUptime(); printUptime();
} else if (substringEquals(m_inputBuffer, detail::STATISTICS_CMD, m_inputSize)) { } else if (substringEquals(m_inputBuffer, detail::STATISTICS_CMD, m_inputSize)) {
printStatistics(); printStatistics();
} else if (substringEquals(m_inputBuffer, detail::HISTOGRAM_CMD, m_inputSize)) {
printHistogram();
} else if (substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) { } else if (substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
printVersion(); printVersion();
} else { } else {
@ -175,6 +178,7 @@ class Terminal {
m_serial << detail::BOOTLOADER_CMD << F(" .: enters the bootloader after 3 seconds") << detail::ENDL; m_serial << detail::BOOTLOADER_CMD << F(" .: enters the bootloader after 3 seconds") << detail::ENDL;
m_serial << detail::UPTIME_CMD << F(" .....: show system uptime") << detail::ENDL; m_serial << detail::UPTIME_CMD << F(" .....: show system uptime") << detail::ENDL;
m_serial << detail::STATISTICS_CMD << F(" .: Print overall statistics like min and max temp") << detail::ENDL; m_serial << detail::STATISTICS_CMD << F(" .: Print overall statistics like min and max temp") << detail::ENDL;
m_serial << detail::HISTOGRAM_CMD << F(" ..: Prints a histogram of the temperature") << detail::ENDL;
m_serial << detail::VERSION_CMD << F(" ....: displays firmware version") << detail::ENDL; m_serial << detail::VERSION_CMD << F(" ....: displays firmware version") << detail::ENDL;
} }
@ -202,7 +206,7 @@ class Terminal {
m_serial.template txNumber<uint8_t, 10, 3, ' '>(Controller::mapTemperature(i)); m_serial.template txNumber<uint8_t, 10, 3, ' '>(Controller::mapTemperature(i));
m_serial << F("%\t"); m_serial << F("%\t");
for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) { for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) {
m_serial << "#"; m_serial << '#';
} }
m_serial << detail::ENDL; m_serial << detail::ENDL;
} }
@ -239,12 +243,23 @@ class Terminal {
static void printStatistics() static void printStatistics()
{ {
char floatBuffer[16]; m_serial << F("Minimum temperature .: ") << Statistics::getMinTemp() << F(" C") << detail::ENDL;
m_serial << F("Maximum temperature .: ") << Statistics::getMaxTemp() << F(" C") << detail::ENDL;
}
sprintf(floatBuffer, "%.2f", Statistics::getMinTemp()); static void printHistogram()
m_serial << F("Minimum temperature .: ") << floatBuffer << F(" C") << detail::ENDL; {
sprintf(floatBuffer, "%.2f", Statistics::getMaxTemp()); auto normalizationFactor = Statistics::getMaximumHistogramSamples();
m_serial << F("Maximum temperature .: ") << floatBuffer << F(" C") << detail::ENDL; normalizationFactor = (normalizationFactor / 100 > 1) ? (normalizationFactor / 100) : 1;
for (uint8_t t = 10; t <= 60; ++t) {
m_serial.template txNumber<uint8_t, 10, 2>(t);
m_serial << F(" C\t");
for (uint8_t i = 0; i < Statistics::getHistogram(t, normalizationFactor); ++i) {
m_serial << '#';
}
m_serial << detail::ENDL;
}
} }
static void printVersion() static void printVersion()