mirror of
https://github.com/ets-cfuhrman-pfe/EvalueTonSavoir.git
synced 2025-08-11 21:23:54 -04:00
cleanup
This commit is contained in:
parent
d3199b9f3f
commit
1e67762b5e
3 changed files with 119 additions and 446 deletions
|
|
@ -39,14 +39,12 @@ export class Watcher extends RoomParticipant {
|
|||
}
|
||||
|
||||
this.checkRessourceInterval = setInterval(() => this.checkRessource(), intervalMs);
|
||||
console.log(`Started resource checking for room ${this.roomName}.`);
|
||||
}
|
||||
|
||||
stopCheckingResources() {
|
||||
if (this.checkRessourceInterval) {
|
||||
clearInterval(this.checkRessourceInterval);
|
||||
this.checkRessourceInterval = null;
|
||||
console.log(`Stopped resource checking for room ${this.roomName}.`);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,255 +5,157 @@ import { Watcher } from './class/watcher.js';
|
|||
import dotenv from 'dotenv';
|
||||
import generateMetricsReport from './utility/metrics_generator.js';
|
||||
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
|
||||
const BASE_URL = process.env.BASE_URL || 'http://localhost';
|
||||
const user = {
|
||||
username: process.env.USER_EMAIL || 'admin@admin.com',
|
||||
password: process.env.USER_PASSWORD || 'admin'
|
||||
const config = {
|
||||
baseUrl: process.env.BASE_URL || 'http://msevignyl.duckdns.org',
|
||||
auth: {
|
||||
username: process.env.USER_EMAIL || 'admin@admin.com',
|
||||
password: process.env.USER_PASSWORD || 'admin'
|
||||
},
|
||||
rooms: {
|
||||
count: parseInt(process.env.NUMBER_ROOMS || '5'),
|
||||
usersPerRoom: parseInt(process.env.USERS_PER_ROOM || '60'),
|
||||
batchSize: 5,
|
||||
batchDelay: 250
|
||||
},
|
||||
simulation: {
|
||||
maxMessages: parseInt(process.env.MAX_MESSAGES || '20'),
|
||||
messageInterval: parseInt(process.env.CONVERSATION_INTERVAL || '1000'),
|
||||
responseTimeout: 5000
|
||||
}
|
||||
};
|
||||
const numberRooms = parseInt(process.env.NUMBER_ROOMS || '4');
|
||||
const usersPerRoom = parseInt(process.env.USERS_PER_ROOM || '60');
|
||||
const roomAssociations = {};
|
||||
const maxMessages = parseInt(process.env.MAX_MESSAGES || '20');
|
||||
const conversationInterval = parseInt(process.env.CONVERSATION_INTERVAL || '1000');
|
||||
const batchSize = 5;
|
||||
const batchDelay = 250;
|
||||
const roomDelay = 500;
|
||||
|
||||
/**
|
||||
* Creates a room and immediately connects a teacher to it.
|
||||
*/
|
||||
async function createRoomWithTeacher(token, index) {
|
||||
const rooms = new Map();
|
||||
|
||||
async function setupRoom(token, index) {
|
||||
try {
|
||||
const room = await createRoomContainer(BASE_URL, token);
|
||||
if (!room?.id) {
|
||||
throw new Error('Room creation failed');
|
||||
}
|
||||
const room = await createRoomContainer(config.baseUrl, token);
|
||||
if (!room?.id) throw new Error('Room creation failed');
|
||||
|
||||
// Initialize room associations
|
||||
roomAssociations[room.id] = { watcher: null, teacher: null, students: [] };
|
||||
|
||||
// Create and connect teacher immediately
|
||||
const teacher = new Teacher(`teacher_${index}`, room.id);
|
||||
roomAssociations[room.id].teacher = teacher;
|
||||
const watcher = new Watcher(`watcher_${index}`, room.id);
|
||||
await Promise.all([
|
||||
teacher.connectToRoom(config.baseUrl)
|
||||
.catch(err => console.warn(`Teacher ${index} connection failed:`, err.message)),
|
||||
watcher.connectToRoom(config.baseUrl)
|
||||
.catch(err => console.warn(`Watcher ${index} connection failed:`, err.message))
|
||||
]);
|
||||
|
||||
// Connect teacher to room
|
||||
await teacher.connectToRoom(BASE_URL);
|
||||
const students = Array.from({ length: config.rooms.usersPerRoom - 2 },
|
||||
(_, i) => new Student(`student_${index}_${i}`, room.id));
|
||||
|
||||
rooms.set(room.id, { teacher, watcher, students });
|
||||
return room.id;
|
||||
} catch (err) {
|
||||
console.warn(`Failed to create/connect room ${index + 1}:`, err.message);
|
||||
console.warn(`Room ${index} setup failed:`, err.message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates rooms and connects teachers with controlled concurrency.
|
||||
*/
|
||||
async function createRoomContainers() {
|
||||
console.log('Attempting login or register to get token');
|
||||
const token = await attemptLoginOrRegister(BASE_URL, user.username, user.password);
|
||||
if (!token) throw new Error('Failed to login or register.');
|
||||
async function connectParticipants(roomId) {
|
||||
const { students } = rooms.get(roomId);
|
||||
const participants = [...students];
|
||||
|
||||
console.log('Room creation with immediate teacher connection');
|
||||
const roomPromises = Array.from({ length: numberRooms }, (_, index) =>
|
||||
createRoomWithTeacher(token, index)
|
||||
for (let i = 0; i < participants.length; i += config.rooms.batchSize) {
|
||||
const batch = participants.slice(i, i + config.rooms.batchSize);
|
||||
await Promise.all(batch.map(p =>
|
||||
Promise.race([
|
||||
p.connectToRoom(config.baseUrl),
|
||||
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
|
||||
]).catch(err => console.warn(`Connection failed for ${p.username}:`, err.message))
|
||||
));
|
||||
await new Promise(resolve => setTimeout(resolve, config.rooms.batchDelay));
|
||||
}
|
||||
}
|
||||
|
||||
async function simulate() {
|
||||
const simulations = Array.from(rooms.entries()).map(async ([roomId, { teacher, students }]) => {
|
||||
const connectedStudents = students.filter(student => student.socket?.connected);
|
||||
const expectedResponses = connectedStudents.length;
|
||||
|
||||
for (let i = 0; i < config.simulation.maxMessages; i++) {
|
||||
const initialMessages = teacher.nbrMessageReceived;
|
||||
|
||||
teacher.broadcastMessage(`Message ${i + 1} from ${teacher.username}`);
|
||||
|
||||
try {
|
||||
await Promise.race([
|
||||
new Promise(resolve => {
|
||||
const checkResponses = setInterval(() => {
|
||||
const receivedResponses = teacher.nbrMessageReceived - initialMessages;
|
||||
if (receivedResponses >= expectedResponses) {
|
||||
clearInterval(checkResponses);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
]);
|
||||
} catch (error) {
|
||||
console.error(`Error in room ${roomId} message ${i + 1}:`, error);
|
||||
}
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, config.simulation.messageInterval));
|
||||
}
|
||||
});
|
||||
|
||||
// Wait for all simulations to complete
|
||||
await Promise.all(simulations);
|
||||
console.log('All room simulations completed');
|
||||
}
|
||||
|
||||
async function generateReport() {
|
||||
const data = Object.fromEntries(
|
||||
Array.from(rooms.entries()).map(([id, { watcher }]) => [
|
||||
id,
|
||||
watcher.roomRessourcesData
|
||||
])
|
||||
);
|
||||
|
||||
const results = await Promise.allSettled(roomPromises);
|
||||
const successfulRooms = results.filter(r => r.status === 'fulfilled' && r.value).length;
|
||||
|
||||
console.log(`Total rooms created and connected (${numberRooms}): ${successfulRooms}`);
|
||||
console.log('Finished room creation and teacher connection');
|
||||
return generateMetricsReport(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds remaining participants (watcher, students) to rooms.
|
||||
*/
|
||||
function addRemainingUsers() {
|
||||
console.log('Adding remaining room participants');
|
||||
Object.keys(roomAssociations).forEach((roomId, roomIndex) => {
|
||||
const participants = roomAssociations[roomId];
|
||||
|
||||
// Add watcher
|
||||
participants.watcher = new Watcher(`watcher_${roomIndex}`, roomId);
|
||||
|
||||
// Add students
|
||||
for (let i = 0; i < usersPerRoom - 2; i++) {
|
||||
participants.students.push(new Student(`student_${roomIndex}_${i}`, roomId));
|
||||
}
|
||||
});
|
||||
console.log('Finished adding remaining room participants');
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects remaining participants to their respective rooms.
|
||||
*/
|
||||
async function connectRemainingParticipants(baseUrl) {
|
||||
console.log('Connecting remaining participants in batches');
|
||||
|
||||
for (const [roomId, participants] of Object.entries(roomAssociations)) {
|
||||
console.log(`Processing room ${roomId}`);
|
||||
|
||||
const remainingParticipants = [
|
||||
participants.watcher,
|
||||
...participants.students
|
||||
].filter(Boolean);
|
||||
|
||||
// Connect in smaller batches with longer delays
|
||||
for (let i = 0; i < remainingParticipants.length; i += batchSize) {
|
||||
const batch = remainingParticipants.slice(i, i + batchSize);
|
||||
|
||||
// Add connection timeout handling
|
||||
const batchPromises = batch.map(participant =>
|
||||
Promise.race([
|
||||
participant.connectToRoom(baseUrl),
|
||||
new Promise((_, reject) =>
|
||||
setTimeout(() => reject(new Error('Connection timeout')), 10000)
|
||||
)
|
||||
]).catch(err => {
|
||||
console.warn(
|
||||
`Failed to connect ${participant.username} in room ${roomId}:`,
|
||||
err.message
|
||||
);
|
||||
return null;
|
||||
})
|
||||
);
|
||||
|
||||
await Promise.all(batchPromises);
|
||||
|
||||
// Cleanup disconnected sockets
|
||||
batch.forEach(participant => {
|
||||
if (!participant.socket?.connected) {
|
||||
participant.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, batchDelay));
|
||||
}
|
||||
|
||||
// Add delay between rooms
|
||||
await new Promise(resolve => setTimeout(resolve, roomDelay));
|
||||
function cleanup() {
|
||||
for (const { teacher, watcher, students } of rooms.values()) {
|
||||
[teacher, watcher, ...students].forEach(p => p?.disconnect());
|
||||
}
|
||||
|
||||
console.log('Finished connecting remaining participants');
|
||||
}
|
||||
|
||||
// Rest of the code remains the same
|
||||
async function simulateParticipants() {
|
||||
const conversationPromises = Object.entries(roomAssociations).map(async ([roomId, participants]) => {
|
||||
const { teacher, students } = participants;
|
||||
|
||||
if (!teacher || students.length === 0) {
|
||||
console.warn(`Room ${roomId} has no teacher or students to simulate.`);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`Starting simulation for room ${roomId}`);
|
||||
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
|
||||
for (let i = 0; i < maxMessages; i++) {
|
||||
const teacherMessage = `Message ${i + 1} from ${teacher.username}`;
|
||||
teacher.broadcastMessage(teacherMessage);
|
||||
await new Promise(resolve => setTimeout(resolve, conversationInterval));
|
||||
}
|
||||
|
||||
console.log(`Finished simulation for room ${roomId}`);
|
||||
});
|
||||
|
||||
await Promise.all(conversationPromises);
|
||||
}
|
||||
|
||||
function disconnectParticipants() {
|
||||
console.time('Disconnecting participants');
|
||||
Object.values(roomAssociations).forEach(participants => {
|
||||
participants.teacher?.disconnect();
|
||||
participants.watcher?.disconnect();
|
||||
participants.students.forEach(student => student.disconnect());
|
||||
});
|
||||
console.timeEnd('Disconnecting participants');
|
||||
console.log('All participants disconnected successfully.');
|
||||
}
|
||||
|
||||
async function wait(ms) {
|
||||
return new Promise(resolve => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
async function generateExecutionData() {
|
||||
console.log('Generating execution data');
|
||||
|
||||
const allRoomsData = {};
|
||||
for (const [roomId, participants] of Object.entries(roomAssociations)) {
|
||||
if (participants.watcher?.roomRessourcesData.length > 0) {
|
||||
// Add phase markers to the data
|
||||
const data = participants.watcher.roomRessourcesData;
|
||||
const simulationStartIdx = 20; // Assuming first 20 samples are baseline
|
||||
const simulationEndIdx = data.length - 20; // Last 20 samples are post-simulation
|
||||
|
||||
data.forEach((sample, index) => {
|
||||
if (index < simulationStartIdx) {
|
||||
sample.phase = 'baseline';
|
||||
} else if (index > simulationEndIdx) {
|
||||
sample.phase = 'post-simulation';
|
||||
} else {
|
||||
sample.phase = 'simulation';
|
||||
}
|
||||
});
|
||||
|
||||
allRoomsData[roomId] = data;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await generateMetricsReport(allRoomsData);
|
||||
console.log(`Generated metrics in ${result.outputDir}`);
|
||||
|
||||
console.log('Finished generating execution data');
|
||||
}
|
||||
|
||||
async function main() {
|
||||
try {
|
||||
await createRoomContainers();
|
||||
addRemainingUsers();
|
||||
await connectRemainingParticipants(BASE_URL);
|
||||
const token = await attemptLoginOrRegister(config.baseUrl, config.auth.username, config.auth.password);
|
||||
if (!token) throw new Error('Authentication failed');
|
||||
|
||||
// Wait for initial baseline metrics
|
||||
console.log('Collecting baseline metrics...');
|
||||
await wait(5000);
|
||||
console.log('Creating rooms...');
|
||||
const roomIds = await Promise.all(
|
||||
Array.from({ length: config.rooms.count }, (_, i) => setupRoom(token, i))
|
||||
);
|
||||
|
||||
await simulateParticipants();
|
||||
console.log('Connecting participants...');
|
||||
await Promise.all(roomIds.filter(Boolean).map(connectParticipants));
|
||||
|
||||
console.log('Waiting for system to stabilize...');
|
||||
await wait(5000); // 5 second delay
|
||||
|
||||
await generateExecutionData();
|
||||
console.log('All tasks completed successfully!');
|
||||
console.log('Retrieving baseline metrics...');
|
||||
await new Promise(resolve => setTimeout(resolve, 10000)); // Baseline metrics
|
||||
|
||||
console.log('Starting simulation across all rooms...');
|
||||
await simulate();
|
||||
|
||||
console.log('Simulation complete. Waiting for system stabilization...');
|
||||
await new Promise(resolve => setTimeout(resolve, 10000)); // System stabilization
|
||||
|
||||
console.log('All simulations finished, generating final report...');
|
||||
const folderName = await generateReport();
|
||||
console.log(`Metrics report generated in ${folderName.outputDir}`);
|
||||
|
||||
console.log('All done!');
|
||||
} catch (error) {
|
||||
console.error('Error:', error.message);
|
||||
} finally {
|
||||
cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
// Graceful shutdown handlers
|
||||
process.on('SIGINT', () => {
|
||||
console.log('Process interrupted (Ctrl+C).');
|
||||
disconnectParticipants();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on('exit', disconnectParticipants);
|
||||
|
||||
process.on('uncaughtException', err => {
|
||||
console.error('Uncaught Exception:', err);
|
||||
disconnectParticipants();
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||
disconnectParticipants();
|
||||
process.exit(1);
|
||||
['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection'].forEach(event => {
|
||||
process.on(event, cleanup);
|
||||
});
|
||||
|
||||
main();
|
||||
|
|
@ -1,227 +0,0 @@
|
|||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { ChartJSNodeCanvas } from "chartjs-node-canvas";
|
||||
|
||||
// Ensure a directory exists, creating it if necessary
|
||||
function ensureDirectoryExists(directory) {
|
||||
try {
|
||||
fs.mkdirSync(directory, { recursive: true });
|
||||
} catch (err) {
|
||||
console.error(`Error creating directory ${directory}:`, err.message);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// Write metrics to a JSON file
|
||||
export function writeMetricsToFile(metrics) {
|
||||
if (!metrics.endTime) {
|
||||
console.error("Error: metrics.endTime is not defined. Ensure it is set before calling this function.");
|
||||
return;
|
||||
}
|
||||
|
||||
const directory = `./metrics/${metrics.startTime.toISOString().replace(/[:.]/g, "-")}`;
|
||||
const filename = path.join(directory, "metrics_report.json");
|
||||
|
||||
// Ensure the directory exists
|
||||
ensureDirectoryExists(directory);
|
||||
|
||||
const metricsData = {
|
||||
summary: {
|
||||
roomsCreated: metrics.roomsCreated,
|
||||
roomsFailed: metrics.roomsFailed,
|
||||
teachersConnected: metrics.teachersConnected,
|
||||
teachersFailed: metrics.teachersFailed,
|
||||
studentsConnected: metrics.studentsConnected,
|
||||
studentsFailed: metrics.studentsFailed,
|
||||
},
|
||||
messages: {
|
||||
messagesSent: metrics.messagesSent,
|
||||
messagesReceived: metrics.messagesReceived,
|
||||
throughput: metrics.throughput.toFixed(2), // Messages per second
|
||||
},
|
||||
latencies: {
|
||||
totalLatency: metrics.totalLatency,
|
||||
averageLatency: metrics.teachersConnected + metrics.studentsConnected
|
||||
? (metrics.totalLatency / (metrics.teachersConnected + metrics.studentsConnected)).toFixed(2)
|
||||
: null,
|
||||
maxLatency: metrics.maxLatency,
|
||||
minLatency: metrics.minLatency,
|
||||
},
|
||||
timing: {
|
||||
startTime: metrics.startTime?.toISOString(),
|
||||
endTime: metrics.endTime?.toISOString(),
|
||||
executionTimeInSeconds: metrics.endTime && metrics.startTime
|
||||
? (metrics.endTime - metrics.startTime) / 1000
|
||||
: null,
|
||||
},
|
||||
};
|
||||
|
||||
// Write metrics to a file
|
||||
fs.writeFile(filename, JSON.stringify(metricsData, null, 4), (err) => {
|
||||
if (err) {
|
||||
console.error(`Error writing metrics to file:`, err.message);
|
||||
} else {
|
||||
console.log(`Metrics saved to file: ${filename}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Generate charts for resource data
|
||||
export async function generateGraphs(resourceData, metrics) {
|
||||
if (!metrics.endTime) {
|
||||
console.error("Error: metrics.endTime is not defined. Ensure it is set before calling this function.");
|
||||
return;
|
||||
}
|
||||
|
||||
const directory = `./metrics/${metrics.startTime.toISOString().replace(/[:.]/g, "-")}`;
|
||||
ensureDirectoryExists(directory);
|
||||
|
||||
const chartJSNodeCanvas = new ChartJSNodeCanvas({ width: 800, height: 600 });
|
||||
|
||||
// Aggregated data for all containers
|
||||
const aggregatedTimestamps = [];
|
||||
const aggregatedMemoryUsage = [];
|
||||
const aggregatedCpuUserPercentage = [];
|
||||
const aggregatedCpuSystemPercentage = [];
|
||||
|
||||
// Generate charts for individual containers
|
||||
for (const [roomId, data] of Object.entries(resourceData)) {
|
||||
if (!data || !data.length) {
|
||||
console.warn(`No data available for room ${roomId}. Skipping individual charts.`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Extract data
|
||||
const timestamps = data.map((point) => new Date(point.timestamp).toLocaleTimeString());
|
||||
const memoryUsage = data.map((point) => (point.memory.rss || 0) / (1024 * 1024)); // MB
|
||||
const cpuUserPercentage = data.map((point) => point.cpu.userPercentage || 0);
|
||||
const cpuSystemPercentage = data.map((point) => point.cpu.systemPercentage || 0);
|
||||
|
||||
// Update aggregated data
|
||||
data.forEach((point, index) => {
|
||||
if (!aggregatedTimestamps[index]) {
|
||||
aggregatedTimestamps[index] = timestamps[index];
|
||||
aggregatedMemoryUsage[index] = 0;
|
||||
aggregatedCpuUserPercentage[index] = 0;
|
||||
aggregatedCpuSystemPercentage[index] = 0;
|
||||
}
|
||||
aggregatedMemoryUsage[index] += memoryUsage[index];
|
||||
aggregatedCpuUserPercentage[index] += cpuUserPercentage[index];
|
||||
aggregatedCpuSystemPercentage[index] += cpuSystemPercentage[index];
|
||||
});
|
||||
|
||||
// Memory usage chart
|
||||
await saveChart(
|
||||
chartJSNodeCanvas,
|
||||
{
|
||||
labels: timestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: "Memory Usage (MB)",
|
||||
data: memoryUsage,
|
||||
borderColor: "blue",
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"Time",
|
||||
"Memory Usage (MB)",
|
||||
path.join(directory, `memory-usage-room-${roomId}.png`)
|
||||
);
|
||||
|
||||
// CPU usage chart
|
||||
await saveChart(
|
||||
chartJSNodeCanvas,
|
||||
{
|
||||
labels: timestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: "CPU User Usage (%)",
|
||||
data: cpuUserPercentage,
|
||||
borderColor: "red",
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
label: "CPU System Usage (%)",
|
||||
data: cpuSystemPercentage,
|
||||
borderColor: "orange",
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"Time",
|
||||
"CPU Usage (%)",
|
||||
path.join(directory, `cpu-usage-room-${roomId}.png`)
|
||||
);
|
||||
|
||||
console.log(`Charts generated for room ${roomId}`);
|
||||
}
|
||||
|
||||
// Ensure aggregated data is not empty
|
||||
if (!aggregatedTimestamps.length) {
|
||||
console.error("Error: Aggregated data is empty. Verify container data.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Aggregated memory usage chart
|
||||
await saveChart(
|
||||
chartJSNodeCanvas,
|
||||
{
|
||||
labels: aggregatedTimestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: "Total Memory Usage (MB)",
|
||||
data: aggregatedMemoryUsage,
|
||||
borderColor: "blue",
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"Time",
|
||||
"Memory Usage (MB)",
|
||||
path.join(directory, "aggregated-memory-usage.png")
|
||||
);
|
||||
|
||||
// Aggregated CPU usage chart
|
||||
await saveChart(
|
||||
chartJSNodeCanvas,
|
||||
{
|
||||
labels: aggregatedTimestamps,
|
||||
datasets: [
|
||||
{
|
||||
label: "Total CPU User Usage (%)",
|
||||
data: aggregatedCpuUserPercentage,
|
||||
borderColor: "red",
|
||||
fill: false,
|
||||
},
|
||||
{
|
||||
label: "Total CPU System Usage (%)",
|
||||
data: aggregatedCpuSystemPercentage,
|
||||
borderColor: "orange",
|
||||
fill: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
"Time",
|
||||
"CPU Usage (%)",
|
||||
path.join(directory, "aggregated-cpu-usage.png")
|
||||
);
|
||||
|
||||
console.log("Aggregated charts generated.");
|
||||
}
|
||||
|
||||
// Helper function to save a chart
|
||||
async function saveChart(chartJSNodeCanvas, data, xLabel, yLabel, outputFile) {
|
||||
const chartConfig = {
|
||||
type: "line",
|
||||
data,
|
||||
options: {
|
||||
scales: {
|
||||
x: { title: { display: true, text: xLabel } },
|
||||
y: { title: { display: true, text: yLabel } },
|
||||
},
|
||||
},
|
||||
};
|
||||
const chartBuffer = await chartJSNodeCanvas.renderToBuffer(chartConfig);
|
||||
fs.writeFileSync(outputFile, chartBuffer);
|
||||
}
|
||||
Loading…
Reference in a new issue