/* **************************************************************************** * Copyright 2019 Open Systems Development BV * * * * Permission is hereby granted, free of charge, to any person obtaining a * * copy of this software and associated documentation files (the "Software"), * * to deal in the Software without restriction, including without limitation * * the rights to use, copy, modify, merge, publish, distribute, sublicense, * * and/or sell copies of the Software, and to permit persons to whom the * * Software is furnished to do so, subject to the following conditions: * * * * The above copyright notice and this permission notice shall be included in * * all copies or substantial portions of the Software. * * * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING * * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER * * DEALINGS IN THE SOFTWARE. * * ***************************************************************************/ #include "ihistogram.h" // std #include #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