]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard-area-chart/dashboard-area-chart.component.ts
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / dashboard-v3 / dashboard-area-chart / dashboard-area-chart.component.ts
1 import { AfterViewInit, Component, Input, OnChanges, ViewChild } from '@angular/core';
2
3 import { CssHelper } from '~/app/shared/classes/css-helper';
4 import { DimlessBinaryPipe } from '~/app/shared/pipes/dimless-binary.pipe';
5 import { DimlessBinaryPerSecondPipe } from '~/app/shared/pipes/dimless-binary-per-second.pipe';
6 import { FormatterService } from '~/app/shared/services/formatter.service';
7 import { BaseChartDirective, PluginServiceGlobalRegistrationAndOptions } from 'ng2-charts';
8 import { DimlessPipe } from '~/app/shared/pipes/dimless.pipe';
9 import { NumberFormatterService } from '~/app/shared/services/number-formatter.service';
10
11 @Component({
12 selector: 'cd-dashboard-area-chart',
13 templateUrl: './dashboard-area-chart.component.html',
14 styleUrls: ['./dashboard-area-chart.component.scss']
15 })
16 export class DashboardAreaChartComponent implements OnChanges, AfterViewInit {
17 @ViewChild(BaseChartDirective) chart: BaseChartDirective;
18
19 @Input()
20 chartTitle: string;
21 @Input()
22 maxValue?: number;
23 @Input()
24 dataUnits: string;
25 @Input()
26 data: Array<[number, string]>;
27 @Input()
28 data2?: Array<[number, string]>;
29 @Input()
30 label: string;
31 @Input()
32 label2?: string;
33
34 currentDataUnits: string;
35 currentData: number;
36 currentDataUnits2?: string;
37 currentData2?: number;
38
39 chartDataUnits: string;
40
41 chartData: any = {
42 dataset: [
43 {
44 label: '',
45 data: [{ x: 0, y: 0 }],
46 tension: 0,
47 pointBackgroundColor: this.cssHelper.propertyValue('chart-color-strong-blue'),
48 backgroundColor: this.cssHelper.propertyValue('chart-color-translucent-blue'),
49 borderColor: this.cssHelper.propertyValue('chart-color-strong-blue')
50 },
51 {
52 label: '',
53 data: [],
54 tension: 0,
55 pointBackgroundColor: this.cssHelper.propertyValue('chart-color-orange'),
56 backgroundColor: this.cssHelper.propertyValue('chart-color-yellow'),
57 borderColor: this.cssHelper.propertyValue('chart-color-orange')
58 }
59 ]
60 };
61
62 options: any = {
63 responsive: true,
64 maintainAspectRatio: false,
65 elements: {
66 point: {
67 radius: 0
68 }
69 },
70 legend: {
71 display: false
72 },
73 tooltips: {
74 intersect: false,
75 displayColors: true,
76 backgroundColor: this.cssHelper.propertyValue('chart-color-tooltip-background'),
77 callbacks: {
78 title: function (tooltipItem: any): any {
79 return tooltipItem[0].xLabel;
80 }
81 }
82 },
83 hover: {
84 intersect: false
85 },
86 scales: {
87 xAxes: [
88 {
89 display: false,
90 type: 'time',
91 gridLines: {
92 display: false
93 },
94 time: {
95 tooltipFormat: 'YYYY/MM/DD hh:mm:ss'
96 }
97 }
98 ],
99 yAxes: [
100 {
101 gridLines: {
102 display: false
103 },
104 ticks: {
105 beginAtZero: true,
106 maxTicksLimit: 3,
107 callback: (value: any) => {
108 if (value === 0) {
109 return null;
110 }
111 return this.fillString(this.convertUnits(value));
112 }
113 }
114 }
115 ]
116 },
117 plugins: {
118 borderArea: true,
119 chartAreaBorder: {
120 borderColor: this.cssHelper.propertyValue('chart-color-slight-dark-gray'),
121 borderWidth: 2
122 }
123 }
124 };
125
126 public chartAreaBorderPlugin: PluginServiceGlobalRegistrationAndOptions[] = [
127 {
128 beforeDraw(chart: Chart) {
129 if (!chart.options.plugins.borderArea) {
130 return;
131 }
132 const {
133 ctx,
134 chartArea: { left, top, right, bottom }
135 } = chart;
136 ctx.save();
137 ctx.strokeStyle = chart.options.plugins.chartAreaBorder.borderColor;
138 ctx.lineWidth = chart.options.plugins.chartAreaBorder.borderWidth;
139 ctx.setLineDash(chart.options.plugins.chartAreaBorder.borderDash || []);
140 ctx.lineDashOffset = chart.options.plugins.chartAreaBorder.borderDashOffset;
141 ctx.strokeRect(left, top, right - left - 1, bottom);
142 ctx.restore();
143 }
144 }
145 ];
146
147 constructor(
148 private cssHelper: CssHelper,
149 private dimlessBinary: DimlessBinaryPipe,
150 private dimlessBinaryPerSecond: DimlessBinaryPerSecondPipe,
151 private dimlessPipe: DimlessPipe,
152 private formatter: FormatterService,
153 private numberFormatter: NumberFormatterService
154 ) {}
155
156 ngOnChanges(): void {
157 this.updateChartData();
158 }
159
160 ngAfterViewInit(): void {
161 this.updateChartData();
162 }
163
164 private updateChartData(): void {
165 if (this.data) {
166 this.setChartTicks();
167 this.chartData.dataset[0].data = this.formatData(this.data);
168 this.chartData.dataset[0].label = this.label;
169 [this.currentData, this.currentDataUnits] = this.convertUnits(
170 this.data[this.data.length - 1][1]
171 ).split(' ');
172 }
173 if (this.data2) {
174 this.chartData.dataset[1].data = this.formatData(this.data2);
175 this.chartData.dataset[1].label = this.label2;
176 [this.currentData2, this.currentDataUnits2] = this.convertUnits(
177 this.data2[this.data2.length - 1][1]
178 ).split(' ');
179 }
180 if (this.chart) {
181 this.chart.chart.update();
182 }
183 }
184
185 private formatData(array: Array<any>): any {
186 let formattedData = {};
187 formattedData = array.map((data: any) => ({
188 x: data[0] * 1000,
189 y: Number(this.convertToChartDataUnits(data[1]).replace(/[^\d,.]+/g, ''))
190 }));
191 return formattedData;
192 }
193
194 private convertToChartDataUnits(data: any): any {
195 let dataWithUnits: string = '';
196 if (this.chartDataUnits) {
197 if (this.dataUnits === 'B') {
198 dataWithUnits = this.numberFormatter.formatBytesFromTo(
199 data,
200 this.dataUnits,
201 this.chartDataUnits
202 );
203 } else if (this.dataUnits === 'B/s') {
204 dataWithUnits = this.numberFormatter.formatBytesPerSecondFromTo(
205 data,
206 this.dataUnits,
207 this.chartDataUnits
208 );
209 } else if (this.dataUnits === 'ms') {
210 dataWithUnits = this.numberFormatter.formatSecondsFromTo(
211 data,
212 this.dataUnits,
213 this.chartDataUnits
214 );
215 } else {
216 dataWithUnits = this.numberFormatter.formatUnitlessFromTo(
217 data,
218 this.dataUnits,
219 this.chartDataUnits
220 );
221 }
222 }
223 return dataWithUnits;
224 }
225
226 private convertUnits(data: any): any {
227 let dataWithUnits: string = '';
228 if (this.dataUnits === 'B') {
229 dataWithUnits = this.dimlessBinary.transform(data);
230 } else if (this.dataUnits === 'B/s') {
231 dataWithUnits = this.dimlessBinaryPerSecond.transform(data);
232 } else if (this.dataUnits === 'ms') {
233 dataWithUnits = this.formatter.format_number(data, 1000, ['ms', 's']);
234 } else {
235 dataWithUnits = this.dimlessPipe.transform(data);
236 }
237 return dataWithUnits;
238 }
239
240 private fillString(str: string): string {
241 let maxNumberOfChar: number = 8;
242 let numberOfChars: number = str.length;
243 if (str.length < 4) {
244 maxNumberOfChar = 11;
245 }
246 for (; numberOfChars < maxNumberOfChar; numberOfChars++) {
247 str = '\u00A0' + str;
248 }
249 return str + '\u00A0\u00A0';
250 }
251
252 private setChartTicks() {
253 if (!this.chart) {
254 return;
255 }
256
257 let maxValue = 0;
258 let maxValueDataUnits = '';
259 let extraRoom = 1.2;
260
261 if (this.maxValue) {
262 extraRoom = 1.0;
263 [maxValue, maxValueDataUnits] = this.convertUnits(this.maxValue).split(' ');
264 } else if (this.data) {
265 extraRoom = 1.2;
266 let maxValueData = Math.max(...this.data.map((values: any) => values[1]));
267 if (this.data2) {
268 let maxValueData2 = Math.max(...this.data2.map((values: any) => values[1]));
269 maxValue = Math.max(maxValueData, maxValueData2);
270 } else {
271 maxValue = maxValueData;
272 }
273 [maxValue, maxValueDataUnits] = this.convertUnits(maxValue).split(' ');
274 }
275
276 const yAxesTicks = this.chart.chart.options.scales.yAxes[0].ticks;
277 yAxesTicks.suggestedMax = maxValue * extraRoom;
278 yAxesTicks.suggestedMin = 0;
279 yAxesTicks.stepSize = Number((yAxesTicks.suggestedMax / 2).toFixed(0));
280 yAxesTicks.callback = (value: any) => {
281 if (value === 0) {
282 return null;
283 }
284 if (!maxValueDataUnits) {
285 return this.fillString(`${value}`);
286 }
287 return this.fillString(`${value} ${maxValueDataUnits}`);
288 };
289 this.chartDataUnits = maxValueDataUnits || '';
290 this.chart.chart.update();
291 }
292 }