/* 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 */ #include "ihistogram.h" // std #include #include #include #include namespace osdev { namespace components { namespace mqtt { namespace measurement { IHistogram::~IHistogram() = default; namespace { std::pair largestValueUnderThreshold(const std::vector& hist, double thresholdValue) { int bin = -1; std::size_t maxValue = 0; // do not use the outlier bins for this calculation. for (std::size_t i = 1; i < hist.size() - 1; ++i) { if (hist[i] > maxValue && hist[i] < thresholdValue) { bin = static_cast(i); maxValue = hist[i]; } } return { bin, maxValue }; } } // namespace std::string visualizeHistogram(const IHistogram& histogram) { const std::size_t diagramHeight = 10; auto histData = histogram.histogramData(); // take a snapshot of the histogram data. const auto& hist = histData.data(); std::ostringstream oss; oss << "Histogram for " << histogram.id() << "\\n"; auto minmax = std::minmax_element(hist.begin(), hist.end()); auto minCountValueString = std::to_string(*minmax.first); auto maxCountValueString = std::to_string(*minmax.second); auto countValueFieldWidth = static_cast(std::max(minCountValueString.size(), maxCountValueString.size())); auto verticalSize = (*minmax.second - *minmax.first) / static_cast(diagramHeight); auto totalMeasurements = std::accumulate(hist.begin(), hist.end(), 0); oss << "Total nr of measurements: " << totalMeasurements << ", Nr of buckets: " << histogram.numBuckets() << ", bucket width: " << histogram.bucketWidth() << ", vertical resolution: " << verticalSize << "\\n"; auto binValuePair = largestValueUnderThreshold(hist, verticalSize); if (binValuePair.first >= 0) { oss << "Largest value under threshold: " << binValuePair.second << " in bin " << binValuePair.first << "\\n"; } if (hist.front() > 0) { oss << "Outliers to the left: " << hist.front() << ", smallest value: " << histData.smallestValueString() << histogram.unit() << "\\n"; } if (hist.back() > 0) { oss << "Outliers to the right: " << hist.back() << ", largest value: " << histData.largestValueString() << histogram.unit() << "\\n"; } oss << "\\nSince last clear:\\n" << " number of values : " << histData.numberOfValuesSinceLastClear() << "\\n" << " smallest value : " << histData.smallestValueSinceLastClearString() << histogram.unit() << "\\n" << " largest value : " << histData.largestValueSinceLastClearString() << histogram.unit() << "\\n" << " average value : " << histData.averageValueSinceLastClearString() << histogram.unit() << "\\n\\n"; oss << std::setw(countValueFieldWidth) << maxCountValueString << " |"; for (std::size_t line = 0; line < diagramHeight; ++line) { for (const auto& cnt : hist) { if ((cnt - *minmax.first) > (diagramHeight - line - 1) * verticalSize) { oss << '*'; } else { oss << ' '; } } oss << "|\\n"; if (line + 1 < diagramHeight) { oss << std::setw(countValueFieldWidth + 2) << '|'; } } oss << std::setw(countValueFieldWidth) << minCountValueString << " |/" << std::string(hist.size() - 2, '-') << "\\| " << histogram.unit() << "\\n"; auto minValueString = histogram.minValueString(); auto maxValueString = histogram.maxValueString(); auto valueFieldWidth = std::max(minValueString.size(), maxValueString.size()); for (std::size_t line = 0; line < valueFieldWidth; ++line) { oss << std::setw(countValueFieldWidth + 3 + static_cast(line)) << ""; oss << (line < minValueString.size() ? minValueString[line] : ' '); if (line < maxValueString.size()) { oss << std::string(histogram.numBuckets() - 1, ' ') << maxValueString[line]; } oss << "\\n"; } // Add the non zero data to the log for later analysis // format: binnumber:value;binnumber:value;.... bool first = true; for (std::size_t idx = 0; idx < hist.size(); ++idx) { if (hist[idx] > 0) { if (!first) { oss << ';'; } else { oss << "\\n"; first = false; } oss << idx << ':' << hist[idx]; } } return oss.str(); } std::ostream& operator<<(std::ostream& os, const IHistogram& rhs) { return os << rhs.toString(); } } // End namespace measurement } // End namespace mqtt } // End namespace components } // End namespace osdev