2018-11-08 16:40:53 +01:00
|
|
|
import React, { PureComponent } from 'react';
|
|
|
|
|
import $ from 'jquery';
|
2018-12-23 09:15:32 +01:00
|
|
|
import { BasicGaugeColor, MappingType, RangeMap, Threshold, ValueMap } from 'app/types';
|
|
|
|
|
import { TimeSeriesVMs } from '@grafana/ui';
|
2018-11-08 16:40:53 +01:00
|
|
|
import config from '../core/config';
|
2018-11-16 16:41:01 +01:00
|
|
|
import kbn from '../core/utils/kbn';
|
2018-11-08 16:40:53 +01:00
|
|
|
|
|
|
|
|
interface Props {
|
2018-12-13 16:46:10 +01:00
|
|
|
baseColor: string;
|
2018-11-19 15:50:51 +01:00
|
|
|
decimals: number;
|
2018-12-11 16:44:42 +01:00
|
|
|
height: number;
|
|
|
|
|
mappings: Array<RangeMap | ValueMap>;
|
|
|
|
|
maxValue: number;
|
|
|
|
|
minValue: number;
|
|
|
|
|
prefix: string;
|
2018-11-08 16:40:53 +01:00
|
|
|
timeSeries: TimeSeriesVMs;
|
2018-11-20 17:01:58 +01:00
|
|
|
thresholds: Threshold[];
|
2018-12-11 16:44:42 +01:00
|
|
|
showThresholdMarkers: boolean;
|
2018-11-19 17:11:17 +01:00
|
|
|
showThresholdLabels: boolean;
|
|
|
|
|
stat: string;
|
2018-11-19 16:05:46 +01:00
|
|
|
suffix: string;
|
2018-12-11 16:44:42 +01:00
|
|
|
unit: string;
|
|
|
|
|
width: number;
|
2018-11-08 16:40:53 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class Gauge extends PureComponent<Props> {
|
2018-11-09 16:37:09 +01:00
|
|
|
canvasElement: any;
|
2018-11-08 16:40:53 +01:00
|
|
|
|
|
|
|
|
static defaultProps = {
|
2018-12-13 16:46:10 +01:00
|
|
|
baseColor: BasicGaugeColor.Green,
|
2018-11-08 16:40:53 +01:00
|
|
|
maxValue: 100,
|
2018-12-11 16:44:42 +01:00
|
|
|
mappings: [],
|
|
|
|
|
minValue: 0,
|
2018-11-19 16:00:18 +01:00
|
|
|
prefix: '',
|
2018-11-08 16:40:53 +01:00
|
|
|
showThresholdMarkers: true,
|
2018-11-19 17:11:17 +01:00
|
|
|
showThresholdLabels: false,
|
2018-11-19 16:05:46 +01:00
|
|
|
suffix: '',
|
2018-12-14 13:23:22 +01:00
|
|
|
thresholds: [],
|
2018-12-11 16:44:42 +01:00
|
|
|
unit: 'none',
|
2018-12-13 16:46:10 +01:00
|
|
|
stat: 'avg',
|
2018-11-08 16:40:53 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
componentDidMount() {
|
|
|
|
|
this.draw();
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-29 15:23:16 +01:00
|
|
|
componentDidUpdate() {
|
2018-11-09 16:37:09 +01:00
|
|
|
this.draw();
|
2018-11-08 16:40:53 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-12 11:04:34 +01:00
|
|
|
formatWithMappings(mappings, value) {
|
|
|
|
|
const valueMaps = mappings.filter(m => m.type === MappingType.ValueToText);
|
|
|
|
|
const rangeMaps = mappings.filter(m => m.type === MappingType.RangeToText);
|
|
|
|
|
|
|
|
|
|
const valueMap = valueMaps.map(mapping => {
|
|
|
|
|
if (mapping.value && value === mapping.value) {
|
|
|
|
|
return mapping.text;
|
|
|
|
|
}
|
|
|
|
|
})[0];
|
|
|
|
|
|
|
|
|
|
const rangeMap = rangeMaps.map(mapping => {
|
|
|
|
|
if (mapping.from && mapping.to && value > mapping.from && value < mapping.to) {
|
|
|
|
|
return mapping.text;
|
|
|
|
|
}
|
|
|
|
|
})[0];
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
rangeMap,
|
|
|
|
|
valueMap,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-16 16:41:01 +01:00
|
|
|
formatValue(value) {
|
2018-12-12 11:04:34 +01:00
|
|
|
const { decimals, mappings, prefix, suffix, unit } = this.props;
|
2018-11-16 16:41:01 +01:00
|
|
|
|
2018-11-19 15:50:51 +01:00
|
|
|
const formatFunc = kbn.valueFormats[unit];
|
2018-12-12 11:04:34 +01:00
|
|
|
const formattedValue = formatFunc(value, decimals);
|
|
|
|
|
|
|
|
|
|
if (mappings.length > 0) {
|
|
|
|
|
const { rangeMap, valueMap } = this.formatWithMappings(mappings, formattedValue);
|
|
|
|
|
|
|
|
|
|
if (valueMap) {
|
|
|
|
|
return valueMap;
|
|
|
|
|
} else if (rangeMap) {
|
|
|
|
|
return rangeMap;
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-12-04 12:56:54 +01:00
|
|
|
|
|
|
|
|
if (isNaN(value)) {
|
|
|
|
|
return '-';
|
|
|
|
|
}
|
2018-11-16 16:41:01 +01:00
|
|
|
|
2018-12-12 11:04:34 +01:00
|
|
|
return `${prefix} ${formattedValue} ${suffix}`;
|
2018-11-16 16:41:01 +01:00
|
|
|
}
|
|
|
|
|
|
2018-12-13 16:46:10 +01:00
|
|
|
getFontColor(value) {
|
2018-12-17 14:26:19 +01:00
|
|
|
const { baseColor, maxValue, thresholds } = this.props;
|
2018-12-13 16:46:10 +01:00
|
|
|
|
2018-12-17 14:26:19 +01:00
|
|
|
const atThreshold = thresholds.filter(threshold => value <= threshold.value);
|
2018-12-13 16:46:10 +01:00
|
|
|
|
2018-12-17 14:26:19 +01:00
|
|
|
if (atThreshold.length > 0) {
|
|
|
|
|
return atThreshold[0].color;
|
|
|
|
|
} else if (value <= maxValue) {
|
|
|
|
|
return BasicGaugeColor.Red;
|
2018-12-13 16:46:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return baseColor;
|
|
|
|
|
}
|
|
|
|
|
|
2018-11-08 16:40:53 +01:00
|
|
|
draw() {
|
2018-12-13 16:46:10 +01:00
|
|
|
const {
|
2018-12-14 14:18:18 +01:00
|
|
|
baseColor,
|
2018-12-13 16:46:10 +01:00
|
|
|
maxValue,
|
|
|
|
|
minValue,
|
|
|
|
|
timeSeries,
|
|
|
|
|
showThresholdLabels,
|
|
|
|
|
showThresholdMarkers,
|
|
|
|
|
thresholds,
|
|
|
|
|
width,
|
|
|
|
|
height,
|
|
|
|
|
stat,
|
|
|
|
|
} = this.props;
|
|
|
|
|
|
|
|
|
|
let value: string | number = '';
|
|
|
|
|
|
|
|
|
|
if (timeSeries[0]) {
|
|
|
|
|
value = timeSeries[0].stats[stat];
|
|
|
|
|
} else {
|
|
|
|
|
value = 'N/A';
|
|
|
|
|
}
|
2018-11-29 15:23:16 +01:00
|
|
|
|
2018-11-09 16:37:09 +01:00
|
|
|
const dimension = Math.min(width, height * 1.3);
|
2018-11-08 16:40:53 +01:00
|
|
|
const backgroundColor = config.bootData.user.lightTheme ? 'rgb(230,230,230)' : 'rgb(38,38,38)';
|
2018-11-09 16:37:09 +01:00
|
|
|
const fontScale = parseInt('80', 10) / 100;
|
|
|
|
|
const fontSize = Math.min(dimension / 5, 100) * fontScale;
|
2018-12-03 14:37:42 +01:00
|
|
|
const gaugeWidthReduceRatio = showThresholdLabels ? 1.5 : 1;
|
|
|
|
|
const gaugeWidth = Math.min(dimension / 6, 60) / gaugeWidthReduceRatio;
|
2018-11-09 16:37:09 +01:00
|
|
|
const thresholdMarkersWidth = gaugeWidth / 5;
|
|
|
|
|
const thresholdLabelFontSize = fontSize / 2.5;
|
|
|
|
|
|
2018-12-14 13:23:22 +01:00
|
|
|
const formattedThresholds = [
|
|
|
|
|
{ value: minValue, color: BasicGaugeColor.Green },
|
|
|
|
|
...thresholds.map((threshold, index) => {
|
|
|
|
|
return {
|
|
|
|
|
value: threshold.value,
|
2018-12-17 14:26:19 +01:00
|
|
|
color: index === 0 ? threshold.color : thresholds[index].color,
|
2018-12-14 13:23:22 +01:00
|
|
|
};
|
|
|
|
|
}),
|
|
|
|
|
{
|
|
|
|
|
value: maxValue,
|
2018-12-14 14:18:18 +01:00
|
|
|
color: thresholds.length > 0 ? BasicGaugeColor.Red : baseColor,
|
2018-12-14 13:23:22 +01:00
|
|
|
},
|
|
|
|
|
];
|
2018-11-08 16:40:53 +01:00
|
|
|
|
|
|
|
|
const options = {
|
|
|
|
|
series: {
|
|
|
|
|
gauges: {
|
|
|
|
|
gauge: {
|
2018-12-13 16:46:10 +01:00
|
|
|
min: minValue,
|
|
|
|
|
max: maxValue,
|
2018-11-08 16:40:53 +01:00
|
|
|
background: { color: backgroundColor },
|
|
|
|
|
border: { color: null },
|
|
|
|
|
shadow: { show: false },
|
2018-11-09 16:37:09 +01:00
|
|
|
width: gaugeWidth,
|
2018-11-08 16:40:53 +01:00
|
|
|
},
|
|
|
|
|
frame: { show: false },
|
|
|
|
|
label: { show: false },
|
|
|
|
|
layout: { margin: 0, thresholdWidth: 0 },
|
|
|
|
|
cell: { border: { width: 0 } },
|
|
|
|
|
threshold: {
|
2018-11-09 16:37:09 +01:00
|
|
|
values: formattedThresholds,
|
2018-11-08 16:40:53 +01:00
|
|
|
label: {
|
2018-11-19 17:11:17 +01:00
|
|
|
show: showThresholdLabels,
|
2018-11-09 16:37:09 +01:00
|
|
|
margin: thresholdMarkersWidth + 1,
|
|
|
|
|
font: { size: thresholdLabelFontSize },
|
2018-11-08 16:40:53 +01:00
|
|
|
},
|
|
|
|
|
show: showThresholdMarkers,
|
2018-11-09 16:37:09 +01:00
|
|
|
width: thresholdMarkersWidth,
|
2018-11-08 16:40:53 +01:00
|
|
|
},
|
|
|
|
|
value: {
|
2018-12-13 16:46:10 +01:00
|
|
|
color: this.getFontColor(value),
|
2018-11-08 16:40:53 +01:00
|
|
|
formatter: () => {
|
2018-12-14 13:23:22 +01:00
|
|
|
return this.formatValue(value);
|
2018-11-08 16:40:53 +01:00
|
|
|
},
|
|
|
|
|
font: {
|
2018-11-09 16:37:09 +01:00
|
|
|
size: fontSize,
|
2018-11-08 16:40:53 +01:00
|
|
|
family: '"Helvetica Neue", Helvetica, Arial, sans-serif',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
show: true,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
|
2018-11-09 16:37:09 +01:00
|
|
|
const plotSeries = {
|
2018-11-14 13:20:19 +01:00
|
|
|
data: [[0, value]],
|
2018-11-09 16:37:09 +01:00
|
|
|
};
|
|
|
|
|
|
2018-11-08 16:40:53 +01:00
|
|
|
try {
|
2018-11-09 16:37:09 +01:00
|
|
|
$.plot(this.canvasElement, [plotSeries], options);
|
2018-11-08 16:40:53 +01:00
|
|
|
} catch (err) {
|
|
|
|
|
console.log('Gauge rendering error', err, options, timeSeries);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
render() {
|
2018-11-14 13:20:19 +01:00
|
|
|
const { height, width } = this.props;
|
2018-11-09 16:37:09 +01:00
|
|
|
|
2018-11-08 16:40:53 +01:00
|
|
|
return (
|
2018-11-19 17:11:17 +01:00
|
|
|
<div className="singlestat-panel">
|
2018-11-09 16:37:09 +01:00
|
|
|
<div
|
|
|
|
|
style={{
|
|
|
|
|
height: `${height * 0.9}px`,
|
|
|
|
|
width: `${Math.min(width, height * 1.3)}px`,
|
|
|
|
|
top: '10px',
|
|
|
|
|
margin: 'auto',
|
|
|
|
|
}}
|
|
|
|
|
ref={element => (this.canvasElement = element)}
|
|
|
|
|
/>
|
2018-11-08 16:40:53 +01:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
}
|
2018-11-09 16:37:09 +01:00
|
|
|
|
2018-11-14 13:20:19 +01:00
|
|
|
export default Gauge;
|