]>
Commit | Line | Data |
---|---|---|
4226b8fb | 1 | /* |
eb7f9197 TL |
2 | * This is a running chart widget you add time datapoints to it, and we only |
3 | * show the last x of it used for ceph performance charts | |
4226b8fb DC |
4 | */ |
5 | Ext.define('PVE.widget.RunningChart', { | |
6 | extend: 'Ext.container.Container', | |
7 | alias: 'widget.pveRunningChart', | |
8 | ||
9 | layout: { | |
10 | type: 'hbox', | |
f6710aac | 11 | align: 'center', |
4226b8fb DC |
12 | }, |
13 | items: [ | |
14 | { | |
15 | width: 80, | |
16 | xtype: 'box', | |
17 | itemId: 'title', | |
18 | data: { | |
f6710aac | 19 | title: '', |
4226b8fb | 20 | }, |
f6710aac | 21 | tpl: '<h3>{title}:</h3>', |
4226b8fb DC |
22 | }, |
23 | { | |
24 | flex: 1, | |
25 | xtype: 'cartesian', | |
26 | height: '100%', | |
27 | itemId: 'chart', | |
28 | border: false, | |
29 | axes: [ | |
30 | { | |
31 | type: 'numeric', | |
32 | position: 'left', | |
33 | hidden: true, | |
f6710aac | 34 | minimum: 0, |
4226b8fb DC |
35 | }, |
36 | { | |
37 | type: 'numeric', | |
38 | position: 'bottom', | |
f6710aac TL |
39 | hidden: true, |
40 | }, | |
4226b8fb DC |
41 | ], |
42 | ||
43 | store: { | |
ca0267fd TL |
44 | trackRemoved: false, |
45 | data: {}, | |
4226b8fb DC |
46 | }, |
47 | ||
48 | sprites: [{ | |
49 | id: 'valueSprite', | |
50 | type: 'text', | |
51 | text: '0 B/s', | |
52 | textAlign: 'end', | |
53 | textBaseline: 'middle', | |
f6710aac | 54 | fontSize: 14, |
4226b8fb DC |
55 | }], |
56 | ||
57 | series: [{ | |
58 | type: 'line', | |
59 | xField: 'time', | |
60 | yField: 'val', | |
61 | fill: 'true', | |
62 | colors: ['#cfcfcf'], | |
63 | tooltip: { | |
64 | trackMouse: true, | |
8058410f | 65 | renderer: function(tooltip, record, ctx) { |
e75392ce | 66 | if (!record || !record.data) return; |
a9e70184 DJ |
67 | const view = this.getChart(); |
68 | const date = new Date(record.data.time); | |
69 | const value = view.up().renderer(record.data.val); | |
70 | const line1 = `${view.up().title}: ${value}`; | |
71 | const line2 = Ext.Date.format(date, 'H:i:s'); | |
72 | tooltip.setHtml(`${line1}<br />${line2}`); | |
f6710aac | 73 | }, |
4226b8fb DC |
74 | }, |
75 | style: { | |
76 | lineWidth: 1.5, | |
f6710aac | 77 | opacity: 0.60, |
4226b8fb DC |
78 | }, |
79 | marker: { | |
80 | opacity: 0, | |
81 | scaling: 0.01, | |
82 | fx: { | |
83 | duration: 200, | |
f6710aac TL |
84 | easing: 'easeOut', |
85 | }, | |
4226b8fb DC |
86 | }, |
87 | highlightCfg: { | |
88 | opacity: 1, | |
f6710aac TL |
89 | scaling: 1.5, |
90 | }, | |
91 | }], | |
92 | }, | |
4226b8fb DC |
93 | ], |
94 | ||
eb7f9197 | 95 | // the renderer for the tooltip and last value, default just the value |
4226b8fb DC |
96 | renderer: Ext.identityFn, |
97 | ||
eb7f9197 | 98 | // show the last x seconds default is 5 minutes |
4226b8fb DC |
99 | timeFrame: 5*60, |
100 | ||
101 | addDataPoint: function(value, time) { | |
a9e70184 DJ |
102 | let view = this.chart; |
103 | let panel = view.up(); | |
497c94f2 | 104 | let now = new Date().getTime(); |
53e3ea84 | 105 | let begin = new Date(now - 1000 * panel.timeFrame).getTime(); |
4226b8fb | 106 | |
a9e70184 | 107 | view.store.add({ |
497c94f2 | 108 | time: time || now, |
f6710aac | 109 | val: value || 0, |
4226b8fb DC |
110 | }); |
111 | ||
112 | // delete all old records when we have 20 times more datapoints | |
113 | // than seconds in our timeframe (so even a subsecond graph does | |
114 | // not trigger this often) | |
115 | // | |
116 | // records in the store do not take much space, but like this, | |
117 | // we prevent a memory leak when someone has the site open for a long time | |
118 | // with minimal graphical glitches | |
a9e70184 DJ |
119 | if (view.store.count() > panel.timeFrame * 20) { |
120 | var oldData = view.store.getData().createFiltered(function(item) { | |
497c94f2 | 121 | return item.data.time < begin; |
4226b8fb DC |
122 | }); |
123 | ||
a9e70184 | 124 | view.store.remove(oldData.getRange()); |
4226b8fb DC |
125 | } |
126 | ||
a9e70184 DJ |
127 | view.timeaxis.setMinimum(begin); |
128 | view.timeaxis.setMaximum(now); | |
129 | view.valuesprite.setText(panel.renderer(value || 0).toString()); | |
130 | view.valuesprite.setAttributes({ | |
131 | x: view.getWidth() - 15, | |
132 | y: view.getHeight()/2, | |
4226b8fb | 133 | }, true); |
a9e70184 | 134 | view.redraw(); |
4226b8fb DC |
135 | }, |
136 | ||
137 | setTitle: function(title) { | |
138 | this.title = title; | |
a9e70184 DJ |
139 | let titlebox = this.getComponent('title'); |
140 | titlebox.update({ title: title }); | |
4226b8fb DC |
141 | }, |
142 | ||
8058410f | 143 | initComponent: function() { |
4226b8fb DC |
144 | var me = this; |
145 | me.callParent(); | |
146 | ||
147 | if (me.title) { | |
8058410f | 148 | me.getComponent('title').update({ title: me.title }); |
4226b8fb DC |
149 | } |
150 | me.chart = me.getComponent('chart'); | |
151 | me.chart.timeaxis = me.chart.getAxes()[1]; | |
152 | me.chart.valuesprite = me.chart.getSurface('chart').get('valueSprite'); | |
153 | if (me.color) { | |
154 | me.chart.series[0].setStyle({ | |
155 | fill: me.color, | |
f6710aac | 156 | stroke: me.color, |
4226b8fb DC |
157 | }); |
158 | } | |
f6710aac | 159 | }, |
4226b8fb | 160 | }); |