graphs: switch to time type graphs

the default one is bound on data. when data is not available, it appears
buggy. it's better to show no data and always show the correct time line
in the x-axis
This commit is contained in:
Girish Ramakrishnan
2025-05-22 18:10:46 +02:00
parent e028eadf60
commit 2d3c1b7702
3 changed files with 56 additions and 39 deletions
+35 -39
View File
@@ -11,10 +11,13 @@ import { SingleSelect, Spinner } from 'pankow';
import Section from './Section.vue';
import SystemModel from '../models/SystemModel.js';
import 'chartjs-adapter-moment'; // https://www.chartjs.org/docs/latest/axes/cartesian/time.html#date-adapters
const systemModel = SystemModel.create();
function trKeyFromPeriod(period) {
if (period === 0) return 'app.graphs.period.live';
if (period === 1) return 'app.graphs.period.1h';
if (period === 6) return 'app.graphs.period.6h';
if (period === 12) return 'app.graphs.period.12h';
if (period === 24) return 'app.graphs.period.24h';
@@ -26,6 +29,7 @@ function trKeyFromPeriod(period) {
const periods = [
{ id: 0, label: t(trKeyFromPeriod(0)) },
{ id: 1, label: t(trKeyFromPeriod(1)) },
{ id: 6, label: t(trKeyFromPeriod(6)) },
{ id: 12, label: t(trKeyFromPeriod(12)) },
{ id: 24, label: t(trKeyFromPeriod(24)) },
@@ -34,7 +38,7 @@ const periods = [
];
const busy = ref(false);
const period = ref(0);
const period = ref(24*7);
const cpuGraphNode = useTemplateRef('cpuGraphNode');
const memoryGraphNode = useTemplateRef('memoryGraphNode');
@@ -70,11 +74,8 @@ async function refresh() {
const [error, result] = await systemModel.getMetrics({ fromSecs: (period.value || 0.1) * 60 * 60, intervalSecs: 300 });
if (error) return console.error(error);
// cpu
const cpuLabels = result.cpu.map(v => {
return moment(v[1]*1000).format('hh:mm');
});
const now = Date.now();
const cpuLabels = result.cpu.map(v => v[1]*1000); // convert to msecs
const cpuData = result.cpu.map(v => v[0]); // already scaled to cpu*100
const cpuGraphData = {
@@ -97,23 +98,25 @@ async function refresh() {
},
scales: {
x: {
type: 'time',
bounds: 'ticks', // otherwise data bound. https://www.chartjs.org/docs/latest/axes/cartesian/time.html#changing-the-scale-type-from-time-scale-to-logarithmic-linear-scale
min: now - period.value*60*60*1000,
max: now,
ticks: {
autoSkip: true, // skip tick labels as needed
autoSkipPadding: 20, // padding between ticks
maxRotation: 0, // don't rotate the labels
maxTicksLimit: 15, // max tick labels to show
},
grid: {
drawOnChartArea: false,
},
}
},
y: {
type: 'linear',
min: 0,
max: result.cpuCount * 100,
ticks: {
callback: (value) => `${value}%`,
maxTicksLimit: 6 // max tick labels to show
},
min: 0,
max: result.cpuCount * 100,
beginAtZero: true,
grid: {
drawOnChartArea: false,
@@ -130,22 +133,14 @@ async function refresh() {
if (cpuGraph) cpuGraph.destroy();
cpuGraph = new Chart(cpuGraphNode.value, { type: 'line', data: cpuGraphData, options: cpuGraphOptions });
const memoryLabels = result.memory.map(v => {
return moment(v[1]*1000).format('hh:mm');
});
const memoryData = result.memory.map(v => {
return (v[0] / 1024 / 1024 / 1024).toFixed(2);
});
const swapData = result.swap.map(v => { // assume that there is 1:1 timeline for swap and memory
return (v[0] / 1024 / 1024 / 1024).toFixed(2);
});
const memoryLabels = result.memory.map(v => v[1]*1000); // convert to msecs
const memoryData = result.memory.map(v => (v[0] / 1024 / 1024 / 1024).toFixed(2));
// assume that there is 1:1 timeline for swap and memory data
const swapData = result.swap.map(v => (v[0] / 1024 / 1024 / 1024).toFixed(2));
const giB = 1024 * 1024 * 1024;
const quarterGiB = 0.25 * giB;
const roundedMemory = Math.round(systemMemory.memory / quarterGiB) * quarterGiB;
const roundedSwap = Math.round(systemMemory.swap / quarterGiB) * quarterGiB;
const roundedMemory = Math.ceil(systemMemory.memory / giB) * giB; // we have to scale up so that the graph can show the data!
const roundedSwap = Math.ceil(systemMemory.swap / giB) * giB;
const memoryGraphData = {
labels: memoryLabels,
@@ -184,27 +179,28 @@ async function refresh() {
},
scales: {
x: {
type: 'time',
bounds: 'ticks', // otherwise data bound. https://www.chartjs.org/docs/latest/axes/cartesian/time.html#changing-the-scale-type-from-time-scale-to-logarithmic-linear-scale
min: now - period.value*60*60*1000,
max: now,
ticks: {
autoSkip: true, // skip tick labels as needed
autoSkipPadding: 20, // padding between ticks
maxRotation: 0, // don't rotate the labels
maxTicksLimit: 15, // max tick labels to show
},
grid: {
drawOnChartArea: false,
},
}
},
y: {
ticks: {
callback: (value) => `${value} GiB`,
color: (value /* ,index, ticks */) => {
const tickValue = parseFloat(value['tick']['value']);
return ((tickValue * 1024 * 1024 * 1024) > roundedMemory) ? '#ff7d98' : '#46a9ec';
},
maxTicksLimit: 6 // max tick labels to show
},
type: 'linear',
min: 0,
max: ((roundedMemory + roundedSwap)/ giB).toFixed(2), // string
max: (roundedMemory + roundedSwap)/ giB,
ticks: {
stepSize: 1,
autoSkip: true, // skip tick labels as needed
autoSkipPadding: 20, // padding between ticks
callback: (value) => `${value} GiB`,
maxTicksLimit: 8 // max tick labels to show
},
beginAtZero: true,
stacked: true,
grid: {