diff options
Diffstat (limited to 'src/pmchart/sampling.cpp')
-rw-r--r-- | src/pmchart/sampling.cpp | 880 |
1 files changed, 880 insertions, 0 deletions
diff --git a/src/pmchart/sampling.cpp b/src/pmchart/sampling.cpp new file mode 100644 index 0000000..f3e1507 --- /dev/null +++ b/src/pmchart/sampling.cpp @@ -0,0 +1,880 @@ +/* + * Copyright (c) 2012, Red Hat. + * Copyright (c) 2012, Nathan Scott. All Rights Reserved. + * Copyright (c) 2007, Aconex. All Rights Reserved. + * + * 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 of the License, 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. + */ +#include <limits> +#include "sampling.h" +#include "main.h" +#include <qnumeric.h> +#include <qwt_picker_machine.h> + +SamplingItem::SamplingItem(Chart *parent, + QmcMetric *mp, pmMetricSpec *msp, pmDesc *dp, + const QString &legend, Chart::Style style, int samples, int index) + : ChartItem(mp, msp, dp, legend) +{ + pmDesc desc = mp->desc().desc(); + + my.chart = parent; + my.info = QString::null; + + // initialize the pcp data and item data arrays + my.dataCount = 0; + my.data = NULL; + my.itemData = NULL; + resetValues(samples, 0.0, 0.0); + + // set base scale, then tweak if value to plot is time / time + my.scale = 1; + if (style != Chart::UtilisationStyle && + desc.sem == PM_SEM_COUNTER && desc.units.dimTime == 0) { + if (desc.units.scaleTime == PM_TIME_USEC) + my.scale = 0.000001; + else if (desc.units.scaleTime == PM_TIME_MSEC) + my.scale = 0.001; + } + + // create and attach the plot right here + my.curve = new SamplingCurve(label()); + my.curve->attach(parent); + + // the 1000 is arbitrary ... just want numbers to be monotonic + // decreasing as plots are added + my.curve->setZ(1000 - index); +} + +SamplingItem::~SamplingItem(void) +{ + if (my.data != NULL) + free(my.data); + if (my.itemData != NULL) + free(my.itemData); +} + +QwtPlotItem * +SamplingItem::item(void) +{ + return my.curve; +} + +QwtPlotCurve * +SamplingItem::curve(void) +{ + return my.curve; +} + +void +SamplingItem::resetValues(int values, double, double) +{ + size_t size; + + // Reset sizes of pcp data array and the plot data array + size = values * sizeof(my.data[0]); + if ((my.data = (double *)realloc(my.data, size)) == NULL) + nomem(); + size = values * sizeof(my.itemData[0]); + if ((my.itemData = (double *)realloc(my.itemData, size)) == NULL) + nomem(); + if (my.dataCount > values) + my.dataCount = values; +} + +void +SamplingItem::preserveSample(int index, int oldindex) +{ + if (my.dataCount > oldindex) + my.itemData[index] = my.data[index] = my.data[oldindex]; + else + my.itemData[index] = my.data[index] = qQNaN(); +} + +void +SamplingItem::punchoutSample(int index) +{ + my.data[index] = my.itemData[index] = qQNaN(); +} + +void +SamplingItem::updateValues(bool forward, + bool rateConvert, pmUnits *units, int sampleHistory, int, + double, double, double) +{ + pmAtomValue scaled, raw; + QmcMetric *metric = ChartItem::my.metric; + double value; + int sz; + + if (metric->numValues() < 1 || metric->error(0)) { + value = qQNaN(); + } else { + // convert raw value to current chart scale + raw.d = rateConvert ? metric->value(0) : metric->currentValue(0); + pmConvScale(PM_TYPE_DOUBLE, &raw, &ChartItem::my.units, &scaled, units); + value = scaled.d * my.scale; + } + + if (my.dataCount < sampleHistory) + sz = qMax(0, (int)(my.dataCount * sizeof(double))); + else + sz = qMax(0, (int)((my.dataCount - 1) * sizeof(double))); + + if (forward) { + memmove(&my.data[1], &my.data[0], sz); + memmove(&my.itemData[1], &my.itemData[0], sz); + my.data[0] = value; + } else { + memmove(&my.data[0], &my.data[1], sz); + memmove(&my.itemData[0], &my.itemData[1], sz); + my.data[my.dataCount - 1] = value; + } + + if (my.dataCount < sampleHistory) + my.dataCount++; +} + +void +SamplingItem::rescaleValues(pmUnits *new_units) +{ + pmUnits *old_units = &ChartItem::my.units; + pmAtomValue old_av, new_av; + + console->post("Chart::update change units from %s to %s", + pmUnitsStr(old_units), pmUnitsStr(new_units)); + + for (int i = my.dataCount - 1; i >= 0; i--) { + if (my.data[i] != qQNaN()) { + old_av.d = my.data[i]; + pmConvScale(PM_TYPE_DOUBLE, &old_av, old_units, &new_av, new_units); + my.data[i] = new_av.d; + } + if (my.itemData[i] != qQNaN()) { + old_av.d = my.itemData[i]; + pmConvScale(PM_TYPE_DOUBLE, &old_av, old_units, &new_av, new_units); + my.itemData[i] = new_av.d; + } + } +} + +void +SamplingItem::replot(int history, double *timeData) +{ + int count = qMin(history, my.dataCount); + my.curve->setRawSamples(timeData, my.itemData, count); +} + +void +SamplingItem::revive(void) +{ + if (removed()) { + setRemoved(false); + my.curve->attach(my.chart); + } +} + +void +SamplingItem::remove(void) +{ + setRemoved(true); + my.curve->detach(); + + // We can't really do this properly (free memory, etc) - working around + // metrics class limit (its using an ordinal index for metrics, remove any + // and we'll get problems. Which means the plots array must also remain + // unchanged, as we drive things via the metriclist at times. D'oh. + // This blows - it means we have to continue to fetch metrics for those + // metrics that have been removed from the chart, which may be remote + // hosts, hosts which are down (introducing retry issues...). Bother. + + //delete my.curve; + //free(my.legend); +} + +void +SamplingItem::setStroke(Chart::Style style, QColor color, bool antiAlias) +{ + int sem = metric()->desc().desc().sem; + bool step = (sem == PM_SEM_INSTANT || sem == PM_SEM_DISCRETE); + + my.curve->setLegendColor(color); + my.curve->setRenderHint(QwtPlotItem::RenderAntialiased, antiAlias); + + switch (style) { + case Chart::BarStyle: + my.curve->setPen(color); + my.curve->setBrush(QBrush(color, Qt::SolidPattern)); + my.curve->setStyle(QwtPlotCurve::Sticks); + break; + + case Chart::AreaStyle: + my.curve->setPen(color); + my.curve->setBrush(QBrush(color, Qt::SolidPattern)); + my.curve->setStyle(step? QwtPlotCurve::Steps : QwtPlotCurve::Lines); + break; + + case Chart::UtilisationStyle: + my.curve->setPen(QColor(Qt::black)); + my.curve->setStyle(QwtPlotCurve::Steps); + my.curve->setBrush(QBrush(color, Qt::SolidPattern)); + break; + + case Chart::LineStyle: + my.curve->setPen(color); + my.curve->setBrush(QBrush(Qt::NoBrush)); + my.curve->setStyle(step? QwtPlotCurve::Steps : QwtPlotCurve::Lines); + break; + + case Chart::StackStyle: + my.curve->setPen(QColor(Qt::black)); + my.curve->setBrush(QBrush(color, Qt::SolidPattern)); + my.curve->setStyle(QwtPlotCurve::Steps); + break; + + default: + break; + } +} + +void +SamplingItem::clearCursor() +{ + // nothing to do here. +} + +bool +SamplingItem::containsPoint(const QRectF &, int) +{ + return false; +} + +void +SamplingItem::updateCursor(const QPointF &p, int) +{ + QString title = my.chart->YAxisTitle(); + + my.info.sprintf("[%.2f", (float)p.y()); + if (title != QString::null) { + my.info.append(" "); + my.info.append(title); + } + my.info.append(" at "); + my.info.append(timeHiResString(p.x())); + my.info.append("]"); + + pmchart->setValueText(my.info); +} + +const QString & +SamplingItem::cursorInfo() +{ + return my.info; +} + +void +SamplingItem::copyRawDataPoint(int index) +{ + if (index < 0) + index = my.dataCount - 1; + my.itemData[index] = my.data[index]; +} + +int +SamplingItem::maximumDataCount(int maximum) +{ + return qMax(maximum, my.dataCount); +} + +void +SamplingItem::truncateData(int offset) +{ + for (int index = my.dataCount + 1; index < offset; index++) { + my.data[index] = 0; + // don't re-set dataCount ... so we don't plot these values, + // we just want them to count 0 towards any Stack aggregation + } +} + +double +SamplingItem::sumData(int index, double sum) +{ + if (index < 0) + index = my.dataCount - 1; + if (index < my.dataCount && !qIsNaN(my.data[index])) + sum += my.data[index]; + return sum; +} + +void +SamplingItem::copyRawDataArray(void) +{ + for (int index = 0; index < my.dataCount; index++) + my.itemData[index] = my.data[index]; +} + +void +SamplingItem::copyDataPoint(int index) +{ + if (hidden() || index >= my.dataCount) + my.itemData[index] = qQNaN(); + else + my.itemData[index] = my.data[index]; +} + +void +SamplingItem::setPlotUtil(int index, double sum) +{ + if (index < 0) + index = my.dataCount - 1; + if (hidden() || sum == 0.0 || + index >= my.dataCount || qIsNaN(my.data[index])) + my.itemData[index] = qQNaN(); + else + my.itemData[index] = 100.0 * my.data[index] / sum; +} + +double +SamplingItem::setPlotStack(int index, double sum) +{ + if (index < 0) + index = my.dataCount - 1; + if (!hidden() && !qIsNaN(my.itemData[index])) { + sum += my.itemData[index]; + my.itemData[index] = sum; + } + return sum; +} + +double +SamplingItem::setDataStack(int index, double sum) +{ + if (index < 0) + index = my.dataCount - 1; + if (hidden() || qIsNaN(my.data[index])) { + my.itemData[index] = qQNaN(); + } else { + sum += my.data[index]; + my.itemData[index] = sum; + } + return sum; +} + + +// +// SamplingCurve deals with overriding some QwtPlotCurve defaults; +// particularly around dealing with empty sections of chart (NaN), +// and the way the legend is rendered. +// + +void +SamplingCurve::drawSeries(QPainter *p, + const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, int from, int to) const +{ + int okFrom, okTo = from; + int size = (to > 0) ? to : dataSize(); + + while (okTo < size) { + okFrom = okTo; + while (qIsNaN(sample(okFrom).y()) && okFrom < size) + ++okFrom; + okTo = okFrom; + while (!qIsNaN(sample(okTo).y()) && okTo < size) + ++okTo; + if (okFrom < size) + QwtPlotCurve::drawSeries(p, xMap, yMap, canvasRect, okFrom, okTo-1); + } +} + + +// +// SamplingScaleEngine deals with rendering the vertical Y-Axis +// + +SamplingScaleEngine::SamplingScaleEngine() : QwtLinearScaleEngine() +{ + my.autoScale = true; + my.minimum = 0.0; + my.maximum = 1.0; +} + +void +SamplingScaleEngine::setScale(bool autoScale, + double minValue, double maxValue) +{ + my.autoScale = autoScale; + my.minimum = minValue; + my.maximum = maxValue; +} + +void +SamplingScaleEngine::autoScale(int maxSteps, double &minValue, + double &maxValue, double &stepSize) const +{ + if (my.autoScale) { + if (minValue > 0) + minValue = 0.0; + } else { + minValue = my.minimum; + maxValue = my.maximum; + } + QwtLinearScaleEngine::autoScale(maxSteps, minValue, maxValue, stepSize); +} + + +// +// The SamplingEngine implements all sampling-specific Chart behaviour +// +SamplingEngine::SamplingEngine(Chart *chart, pmDesc &desc) +{ + QwtPlotPicker *picker = chart->my.picker; + ChartEngine *engine = chart->my.engine; + + my.chart = chart; + my.rateConvert = engine->rateConvert(); + my.antiAliasing = engine->antiAliasing(); + + normaliseUnits(desc); + my.units = desc.units; + + my.scaleEngine = new SamplingScaleEngine(); + chart->setAxisScaleEngine(QwtPlot::yLeft, my.scaleEngine); + chart->setAxisScaleDraw(QwtPlot::yLeft, new QwtScaleDraw()); + + // use an individual point picker for sampled data + picker->setStateMachine(new QwtPickerDragPointMachine()); + picker->setRubberBand(QwtPicker::CrossRubberBand); + picker->setRubberBandPen(QColor(Qt::green)); +} + +SamplingItem * +SamplingEngine::samplingItem(int index) +{ + return (SamplingItem *)my.chart->my.items[index]; +} + +ChartItem * +SamplingEngine::addItem(QmcMetric *mp, pmMetricSpec *msp, pmDesc *desc, const QString &legend) +{ + int sampleHistory = my.chart->my.tab->group()->sampleHistory(); + int existingItemCount = my.chart->metricCount(); + SamplingItem *item = new SamplingItem(my.chart, mp, msp, desc, legend, + my.chart->my.style, + sampleHistory, existingItemCount); + + // Find current max count for all plot items + int i, size = 0; + for (i = 0; i < existingItemCount; i++) + size = samplingItem(i)->maximumDataCount(size); + // Zero any plot from there to end, so Stack<->Line transitions work + for (i = 0; i < existingItemCount; i++) + samplingItem(i)->truncateData(size); + + return item; +} + +void +SamplingEngine::normaliseUnits(pmDesc &desc) +{ + if (my.rateConvert && desc.sem == PM_SEM_COUNTER) { + if (desc.units.dimTime == 0) { + desc.units.dimTime = -1; + desc.units.scaleTime = PM_TIME_SEC; + } + else if (desc.units.dimTime == 1) { + desc.units.dimTime = 0; + // don't play with scaleTime, need native per item scaleTime + // so we can apply correct scaling via item->scale, e.g. in + // the msec -> msec/sec after rate conversion ... see the + // calculation for item->scale below + } + } +} + +bool +SamplingEngine::isCompatible(pmDesc &desc) +{ + console->post("SamplingEngine::isCompatible" + " type=%d, units=%s", desc.type, pmUnitsStr(&desc.units)); + + if (desc.type == PM_TYPE_EVENT || desc.type == PM_TYPE_HIGHRES_EVENT) + return false; + normaliseUnits(desc); + if (my.units.dimSpace != desc.units.dimSpace || + my.units.dimTime != desc.units.dimTime || + my.units.dimCount != desc.units.dimCount) + return false; + return true; +} + +void +SamplingEngine::updateValues(bool forward, + int size, int points, double left, double right, double delta) +{ + int i, index = forward ? 0 : -1; /* first or last data point */ + int itemCount = my.chart->metricCount(); + Chart::Style style = my.chart->my.style; + + // Drive new values into each chart item + for (int i = 0; i < itemCount; i++) { + samplingItem(i)->updateValues(forward, my.rateConvert, &my.units, + size, points, left, right, delta); + } + + if (style == Chart::BarStyle || style == Chart::AreaStyle || style == Chart::LineStyle) { + for (i = 0; i < itemCount; i++) + samplingItem(i)->copyRawDataPoint(index); + } + // Utilisation: like Stack, but normalize value to a percentage (0,100) + else if (style == Chart::UtilisationStyle) { + double sum = 0.0; + // compute sum + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->sumData(index, sum); + // scale all components + for (i = 0; i < itemCount; i++) + samplingItem(i)->setPlotUtil(index, sum); + // stack components + sum = 0.0; + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->setPlotStack(index, sum); + } + else if (style == Chart::StackStyle) { + double sum = 0.0; + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->setDataStack(index, sum); + } + +#if DESPERATE + for (i = 0; i < my.chart->metricCount(); i++) { + console->post(PmChart::DebugForce, "metric[%d] value %f", i, + samplingItem(i)->metric()->currentValue(0)); + } +#endif +} + +void +SamplingEngine::redoScale(void) +{ + bool rescale = false; + + // The 1,000 and 0.1 thresholds are just a heuristic guess. + // + // We're assuming lBound() plays no part in this, which is OK as + // the upper bound of the y-axis range (hBound()) drives the choice + // of appropriate units scaling. + // + if (my.scaleEngine->autoScale() && + my.chart->axisScaleDiv(QwtPlot::yLeft)->upperBound() > 1000) { + double scaled_max = my.chart->axisScaleDiv(QwtPlot::yLeft)->upperBound(); + if (my.units.dimSpace == 1) { + switch (my.units.scaleSpace) { + case PM_SPACE_BYTE: + my.units.scaleSpace = PM_SPACE_KBYTE; + rescale = true; + break; + case PM_SPACE_KBYTE: + my.units.scaleSpace = PM_SPACE_MBYTE; + rescale = true; + break; + case PM_SPACE_MBYTE: + my.units.scaleSpace = PM_SPACE_GBYTE; + rescale = true; + break; + case PM_SPACE_GBYTE: + my.units.scaleSpace = PM_SPACE_TBYTE; + rescale = true; + break; + case PM_SPACE_TBYTE: + my.units.scaleSpace = PM_SPACE_PBYTE; + rescale = true; + break; + case PM_SPACE_PBYTE: + my.units.scaleSpace = PM_SPACE_EBYTE; + rescale = true; + break; + } + if (rescale) { + // logic here depends on PM_SPACE_* values being consecutive + // integer values as the scale increases + scaled_max /= 1024; + while (scaled_max > 1000) { + my.units.scaleSpace++; + scaled_max /= 1024; + if (my.units.scaleSpace == PM_SPACE_EBYTE) break; + } + } + } + else if (my.units.dimTime == 1) { + switch (my.units.scaleTime) { + case PM_TIME_NSEC: + my.units.scaleTime = PM_TIME_USEC; + rescale = true; + scaled_max /= 1000; + break; + case PM_TIME_USEC: + my.units.scaleTime = PM_TIME_MSEC; + rescale = true; + scaled_max /= 1000; + break; + case PM_TIME_MSEC: + my.units.scaleTime = PM_TIME_SEC; + rescale = true; + scaled_max /= 1000; + break; + case PM_TIME_SEC: + my.units.scaleTime = PM_TIME_MIN; + rescale = true; + scaled_max /= 60; + break; + case PM_TIME_MIN: + my.units.scaleTime = PM_TIME_HOUR; + rescale = true; + scaled_max /= 60; + break; + } + if (rescale) { + // logic here depends on PM_TIME* values being consecutive + // integer values as the scale increases + while (scaled_max > 1000) { + my.units.scaleTime++; + if (my.units.scaleTime <= PM_TIME_SEC) + scaled_max /= 1000; + else + scaled_max /= 60; + if (my.units.scaleTime == PM_TIME_HOUR) break; + } + } + } + } + + if (rescale == false && + my.scaleEngine->autoScale() && + my.chart->axisScaleDiv(QwtPlot::yLeft)->upperBound() < 0.1) { + double scaled_max = my.chart->axisScaleDiv(QwtPlot::yLeft)->upperBound(); + if (my.units.dimSpace == 1) { + switch (my.units.scaleSpace) { + case PM_SPACE_KBYTE: + my.units.scaleSpace = PM_SPACE_BYTE; + rescale = true; + break; + case PM_SPACE_MBYTE: + my.units.scaleSpace = PM_SPACE_KBYTE; + rescale = true; + break; + case PM_SPACE_GBYTE: + my.units.scaleSpace = PM_SPACE_MBYTE; + rescale = true; + break; + case PM_SPACE_TBYTE: + my.units.scaleSpace = PM_SPACE_GBYTE; + rescale = true; + break; + case PM_SPACE_PBYTE: + my.units.scaleSpace = PM_SPACE_TBYTE; + rescale = true; + break; + case PM_SPACE_EBYTE: + my.units.scaleSpace = PM_SPACE_PBYTE; + rescale = true; + break; + } + if (rescale) { + // logic here depends on PM_SPACE_* values being consecutive + // integer values (in reverse) as the scale decreases + scaled_max *= 1024; + while (scaled_max < 0.1) { + my.units.scaleSpace--; + scaled_max *= 1024; + if (my.units.scaleSpace == PM_SPACE_BYTE) break; + } + } + } + else if (my.units.dimTime == 1) { + switch (my.units.scaleTime) { + case PM_TIME_USEC: + my.units.scaleTime = PM_TIME_NSEC; + rescale = true; + scaled_max *= 1000; + break; + case PM_TIME_MSEC: + my.units.scaleTime = PM_TIME_USEC; + rescale = true; + scaled_max *= 1000; + break; + case PM_TIME_SEC: + my.units.scaleTime = PM_TIME_MSEC; + rescale = true; + scaled_max *= 1000; + break; + case PM_TIME_MIN: + my.units.scaleTime = PM_TIME_SEC; + rescale = true; + scaled_max *= 60; + break; + case PM_TIME_HOUR: + my.units.scaleTime = PM_TIME_MIN; + rescale = true; + scaled_max *= 60; + break; + } + if (rescale) { + // logic here depends on PM_TIME* values being consecutive + // integer values (in reverse) as the scale decreases + while (scaled_max < 0.1) { + my.units.scaleTime--; + if (my.units.scaleTime < PM_TIME_SEC) + scaled_max *= 1000; + else + scaled_max *= 60; + if (my.units.scaleTime == PM_TIME_NSEC) break; + } + } + } + } + + if (rescale) { + // + // need to rescale ... we transform all of the historical (raw) + // data, new data will be taken care of by changing my.units. + // + for (int i = 0; i < my.chart->metricCount(); i++) + samplingItem(i)->rescaleValues(&my.units); + + if (my.chart->my.style == Chart::UtilisationStyle) + my.chart->setYAxisTitle("% utilization"); + else + my.chart->setYAxisTitle(pmUnitsStr(&my.units)); + my.chart->replot(); + } +} + +void +SamplingEngine::replot(void) +{ + GroupControl *group = my.chart->my.tab->group(); + int vh = group->visibleHistory(); + double *vp = group->timeAxisData(); + int itemCount = my.chart->metricCount(); + int maxCount = 0; + int i, m; + double sum; + +#if DESPERATE + console->post(PmChart::DebugForce, "SamplingEngine::replot %d items)", itemCount); +#endif + + for (i = 0; i < itemCount; i++) + samplingItem(i)->replot(vh, vp); + + switch (my.chart->style()) { + case Chart::BarStyle: + case Chart::AreaStyle: + case Chart::LineStyle: + for (i = 0; i < itemCount; i++) + samplingItem(i)->copyRawDataArray(); + break; + + case Chart::UtilisationStyle: + for (i = 0; i < itemCount; i++) + maxCount = samplingItem(i)->maximumDataCount(maxCount); + for (m = 0; m < maxCount; m++) { + sum = 0.0; + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->sumData(m, sum); + for (i = 0; i < itemCount; i++) + samplingItem(i)->setPlotUtil(m, sum); + sum = 0.0; + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->setPlotStack(m, sum); + } + break; + + case Chart::StackStyle: + for (i = 0; i < itemCount; i++) + maxCount = samplingItem(i)->maximumDataCount(maxCount); + for (m = 0; m < maxCount; m++) { + for (i = 0; i < itemCount; i++) + samplingItem(i)->copyDataPoint(m); + sum = 0.0; + for (i = 0; i < itemCount; i++) + sum = samplingItem(i)->setPlotStack(m, sum); + } + break; + + default: + break; + } +} + +void +SamplingEngine::scale(bool *autoScale, double *yMin, double *yMax) +{ + *autoScale = my.scaleEngine->autoScale(); + *yMin = my.scaleEngine->minimum(); + *yMax = my.scaleEngine->maximum(); +} + +void +SamplingEngine::setScale(bool autoScale, double yMin, double yMax) +{ + my.scaleEngine->setScale(autoScale, yMin, yMax); + + if (autoScale) + my.chart->setAxisAutoScale(QwtPlot::yLeft); + else + my.chart->setAxisScale(QwtPlot::yLeft, yMin, yMax); +} + +void +SamplingEngine::selected(const QPolygon &) +{ + // Nothing to do here. +} + +void +SamplingEngine::moved(const QPointF &p) +{ + my.chart->showPoint(p); +} + +void +SamplingEngine::setStyle(Chart::Style style) +{ + // Y-Axis title choice is difficult. A Utilisation plot by definition + // is dimensionless and scaled to a percentage, so a label of just + // "% utilization" makes sense ... there has been some argument in + // support of "% time utilization" as a special case when the metrics + // involve some aspect of time, but the base metrics in the common case + // are counters in units of time (e.g. the CPU view), which after rate + // conversion is indistinguishable from instantaneous or discrete + // metrics of dimension time^0 which are units compatible ... so we're + // opting for the simplest possible interpretation of utilization or + // everything else. + // + switch (style) { + case Chart::BarStyle: + case Chart::AreaStyle: + case Chart::LineStyle: + case Chart::StackStyle: + if (my.chart->style() == Chart::UtilisationStyle) + my.scaleEngine->setAutoScale(true); + my.chart->setYAxisTitle(pmUnitsStr(&my.units)); + break; + case Chart::UtilisationStyle: + my.scaleEngine->setScale(false, 0.0, 100.0); + my.chart->setYAxisTitle("% utilization"); + break; + default: + break; + } +} |