Add error handling to improve user experience
[portal/ric-dashboard.git] / webapp-frontend / src / app / stats / stats.component.ts
1 /*-
2  * ========================LICENSE_START=================================
3  * O-RAN-SC
4  * %%
5  * Copyright (C) 2019 AT&T Intellectual Property and Nokia
6  * %%
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ========================LICENSE_END===================================
19  */
20 import { Component, OnInit, ViewChildren, QueryList } from '@angular/core';
21 import { BaseChartDirective } from 'ng2-charts/ng2-charts';
22 import { StatsService } from '../services/stats/stats.service';
23 import { HttpClient } from '@angular/common/http';
24 import { map } from 'rxjs/operators';
25
26 @Component({
27     selector: 'rd-stats',
28     templateUrl: './stats.component.html',
29     styleUrls: ['./stats.component.scss']
30 })
31 export class StatsComponent implements OnInit {
32
33     @ViewChildren(BaseChartDirective) charts: QueryList<BaseChartDirective>;
34     timeLeft = 60;
35     interval;
36     checked = false;
37     load;
38     delay;
39
40     public latencyChartColors: Array<any> = [
41         { // blue
42             backgroundColor: 'rgba(197, 239, 247, 0.2)',
43             borderColor: 'lightblue',
44             pointBackgroundColor: 'lightblue',
45             pointBorderColor: '#fff',
46             pointHoverBackgroundColor: '#fff',
47             pointHoverBorderColor: 'rgba(148,159,177,0.8)'
48         }
49     ];
50     public latencyChartOptions = {
51         scaleShowVerticalLines: true,
52         responsive: true,
53         animation: {
54             duration: 800 * 1.5,
55             easing: 'linear'
56         },
57         hover: {
58             animationDuration: 1 // duration of animations when hovering an item
59         },
60         responsiveAnimationDuration: 500,
61         scales: {
62             yAxes: [{
63                 ticks: {
64                     // the data minimum used for determining the ticks is Math.min(dataMin, suggestedMin)
65                     suggestedMin: 0,
66                     // the data maximum used for determining the ticks is Math.max(dataMax, suggestedMax)
67                     //                    suggestedMax: 1000
68                 },
69                 scaleLabel: {
70                     display: true,
71                     labelString: 'msecs'
72                 }
73             }],
74             xAxes: [{
75                 scaleLabel: {
76                     display: true,
77                     labelString: 'time (last 10 seconds)'
78                 }
79             }]
80         },
81     };
82     public latencyChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
83     public latencyChartType = 'line';
84     public latencyChartLegend = true;
85     public latencyChartData = [
86         { data: [65, 59, 80, 81, 56, 55, 40, 20, 12, 34], label: 'Latency' },
87     ];
88
89     public loadChartColors: Array<any> = [
90
91         { // green
92             backgroundColor: 'rgba(200, 247, 197, 0.2)',
93             borderColor: 'lightgreen',
94             pointBackgroundColor: 'lightgreen',
95             pointBorderColor: '#fff',
96             pointHoverBackgroundColor: '#fff',
97             pointHoverBorderColor: 'rgba(0,200,0,0.5)'
98         }
99     ];
100     public loadChartOptions = {
101         scaleShowVerticalLines: false,
102         responsive: true,
103         animation: {
104             duration: 800 * 1.5,
105             easing: 'linear'
106         },
107         hover: {
108             animationDuration: 1 // duration of animations when hovering an item
109         },
110         responsiveAnimationDuration: 500,
111         scales: {
112             yAxes: [{
113                 ticks: {
114                     // the data minimum used for determining the ticks is Math.min(dataMin, suggestedMin)
115                     suggestedMin: 0,
116                     // the data maximum used for determining the ticks is Math.max(dataMax, suggestedMax)
117                     //                    suggestedMax: 1000
118                 },
119                 scaleLabel: {
120                     display: true,
121                     labelString: '# of requests'
122                 }
123             }],
124             xAxes: [{
125                 scaleLabel: {
126                     display: true,
127                     labelString: 'time (last 10 seconds)'
128                 }
129             }]
130         },
131     };
132     public loadChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
133     public loadChartType = 'line';
134     public loadChartLegend = true;
135     public loadChartData = [
136         { data: [28, 48, 40, 19, 86, 77, 90, 20, 12, 34], label: 'Load' }
137     ];
138
139     public cpuChartColors: Array<any> = [
140
141         { // red
142             backgroundColor: 'rgba(241, 169, 160, 0.2)',
143             borderColor: 'brown',
144             pointBackgroundColor: 'brown',
145             pointBorderColor: '#fff',
146             pointHoverBackgroundColor: '#fff',
147             pointHoverBorderColor: 'rgba(0,200,0,0.5)'
148         }
149     ];
150     public cpuChartOptions = {
151         scaleShowVerticalLines: false,
152         responsive: true,
153         animation: {
154             duration: 800 * 1.5,
155             easing: 'linear'
156         },
157         hover: {
158             animationDuration: 1 // duration of animations when hovering an item
159         },
160         responsiveAnimationDuration: 500,
161         scales: {
162             yAxes: [{
163                 ticks: {
164                     // the data minimum used for determining the ticks is Math.min(dataMin, suggestedMin)
165                     suggestedMin: 0,
166                     // the data maximum used for determining the ticks is Math.max(dataMax, suggestedMax)
167                     //                    suggestedMax: 1000
168                 },
169                 scaleLabel: {
170                     display: true,
171                     labelString: '# of requests'
172                 }
173             }],
174             xAxes: [{
175                 scaleLabel: {
176                     display: true,
177                     labelString: 'time (last 10 seconds)'
178                 }
179             }]
180         },
181     };
182     public cpuChartLabels = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10'];
183     public cpuChartType = 'line';
184     public cpuChartLegend = true;
185     public cpuChartData = [
186         { data: [15, 29, 30, 31, 53, 52, 41, 70, 32, 14], label: 'RICLoad' }
187     ];
188
189     public x = 11;
190     public y = 11;
191     public z = 11;
192     public loop = true;
193
194     public sliderLoadMax = Number(this.service.loadMax) || 0;
195
196     public sliderDelayMax = Number(this.service.delayMax) || 0;
197
198     latencyClickData() {
199         // this.latencyChartData = [{data: [Math.random() * 100, Math.random() * 100, Math.random() * 100,
200         // Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100,
201         // Math.random() * 100, Math.random() * 100], label: 'Latency'}];
202         this.charts.forEach((child) => {
203             if (child.datasets[0].label === 'Latency') {
204                 this.latencyChartLabels.shift();
205                 child.datasets[0].data.shift();
206
207                 const latencyData = this.service.getLatencyMetrics();
208                 child.datasets[0].data.push(latencyData);
209                 this.latencyChartLabels.push('' + this.x++);
210             }
211             // once new data is computed and datasets are updated, tell our baseChart the datasets changed
212             child.ngOnChanges({
213                 datasets: {
214                     currentValue: child.datasets,
215                     previousValue: null,
216                     firstChange: true,
217                     isFirstChange: () => true
218                 }
219             });
220         });
221     }
222
223     loadClickData() {
224         if (this.loop) {
225             this.loop = false;
226             this.startLoadTimer();
227         } else {
228             this.loop = true;
229             this.pauseLoadTimer();
230         }
231     }
232
233     loopLoadData(metricsv: any) {
234         this.charts.forEach((child) => {
235             if (child.datasets[0].label === 'Load') {
236                 this.loadChartLabels.shift();
237                 child.datasets[0].data.shift();
238
239                 // const loadData = this.service.getLoad();
240                 // child.datasets[0].data.push(this.service.load);
241                 child.datasets[0].data.push(metricsv['load']);
242                 this.loadChartLabels.push('' + this.x++);
243             }
244             if (child.datasets[0].label === 'Latency') {
245                 this.latencyChartLabels.shift();
246                 child.datasets[0].data.shift();
247
248                 // const loadData = this.service.getLoad();
249                 // child.datasets[0].data.push(this.service.load);
250                 child.datasets[0].data.push(metricsv['latency']);
251                 this.latencyChartLabels.push('' + this.x++);
252             }
253             if (child.datasets[0].label === 'RICLoad') {
254                 this.latencyChartLabels.shift();
255                 child.datasets[0].data.shift();
256
257                 // const loadData = this.service.getLoad();
258                 // child.datasets[0].data.push(this.service.load);
259                 child.datasets[0].data.push(metricsv['ricload']);
260                 this.latencyChartLabels.push('' + this.x++);
261             }
262             // once new data is computed and datasets are updated, tell our baseChart the datasets changed
263             child.ngOnChanges({
264                 datasets: {
265                     currentValue: child.datasets,
266                     previousValue: null,
267                     firstChange: true,
268                     isFirstChange: () => true
269                 }
270             });
271         });
272     }
273
274     cpuClickData() {
275         // this.cpuChartData = [{data: [Math.random() * 100, Math.random() * 100, Math.random() * 100,
276         // Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100, Math.random() * 100,
277         // Math.random() * 100, Math.random() * 100], label: 'CPU'}];
278         const cpuData = this.service.getLatencyMetrics();
279         this.newDataPoint([cpuData], this.z++);
280     }
281
282     newDataPoint(dataArr = [100], label) {
283
284         this.cpuChartData.forEach((dataset, index) => {
285             this.cpuChartData[index] = Object.assign({}, this.cpuChartData[index], {
286                 data: [...this.cpuChartData[index].data, dataArr[index]]
287             });
288         });
289
290         this.cpuChartLabels = [...this.cpuChartLabels, label];
291         // console.log(this.cpuChartLabels);
292         // console.log(this.cpuChartData);
293     }
294
295     formatLabel(value: number | null) {
296         if (!value) {
297             return 0;
298         }
299
300         if (value >= 1000) {
301             return Math.round(value / 1000);
302         }
303
304         return value;
305     }
306
307     constructor(private service: StatsService, private httpClient: HttpClient) {
308         this.sliderLoadMax = Number(this.service.loadMax) || 0;
309         this.sliderDelayMax = Number(this.service.delayMax) || 0;
310         // console.log('this.sliderLoadMax: ' + this.sliderLoadMax);
311         // console.log('this.sliderDelayMax: ' + this.sliderDelayMax);
312     }
313     ngOnInit() {
314         this.fetchLoad().subscribe(loadv => {
315             // console.log('loadv: ' + loadv);
316             this.checked = loadv;
317         });
318         this.fetchDelay().subscribe(delayv => {
319             // console.log('delayv: ' + delayv);
320             this.delay = delayv;
321         });
322         this.fetchMetrics().subscribe(metricsv => {
323             // console.log('metricsv.load: ' + metricsv['load']);
324         });
325     }
326
327     startLoadTimer() {
328         this.interval = setInterval(() => {
329             if (this.timeLeft > 0) {
330                 this.timeLeft--;
331                 this.fetchMetrics().subscribe(metricsv => {
332                     // console.log('metricsv.load: ' + metricsv['latency']);
333                     // console.log('metricsv.load: ' + metricsv['load']);
334                     // console.log('metricsv.load: ' + metricsv['ricload']);
335                     this.loopLoadData(metricsv);
336                 });
337
338             } else {
339                 this.timeLeft = 60;
340             }
341         }, 1000);
342     }
343
344     pauseLoadTimer() {
345         clearInterval(this.interval);
346     }
347
348     fetchMetrics() {
349         return this.httpClient.get<any[]>(this.service.hostURL + this.service.metricsPath, this.service.httpOptions).pipe(map(res => {
350             // console.log(res);
351             // console.log(res['load']);
352             return res;
353         }));
354     }
355
356     fetchDelay() {
357         return this.httpClient.get<any[]>(this.service.hostURL + this.service.delayPath, this.service.httpOptions).pipe(map(res => {
358             // console.log(res);
359             // console.log(res['delay']);
360             const delayv = res['delay'];
361             // console.log(delayv);
362             this.delay = delayv;
363             return this.delay;
364         }));
365     }
366
367     saveDelay() {
368         // console.log(this.delay);
369         this.service.putDelay(this.delay);
370     }
371
372     fetchLoad() {
373         return this.httpClient.get<any[]>(this.service.hostURL + this.service.loadPath, this.service.httpOptions).pipe(map(res => {
374             // console.log(res);
375             // console.log(res['load']);
376             const loadv = res['load'];
377             // console.log(loadv);
378             this.load = loadv;
379             return this.load;
380         }));
381
382     }
383
384     saveLoad() {
385         // console.log(this.load);
386         this.service.putLoad(this.load);
387     }
388
389 }