]> git.proxmox.com Git - proxmox-widget-toolkit.git/blob - src/panel/RRDChart.js
3cea635992a2df54bfe6d24e14e76d09b4ce7f56
[proxmox-widget-toolkit.git] / src / panel / RRDChart.js
1 Ext.define('Proxmox.widget.RRDChart', {
2 extend: 'Ext.chart.CartesianChart',
3 alias: 'widget.proxmoxRRDChart',
4
5 unit: undefined, // bytes, bytespersecond, percent
6
7 powerOfTwo: false,
8
9 controller: {
10 xclass: 'Ext.app.ViewController',
11
12 init: function(view) {
13 this.powerOfTwo = view.powerOfTwo;
14 },
15
16 convertToUnits: function(value) {
17 let units = ['', 'k', 'M', 'G', 'T', 'P'];
18 let si = 0;
19 let format = '0.##';
20 if (value < 0.1) format += '#';
21 const baseValue = this.powerOfTwo ? 1024 : 1000;
22 while (value >= baseValue && si < units.length -1) {
23 value = value / baseValue;
24 si++;
25 }
26
27 // javascript floating point weirdness
28 value = Ext.Number.correctFloat(value);
29
30 // limit decimal points
31 value = Ext.util.Format.number(value, format);
32
33 let unit = units[si];
34 if (this.powerOfTwo) unit += 'i';
35
36 return `${value.toString()} ${unit}`;
37 },
38
39 leftAxisRenderer: function(axis, label, layoutContext) {
40 let me = this;
41 return me.convertToUnits(label);
42 },
43
44 onSeriesTooltipRender: function(tooltip, record, item) {
45 let view = this.getView();
46
47 let suffix = '';
48 if (view.unit === 'percent') {
49 suffix = '%';
50 } else if (view.unit === 'bytes') {
51 suffix = 'B';
52 } else if (view.unit === 'bytespersecond') {
53 suffix = 'B/s';
54 }
55
56 let prefix = item.field;
57 if (view.fieldTitles && view.fieldTitles[view.fields.indexOf(item.field)]) {
58 prefix = view.fieldTitles[view.fields.indexOf(item.field)];
59 }
60 let v = this.convertToUnits(record.get(item.field));
61 let t = new Date(record.get('time'));
62 tooltip.setHtml(`${prefix}: ${v}${suffix}<br>${t}`);
63 },
64
65 onAfterAnimation: function(chart, eopts) {
66 // if the undo button is disabled, disable our tool
67 let ourUndoZoomButton = chart.header.tools[0];
68 let undoButton = chart.interactions[0].getUndoButton();
69 ourUndoZoomButton.setDisabled(undoButton.isDisabled());
70 },
71 },
72
73 width: 770,
74 height: 300,
75 animation: false,
76 interactions: [
77 {
78 type: 'crosszoom',
79 },
80 ],
81 legend: {
82 padding: 0,
83 },
84 axes: [
85 {
86 type: 'numeric',
87 position: 'left',
88 grid: true,
89 renderer: 'leftAxisRenderer',
90 minimum: 0,
91 },
92 {
93 type: 'time',
94 position: 'bottom',
95 grid: true,
96 fields: ['time'],
97 },
98 ],
99 listeners: {
100 animationend: 'onAfterAnimation',
101 },
102
103 initComponent: function() {
104 let me = this;
105
106 if (!me.store) {
107 throw "cannot work without store";
108 }
109
110 if (!me.fields) {
111 throw "cannot work without fields";
112 }
113
114 me.callParent();
115
116 // add correct label for left axis
117 let axisTitle = "";
118 if (me.unit === 'percent') {
119 axisTitle = "%";
120 } else if (me.unit === 'bytes') {
121 axisTitle = "Bytes";
122 } else if (me.unit === 'bytespersecond') {
123 axisTitle = "Bytes/s";
124 } else if (me.fieldTitles && me.fieldTitles.length === 1) {
125 axisTitle = me.fieldTitles[0];
126 } else if (me.fields.length === 1) {
127 axisTitle = me.fields[0];
128 }
129
130 me.axes[0].setTitle(axisTitle);
131
132 me.updateHeader();
133
134 if (me.header && me.legend) {
135 me.header.padding = '4 9 4';
136 me.header.add(me.legend);
137 }
138
139 if (!me.noTool) {
140 me.addTool({
141 type: 'minus',
142 disabled: true,
143 tooltip: gettext('Undo Zoom'),
144 handler: function() {
145 let undoButton = me.interactions[0].getUndoButton();
146 if (undoButton.handler) {
147 undoButton.handler();
148 }
149 },
150 });
151 }
152
153 // add a series for each field we get
154 me.fields.forEach(function(item, index) {
155 let title = item;
156 if (me.fieldTitles && me.fieldTitles[index]) {
157 title = me.fieldTitles[index];
158 }
159 me.addSeries(Ext.apply(
160 {
161 type: 'line',
162 xField: 'time',
163 yField: item,
164 title: title,
165 fill: true,
166 style: {
167 lineWidth: 1.5,
168 opacity: 0.60,
169 },
170 marker: {
171 opacity: 0,
172 scaling: 0.01,
173 fx: {
174 duration: 200,
175 easing: 'easeOut',
176 },
177 },
178 highlightCfg: {
179 opacity: 1,
180 scaling: 1.5,
181 },
182 tooltip: {
183 trackMouse: true,
184 renderer: 'onSeriesTooltipRender',
185 },
186 },
187 me.seriesConfig,
188 ));
189 });
190
191 // enable animation after the store is loaded
192 me.store.onAfter('load', function() {
193 me.setAnimation(true);
194 }, this, { single: true });
195 },
196 });