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

View File

@ -22,9 +22,10 @@ GF(MONITOR_CMD, "monitor");
GF(BOOTLOADER_CMD, "bootloader");
GF(UPTIME_CMD, "uptime");
GF(STATISTICS_CMD, "statistics");
GF(HISTOGRAM_CMD, "histogram");
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)
{
@ -146,6 +147,8 @@ class Terminal {
printUptime();
} else if (substringEquals(m_inputBuffer, detail::STATISTICS_CMD, m_inputSize)) {
printStatistics();
} else if (substringEquals(m_inputBuffer, detail::HISTOGRAM_CMD, m_inputSize)) {
printHistogram();
} else if (substringEquals(m_inputBuffer, detail::VERSION_CMD, m_inputSize)) {
printVersion();
} else {
@ -175,6 +178,7 @@ class Terminal {
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::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;
}
@ -202,7 +206,7 @@ class Terminal {
m_serial.template txNumber<uint8_t, 10, 3, ' '>(Controller::mapTemperature(i));
m_serial << F("%\t");
for (uint8_t s = 0; s < Controller::mapTemperature(i); ++s) {
m_serial << "#";
m_serial << '#';
}
m_serial << detail::ENDL;
}
@ -239,12 +243,23 @@ class Terminal {
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());
m_serial << F("Minimum temperature .: ") << floatBuffer << F(" C") << detail::ENDL;
sprintf(floatBuffer, "%.2f", Statistics::getMaxTemp());
m_serial << F("Maximum temperature .: ") << floatBuffer << F(" C") << detail::ENDL;
static void printHistogram()
{
auto normalizationFactor = Statistics::getMaximumHistogramSamples();
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()