| // Licensed to the Apache Software Foundation (ASF) under one | |
| // or more contributor license agreements. See the NOTICE file | |
| // distributed with this work for additional information | |
| // regarding copyright ownership. The ASF licenses this file | |
| // to you under the Apache License, Version 2.0 (the | |
| // "License"); you may not use this file except in compliance | |
| // with the License. You may obtain a copy of the License at | |
| // | |
| // http://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, | |
| // software distributed under the License is distributed on an | |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| // KIND, either express or implied. See the License for the | |
| // specific language governing permissions and limitations | |
| // under the License. | |
| <template> | |
| <Line | |
| :chart-options="preparedOptions" | |
| :chart-data="preparedData" | |
| :width="chartWidth" | |
| :height="chartHeight" | |
| /> | |
| </template> | |
| <script> | |
| import { Line } from 'vue-chartjs' | |
| import { Chart as ChartJS, Title, Tooltip, Legend, LineElement, CategoryScale, TimeScale, LinearScale, PointElement, Filler } from 'chart.js' | |
| ChartJS.register(Title, Tooltip, Legend, LineElement, CategoryScale, TimeScale, LinearScale, PointElement, Filler) | |
| export default { | |
| name: 'ResourceStatsLineChart', | |
| components: { Line }, | |
| props: { | |
| chartData: { | |
| type: Object, | |
| required: true | |
| }, | |
| chartLabels: { | |
| type: Array, | |
| required: true | |
| }, | |
| yAxisMeasurementUnit: { | |
| type: String, | |
| required: true | |
| }, | |
| yAxisInitialMax: { | |
| type: Number, | |
| default: 1 | |
| }, | |
| yAxisIncrementValue: { | |
| type: Number, | |
| default: 1 | |
| }, | |
| chartWidth: { | |
| type: Number, | |
| default: 1024 | |
| }, | |
| chartHeight: { | |
| type: Number, | |
| default: 250 | |
| } | |
| }, | |
| computed: { | |
| preparedData () { | |
| if (this.chartData) { | |
| return this.prepareData(this.chartData) | |
| } | |
| return {} | |
| }, | |
| preparedOptions () { | |
| if (this.chartData) { | |
| return this.getChartOptions(this.calculateMaxYAxisAndStepSize(this.chartData, this.yAxisInitialMax, this.yAxisIncrementValue), this.yAxisMeasurementUnit) | |
| } | |
| return {} | |
| } | |
| }, | |
| methods: { | |
| /** | |
| * Converts a value (Byte-based) from an unit to other one. For example: from Byte to KiB; from GiB to MiB; etc. | |
| * To use it consider the following sequence: Byte -> KiB -> MiB -> GiB ... | |
| * So, from Byte to MiB there are 2 steps, while from MiB to Byte there are -2 steps. | |
| * @param value the value to be converted. | |
| * @param step the number of steps between Byte-based units of measure. | |
| * @returns the converted value. | |
| */ | |
| convertByteBasedUnitOfMeasure (value, step) { | |
| if (value === 0) { | |
| return 0.00 | |
| } | |
| if (step === 0) { | |
| return value | |
| } | |
| if (step > 0) { | |
| return parseFloat(value / (Math.pow(1024, step))).toFixed(2) | |
| } | |
| return parseFloat(value * (Math.pow(1024, Math.abs(step)))).toFixed(2) | |
| }, | |
| calculateMaxYAxisAndStepSize (chartLines, initialMaxYAxis, incrementValue) { | |
| const numberOfLabelsOnYaxis = 4 | |
| var highestValue = 0 | |
| var maxYAxis = initialMaxYAxis | |
| for (const line of chartLines) { | |
| for (const d of line.data) { | |
| const currentValue = parseFloat(d.stat) | |
| if (currentValue > highestValue) { | |
| highestValue = currentValue | |
| while (highestValue > maxYAxis) { | |
| maxYAxis += incrementValue | |
| if (maxYAxis % incrementValue !== 0) { | |
| maxYAxis = Math.round(maxYAxis / incrementValue) * incrementValue | |
| } | |
| } | |
| } | |
| } | |
| } | |
| return { maxYAxes: maxYAxis, stepSize: maxYAxis / numberOfLabelsOnYaxis } | |
| }, | |
| /** | |
| * Returns the chart options. | |
| * @param yAxesStepSize the step size for the Y axes. | |
| * @param yAxesUnitOfMeasurement the unit of measurement label used on the Y axes. | |
| * @returns the chart options. | |
| */ | |
| getChartOptions (yAxesOptions, yAxesUnitOfMeasurement) { | |
| const dateTimes = this.convertStringArrayToDateArray(JSON.parse(JSON.stringify(this.chartLabels))) | |
| const averageDifference = this.averageDifferenceBetweenTimes(dateTimes) | |
| const xAxisStepSize = this.calculateStepSize(this.chartLabels.length, averageDifference) | |
| const startDate = new Date(dateTimes[0]) | |
| const endDate = new Date(dateTimes[dateTimes.length - 1]) | |
| const differentDay = startDate.getDate() !== endDate.getDate() | |
| const differentYear = startDate.getFullYear() !== endDate.getFullYear() | |
| var displayFormat = 'HH:mm' | |
| if (xAxisStepSize < 5 * 60) { | |
| displayFormat += ':ss' | |
| } | |
| if (differentDay) { | |
| displayFormat = 'MMM-DD ' + displayFormat | |
| } | |
| if (xAxisStepSize >= 24 * 60 * 60) { | |
| displayFormat = 'MMM-DD' | |
| } | |
| if (differentYear) { | |
| displayFormat = 'YYYY-' + displayFormat | |
| } | |
| var chartOptions = { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| scales: { | |
| yAxis: { | |
| min: 0, | |
| max: yAxesOptions.maxYAxes, | |
| reverse: false, | |
| ticks: { | |
| stepSize: yAxesOptions.stepSize, | |
| callback: function (label) { | |
| return label + yAxesUnitOfMeasurement | |
| } | |
| } | |
| }, | |
| xAxis: { | |
| type: 'time', | |
| autoSkip: false, | |
| time: { | |
| parser: 'YYYY-MM-DD HH:mm:ss', | |
| unit: 'second', | |
| displayFormats: { | |
| second: displayFormat | |
| } | |
| } | |
| } | |
| } | |
| } | |
| chartOptions.scales.xAxis.time.stepSize = xAxisStepSize | |
| return chartOptions | |
| }, | |
| convertStringArrayToDateArray (stringArray) { | |
| const dateArray = [] | |
| for (const element of stringArray) { | |
| dateArray.push(new Date(element.replace(' ', 'T'))) | |
| } | |
| return dateArray | |
| }, | |
| averageDifferenceBetweenTimes (timeList) { | |
| const oneSecond = 1000 // 1 second represented as milliseconds | |
| const differences = [] | |
| var previous = timeList.splice(0, 1)[0] | |
| for (const time of timeList) { | |
| differences.push((time - previous) / oneSecond) // push the difference in seconds | |
| previous = time | |
| } | |
| if (differences.length === 0) { | |
| return 1 | |
| } | |
| const averageDifference = Math.trunc(differences.reduce((a, b) => a + b, 0) / differences.length) | |
| return averageDifference | |
| }, | |
| calculateStepSize (numberOfDataPoints, differenceBetweenTimes) { | |
| const idealNumberOfLabels = 8 | |
| const result = numberOfDataPoints / idealNumberOfLabels | |
| if (result > 1) { | |
| return result * differenceBetweenTimes | |
| } | |
| return differenceBetweenTimes | |
| }, | |
| prepareData (chartData) { | |
| const datasetList = [] | |
| for (const element of chartData) { | |
| datasetList.push( | |
| { | |
| backgroundColor: element.backgroundColor, | |
| borderColor: element.borderColor, | |
| borderWidth: 3, | |
| label: element.label, | |
| data: element.data.map(d => d.stat), | |
| hidden: this.hideLine(element.data.map(d => d.stat)), | |
| pointRadius: element.pointRadius, | |
| fill: element.fill || 'origin' | |
| } | |
| ) | |
| } | |
| return { | |
| labels: this.chartLabels, | |
| datasets: datasetList | |
| } | |
| }, | |
| hideLine (data) { | |
| for (const d of data) { | |
| if (d < 0) { | |
| return true | |
| } | |
| } | |
| return false | |
| } | |
| } | |
| } | |
| </script> |