]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { Component, ElementRef, Input, OnChanges, OnInit, ViewChild } from '@angular/core'; |
2 | ||
eafe8130 | 3 | import { ChartDataSets, ChartOptions, ChartPoint, ChartType } from 'chart.js'; |
f67539c2 TL |
4 | import _ from 'lodash'; |
5 | import moment from 'moment'; | |
11fdf7f2 | 6 | |
f67539c2 | 7 | import { ChartTooltip } from '~/app/shared/models/chart-tooltip'; |
11fdf7f2 TL |
8 | |
9 | @Component({ | |
10 | selector: 'cd-cephfs-chart', | |
11 | templateUrl: './cephfs-chart.component.html', | |
12 | styleUrls: ['./cephfs-chart.component.scss'] | |
13 | }) | |
14 | export class CephfsChartComponent implements OnChanges, OnInit { | |
9f95a23c | 15 | @ViewChild('chartCanvas', { static: true }) |
11fdf7f2 | 16 | chartCanvas: ElementRef; |
9f95a23c | 17 | @ViewChild('chartTooltip', { static: true }) |
11fdf7f2 TL |
18 | chartTooltip: ElementRef; |
19 | ||
20 | @Input() | |
21 | mdsCounter: any; | |
22 | ||
494da23a | 23 | lhsCounter = 'mds_mem.ino'; |
11fdf7f2 TL |
24 | rhsCounter = 'mds_server.handle_client_request'; |
25 | ||
eafe8130 TL |
26 | chart: { |
27 | datasets: ChartDataSets[]; | |
28 | options: ChartOptions; | |
29 | chartType: ChartType; | |
30 | } = { | |
31 | datasets: [ | |
32 | { | |
33 | label: this.lhsCounter, | |
34 | yAxisID: 'LHS', | |
35 | data: [], | |
36 | lineTension: 0.1 | |
37 | }, | |
38 | { | |
39 | label: this.rhsCounter, | |
40 | yAxisID: 'RHS', | |
41 | data: [], | |
42 | lineTension: 0.1 | |
43 | } | |
44 | ], | |
45 | options: { | |
46 | title: { | |
47 | text: '', | |
48 | display: true | |
49 | }, | |
50 | responsive: true, | |
51 | maintainAspectRatio: false, | |
52 | legend: { | |
53 | position: 'top' | |
54 | }, | |
55 | scales: { | |
56 | xAxes: [ | |
57 | { | |
58 | position: 'top', | |
59 | type: 'time', | |
60 | time: { | |
61 | displayFormats: { | |
62 | quarter: 'MMM YYYY' | |
63 | } | |
64 | }, | |
65 | ticks: { | |
66 | maxRotation: 0 | |
67 | } | |
68 | } | |
69 | ], | |
70 | yAxes: [ | |
71 | { | |
72 | id: 'LHS', | |
73 | type: 'linear', | |
74 | position: 'left' | |
75 | }, | |
76 | { | |
77 | id: 'RHS', | |
78 | type: 'linear', | |
79 | position: 'right' | |
80 | } | |
81 | ] | |
82 | }, | |
83 | tooltips: { | |
84 | enabled: false, | |
85 | mode: 'index', | |
86 | intersect: false, | |
87 | position: 'nearest', | |
88 | callbacks: { | |
89 | // Pick the Unix timestamp of the first tooltip item. | |
90 | title: (tooltipItems, data): string => { | |
91 | let ts = 0; | |
92 | if (tooltipItems.length > 0) { | |
93 | const item = tooltipItems[0]; | |
94 | const point = data.datasets[item.datasetIndex].data[item.index] as ChartPoint; | |
95 | ts = point.x as number; | |
96 | } | |
97 | return ts.toString(); | |
98 | } | |
99 | } | |
100 | } | |
101 | }, | |
102 | chartType: 'line' | |
103 | }; | |
11fdf7f2 | 104 | |
11fdf7f2 TL |
105 | ngOnInit() { |
106 | if (_.isUndefined(this.mdsCounter)) { | |
107 | return; | |
108 | } | |
eafe8130 TL |
109 | this.setChartTooltip(); |
110 | this.updateChart(); | |
111 | } | |
11fdf7f2 | 112 | |
eafe8130 TL |
113 | ngOnChanges() { |
114 | if (_.isUndefined(this.mdsCounter)) { | |
115 | return; | |
116 | } | |
117 | this.updateChart(); | |
118 | } | |
11fdf7f2 | 119 | |
eafe8130 | 120 | private setChartTooltip() { |
11fdf7f2 TL |
121 | const chartTooltip = new ChartTooltip( |
122 | this.chartCanvas, | |
123 | this.chartTooltip, | |
9f95a23c TL |
124 | (tooltip: any) => tooltip.caretX + 'px', |
125 | (tooltip: any) => tooltip.caretY - tooltip.height - 23 + 'px' | |
11fdf7f2 | 126 | ); |
eafe8130 | 127 | chartTooltip.getTitle = (ts) => moment(ts, 'x').format('LTS'); |
11fdf7f2 | 128 | chartTooltip.checkOffset = true; |
eafe8130 TL |
129 | const chartOptions: ChartOptions = { |
130 | title: { | |
131 | text: this.mdsCounter.name | |
11fdf7f2 | 132 | }, |
eafe8130 TL |
133 | tooltips: { |
134 | custom: (tooltip) => chartTooltip.customTooltips(tooltip) | |
135 | } | |
11fdf7f2 | 136 | }; |
eafe8130 | 137 | _.merge(this.chart, { options: chartOptions }); |
11fdf7f2 TL |
138 | } |
139 | ||
eafe8130 TL |
140 | private updateChart() { |
141 | const chartDataSets: ChartDataSets[] = [ | |
142 | { | |
143 | data: this.convertTimeSeries(this.mdsCounter[this.lhsCounter]) | |
144 | }, | |
145 | { | |
146 | data: this.deltaTimeSeries(this.mdsCounter[this.rhsCounter]) | |
147 | } | |
148 | ]; | |
149 | _.merge(this.chart, { | |
150 | datasets: chartDataSets | |
151 | }); | |
152 | this.chart.datasets = [...this.chart.datasets]; // Force angular to update | |
11fdf7f2 TL |
153 | } |
154 | ||
eafe8130 TL |
155 | /** |
156 | * Convert ceph-mgr's time series format (list of 2-tuples | |
157 | * with seconds-since-epoch timestamps) into what chart.js | |
158 | * can handle (list of objects with millisecs-since-epoch | |
159 | * timestamps) | |
160 | */ | |
9f95a23c TL |
161 | private convertTimeSeries(sourceSeries: any) { |
162 | const data: any[] = []; | |
11fdf7f2 TL |
163 | _.each(sourceSeries, (dp) => { |
164 | data.push({ | |
165 | x: dp[0] * 1000, | |
166 | y: dp[1] | |
167 | }); | |
168 | }); | |
169 | ||
eafe8130 TL |
170 | /** |
171 | * MDS performance counters chart is expecting the same number of items | |
172 | * from each data series. Since in deltaTimeSeries we are ignoring the first | |
173 | * element, we will do the same here. | |
174 | */ | |
175 | data.shift(); | |
176 | ||
11fdf7f2 TL |
177 | return data; |
178 | } | |
179 | ||
9f95a23c | 180 | private deltaTimeSeries(sourceSeries: any) { |
11fdf7f2 TL |
181 | let i; |
182 | let prev = sourceSeries[0]; | |
183 | const result = []; | |
184 | for (i = 1; i < sourceSeries.length; i++) { | |
185 | const cur = sourceSeries[i]; | |
11fdf7f2 TL |
186 | |
187 | result.push({ | |
188 | x: cur[0] * 1000, | |
9f95a23c | 189 | y: cur[1] - prev[1] |
11fdf7f2 TL |
190 | }); |
191 | ||
192 | prev = cur; | |
193 | } | |
194 | return result; | |
195 | } | |
196 | } |