/* Copyright (C) 2019 * * This file is part of the osdev components suite * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2, or (at your option) any * later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifndef OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H #define OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H #include #include #include #include #include #include #include "utils.h" #include "ihistogram.h" namespace osdev { namespace components { namespace mqtt { namespace measurement { template double fabs(T val) { return std::fabs(val); } template double fabs(const std::chrono::duration& dur) { return std::fabs(dur.count()); } template double to_double(T val) { return static_cast(val); } template double to_double(const std::chrono::duration& dur) { return static_cast(dur.count()); } template std::string to_string(T val) { return std::to_string(val); } template std::string to_string(const std::chrono::duration& dur) { return std::to_string(dur.count()); } template T min(T) { return std::numeric_limits::min(); } template std::chrono::duration min(const std::chrono::duration&) { return std::chrono::duration::min(); } template T max(T) { return std::numeric_limits::max(); } template std::chrono::duration max(const std::chrono::duration&) { return std::chrono::duration::max(); } /** * @brief This class implements a histogram for typed values. * Outlier values to the left are counted on the first bin and to the right on the last bin. * @tparam T The value type. T must be default constructable. * @tparam Buckets The number of buckets to use for this histogram. Default is 10 buckets. */ template class Histogram : public IHistogram { public: /** * @brief Construct a histogram. * @param id The histogram identification * @param minValue The minimum value of the histogram. * @param maxValue The maxmim value of the histogram. * @param unit The unit of the values that are collected in this histogram. */ Histogram(const std::string& id, T minValue, T maxValue, const char* unit); /** * @brief Set a value in the histogram. */ void setValue(T value); /** * @return true when values are added since the last time a snapshot was taken of the histogram or a reset was performed, false otherwise. */ bool isUpdated() const; /** * @brief Reset the histogram. * All counts are made zero and the smallest and largest recorded values are set to default. */ void reset(); // IHistogram implementation virtual const std::string& id() const override; virtual std::size_t numBuckets() const override; virtual double bucketWidth() const override; virtual std::string minValueString() const override; virtual std::string maxValueString() const override; virtual const std::string& unit() const override; virtual HistogramData histogramData() const override; virtual std::size_t numberOfValuesSinceLastClear() const override; virtual std::string smallestValueSinceLastClear() const override; virtual std::string largestValueSinceLastClear() const override; virtual std::string averageValueSinceLastClear() const override; virtual void clearRunningValues() override; virtual std::string toString() const override; private: /** * @brief Get the index on which to count the given value. * The outliers to the left are counted on index 0 and to the right on index size() - 1. * @param value The value to get the index for. * @return the index in the histogram on which to count the given value. */ inline std::size_t index(T value) { if (value > m_maxValue) { return m_histogram.size() - 1; } if (value < m_minValue) { return 0; } return 1 + static_cast(to_double((value - m_minValue) / m_bucketWidth)); } const std::string m_id; const std::string m_unit; const T m_minValue; const T m_maxValue; const double m_bucketWidth; T m_smallestValue; T m_largestValue; T m_smallestValueSinceLastClear; T m_largestValueSinceLastClear; T m_summedValueSinceLastClear; std::size_t m_numberOfValuesSinceLastClear; mutable std::mutex m_mutex; std::vector m_histogram; mutable std::atomic_bool m_isUpdated; }; template Histogram::Histogram(const std::string& _id, T minValue, T maxValue, const char* _unit) : m_id(_id) , m_unit(_unit) , m_minValue(minValue) , m_maxValue(maxValue) , m_bucketWidth(fabs((m_maxValue - m_minValue)) / Buckets) , m_smallestValue{ max(maxValue) } , m_largestValue{ min(maxValue) } , m_smallestValueSinceLastClear{ max(maxValue) } , m_largestValueSinceLastClear{ min(maxValue) } , m_summedValueSinceLastClear{} , m_numberOfValuesSinceLastClear{} , m_mutex() , m_histogram(Buckets + 2) // + 2 for the out of bound buckets , m_isUpdated(false) { } template void Histogram::setValue(T value) { std::lock_guard lg(m_mutex); apply_unused_parameters(lg); if (value > m_largestValueSinceLastClear) { m_largestValueSinceLastClear = value; } if (value < m_smallestValueSinceLastClear) { m_smallestValueSinceLastClear = value; } if (value > m_largestValue) { m_largestValue = value; } if (value < m_smallestValue) { m_smallestValue = value; } m_summedValueSinceLastClear += value; ++m_numberOfValuesSinceLastClear; m_histogram[this->index(value)]++; m_isUpdated = true; } template bool Histogram::isUpdated() const { return m_isUpdated; } template void Histogram::reset() { std::lock_guard lg(m_mutex); apply_unused_parameters(lg); m_smallestValue = T{ max(m_maxValue) }; m_largestValue = T{ min(m_maxValue) }; m_smallestValueSinceLastClear = T{ max(m_maxValue) }; m_largestValueSinceLastClear = T{ min(m_maxValue) }; m_summedValueSinceLastClear = T{}; m_numberOfValuesSinceLastClear = 0; std::fill(m_histogram.begin(), m_histogram.end(), 0); m_isUpdated = false; } template const std::string& Histogram::id() const { return m_id; } template std::size_t Histogram::numBuckets() const { return Buckets; } template double Histogram::bucketWidth() const { return m_bucketWidth; } template std::string Histogram::minValueString() const { return to_string(m_minValue); } template std::string Histogram::maxValueString() const { return to_string(m_maxValue); } template const std::string& Histogram::unit() const { return m_unit; } template HistogramData Histogram::histogramData() const { std::lock_guard lg(m_mutex); apply_unused_parameters(lg); m_isUpdated = false; return HistogramData(to_string(m_smallestValue), to_string(m_largestValue), to_string(m_smallestValueSinceLastClear), to_string(m_largestValueSinceLastClear), averageValueSinceLastClear(), m_numberOfValuesSinceLastClear, m_histogram); } template std::size_t Histogram::numberOfValuesSinceLastClear() const { return m_numberOfValuesSinceLastClear; } template std::string Histogram::smallestValueSinceLastClear() const { return to_string(m_smallestValueSinceLastClear); } template std::string Histogram::largestValueSinceLastClear() const { return to_string(m_largestValueSinceLastClear); } template std::string Histogram::averageValueSinceLastClear() const { if (0 == m_numberOfValuesSinceLastClear) { return "undefined"; } return to_string(to_double(m_summedValueSinceLastClear) / m_numberOfValuesSinceLastClear); } template void Histogram::clearRunningValues() { std::lock_guard lg(m_mutex); apply_unused_parameters(lg); m_smallestValueSinceLastClear = T{ max(m_maxValue) }; m_largestValueSinceLastClear = T{ min(m_maxValue) }; m_summedValueSinceLastClear = T{}; m_numberOfValuesSinceLastClear = 0; } template std::string Histogram::toString() const { return visualizeHistogram(*this); } } // End namespace measurement } // End namespace mqtt } // End namespace components } // End namespace osdev #endif // OSDEV_COMPONENTS_MQTT_MEASUREMENT_HISTOGRAM_H