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