mirror of
https://github.com/apache/cloudstack.git
synced 2025-12-15 18:12:35 +01:00
240 lines
7.4 KiB
Vue
240 lines
7.4 KiB
Vue
// 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: 'origin'
|
|
}
|
|
)
|
|
}
|
|
return {
|
|
labels: this.chartLabels,
|
|
datasets: datasetList
|
|
}
|
|
},
|
|
hideLine (data) {
|
|
for (const d of data) {
|
|
if (d < 0) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
</script>
|