proxmox-widget-toolkit/src/panel/RRDChart.js
Thomas Lamprecht 4337ad5b74 rrd chart: add option to render values as power of two base
Signed-off-by: Thomas Lamprecht <t.lamprecht@proxmox.com>
2021-04-15 19:02:33 +02:00

197 lines
4.2 KiB
JavaScript

Ext.define('Proxmox.widget.RRDChart', {
extend: 'Ext.chart.CartesianChart',
alias: 'widget.proxmoxRRDChart',
unit: undefined, // bytes, bytespersecond, percent
powerOfTwo: false,
controller: {
xclass: 'Ext.app.ViewController',
init: function(view) {
this.powerOfTwo = view.powerOfTwo;
},
convertToUnits: function(value) {
let units = ['', 'k', 'M', 'G', 'T', 'P'];
let si = 0;
let format = '0.##';
if (value < 0.1) format += '#';
const baseValue = this.powerOfTwo ? 1024 : 1000;
while (value >= baseValue && si < units.length -1) {
value = value / baseValue;
si++;
}
// javascript floating point weirdness
value = Ext.Number.correctFloat(value);
// limit decimal points
value = Ext.util.Format.number(value, format);
let unit = units[si];
if (this.powerOfTwo) unit += 'i';
return `${value.toString()} ${unit}`;
},
leftAxisRenderer: function(axis, label, layoutContext) {
let me = this;
return me.convertToUnits(label);
},
onSeriesTooltipRender: function(tooltip, record, item) {
let view = this.getView();
let suffix = '';
if (view.unit === 'percent') {
suffix = '%';
} else if (view.unit === 'bytes') {
suffix = 'B';
} else if (view.unit === 'bytespersecond') {
suffix = 'B/s';
}
let prefix = item.field;
if (view.fieldTitles && view.fieldTitles[view.fields.indexOf(item.field)]) {
prefix = view.fieldTitles[view.fields.indexOf(item.field)];
}
let v = this.convertToUnits(record.get(item.field));
let t = new Date(record.get('time'));
tooltip.setHtml(`${prefix}: ${v}${suffix}<br>${t}`);
},
onAfterAnimation: function(chart, eopts) {
// if the undo button is disabled, disable our tool
let ourUndoZoomButton = chart.header.tools[0];
let undoButton = chart.interactions[0].getUndoButton();
ourUndoZoomButton.setDisabled(undoButton.isDisabled());
},
},
width: 770,
height: 300,
animation: false,
interactions: [
{
type: 'crosszoom',
},
],
legend: {
padding: 0,
},
axes: [
{
type: 'numeric',
position: 'left',
grid: true,
renderer: 'leftAxisRenderer',
minimum: 0,
},
{
type: 'time',
position: 'bottom',
grid: true,
fields: ['time'],
},
],
listeners: {
animationend: 'onAfterAnimation',
},
initComponent: function() {
let me = this;
if (!me.store) {
throw "cannot work without store";
}
if (!me.fields) {
throw "cannot work without fields";
}
me.callParent();
// add correct label for left axis
let axisTitle = "";
if (me.unit === 'percent') {
axisTitle = "%";
} else if (me.unit === 'bytes') {
axisTitle = "Bytes";
} else if (me.unit === 'bytespersecond') {
axisTitle = "Bytes/s";
} else if (me.fieldTitles && me.fieldTitles.length === 1) {
axisTitle = me.fieldTitles[0];
} else if (me.fields.length === 1) {
axisTitle = me.fields[0];
}
me.axes[0].setTitle(axisTitle);
me.updateHeader();
if (me.header && me.legend) {
me.header.padding = '4 9 4';
me.header.add(me.legend);
}
if (!me.noTool) {
me.addTool({
type: 'minus',
disabled: true,
tooltip: gettext('Undo Zoom'),
handler: function() {
let undoButton = me.interactions[0].getUndoButton();
if (undoButton.handler) {
undoButton.handler();
}
},
});
}
// add a series for each field we get
me.fields.forEach(function(item, index) {
let title = item;
if (me.fieldTitles && me.fieldTitles[index]) {
title = me.fieldTitles[index];
}
me.addSeries(Ext.apply(
{
type: 'line',
xField: 'time',
yField: item,
title: title,
fill: true,
style: {
lineWidth: 1.5,
opacity: 0.60,
},
marker: {
opacity: 0,
scaling: 0.01,
fx: {
duration: 200,
easing: 'easeOut',
},
},
highlightCfg: {
opacity: 1,
scaling: 1.5,
},
tooltip: {
trackMouse: true,
renderer: 'onSeriesTooltipRender',
},
},
me.seriesConfig,
));
});
// enable animation after the store is loaded
me.store.onAfter('load', function() {
me.setAnimation(true);
}, this, { single: true });
},
});