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);
|
this.checkRessourceInterval = setInterval(() => this.checkRessource(), intervalMs);
|
||||||
console.log(`Started resource checking for room ${this.roomName}.`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
stopCheckingResources() {
|
stopCheckingResources() {
|
||||||
if (this.checkRessourceInterval) {
|
if (this.checkRessourceInterval) {
|
||||||
clearInterval(this.checkRessourceInterval);
|
clearInterval(this.checkRessourceInterval);
|
||||||
this.checkRessourceInterval = null;
|
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 dotenv from 'dotenv';
|
||||||
import generateMetricsReport from './utility/metrics_generator.js';
|
import generateMetricsReport from './utility/metrics_generator.js';
|
||||||
|
|
||||||
// Load environment variables
|
|
||||||
dotenv.config();
|
dotenv.config();
|
||||||
|
|
||||||
const BASE_URL = process.env.BASE_URL || 'http://localhost';
|
const config = {
|
||||||
const user = {
|
baseUrl: process.env.BASE_URL || 'http://msevignyl.duckdns.org',
|
||||||
|
auth: {
|
||||||
username: process.env.USER_EMAIL || 'admin@admin.com',
|
username: process.env.USER_EMAIL || 'admin@admin.com',
|
||||||
password: process.env.USER_PASSWORD || 'admin'
|
password: process.env.USER_PASSWORD || 'admin'
|
||||||
};
|
},
|
||||||
const numberRooms = parseInt(process.env.NUMBER_ROOMS || '4');
|
rooms: {
|
||||||
const usersPerRoom = parseInt(process.env.USERS_PER_ROOM || '60');
|
count: parseInt(process.env.NUMBER_ROOMS || '5'),
|
||||||
const roomAssociations = {};
|
usersPerRoom: parseInt(process.env.USERS_PER_ROOM || '60'),
|
||||||
const maxMessages = parseInt(process.env.MAX_MESSAGES || '20');
|
batchSize: 5,
|
||||||
const conversationInterval = parseInt(process.env.CONVERSATION_INTERVAL || '1000');
|
batchDelay: 250
|
||||||
const batchSize = 5;
|
},
|
||||||
const batchDelay = 250;
|
simulation: {
|
||||||
const roomDelay = 500;
|
maxMessages: parseInt(process.env.MAX_MESSAGES || '20'),
|
||||||
|
messageInterval: parseInt(process.env.CONVERSATION_INTERVAL || '1000'),
|
||||||
/**
|
responseTimeout: 5000
|
||||||
* Creates a room and immediately connects a teacher to it.
|
|
||||||
*/
|
|
||||||
async function createRoomWithTeacher(token, index) {
|
|
||||||
try {
|
|
||||||
const room = await createRoomContainer(BASE_URL, token);
|
|
||||||
if (!room?.id) {
|
|
||||||
throw new Error('Room creation failed');
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Initialize room associations
|
const rooms = new Map();
|
||||||
roomAssociations[room.id] = { watcher: null, teacher: null, students: [] };
|
|
||||||
|
async function setupRoom(token, index) {
|
||||||
|
try {
|
||||||
|
const room = await createRoomContainer(config.baseUrl, token);
|
||||||
|
if (!room?.id) throw new Error('Room creation failed');
|
||||||
|
|
||||||
// Create and connect teacher immediately
|
|
||||||
const teacher = new Teacher(`teacher_${index}`, room.id);
|
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
|
const students = Array.from({ length: config.rooms.usersPerRoom - 2 },
|
||||||
await teacher.connectToRoom(BASE_URL);
|
(_, i) => new Student(`student_${index}_${i}`, room.id));
|
||||||
|
|
||||||
|
rooms.set(room.id, { teacher, watcher, students });
|
||||||
return room.id;
|
return room.id;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.warn(`Failed to create/connect room ${index + 1}:`, err.message);
|
console.warn(`Room ${index} setup failed:`, err.message);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
async function connectParticipants(roomId) {
|
||||||
* Creates rooms and connects teachers with controlled concurrency.
|
const { students } = rooms.get(roomId);
|
||||||
*/
|
const participants = [...students];
|
||||||
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.');
|
|
||||||
|
|
||||||
console.log('Room creation with immediate teacher connection');
|
for (let i = 0; i < participants.length; i += config.rooms.batchSize) {
|
||||||
const roomPromises = Array.from({ length: numberRooms }, (_, index) =>
|
const batch = participants.slice(i, i + config.rooms.batchSize);
|
||||||
createRoomWithTeacher(token, index)
|
await Promise.all(batch.map(p =>
|
||||||
);
|
|
||||||
|
|
||||||
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');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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([
|
Promise.race([
|
||||||
participant.connectToRoom(baseUrl),
|
p.connectToRoom(config.baseUrl),
|
||||||
new Promise((_, reject) =>
|
new Promise((_, reject) => setTimeout(() => reject(new Error('Timeout')), 10000))
|
||||||
setTimeout(() => reject(new Error('Connection timeout')), 10000)
|
]).catch(err => console.warn(`Connection failed for ${p.username}:`, err.message))
|
||||||
)
|
));
|
||||||
]).catch(err => {
|
await new Promise(resolve => setTimeout(resolve, config.rooms.batchDelay));
|
||||||
console.warn(
|
}
|
||||||
`Failed to connect ${participant.username} in room ${roomId}:`,
|
}
|
||||||
err.message
|
|
||||||
);
|
async function simulate() {
|
||||||
return null;
|
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
|
||||||
|
])
|
||||||
);
|
);
|
||||||
|
return generateMetricsReport(data);
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Finished connecting remaining participants');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rest of the code remains the same
|
function cleanup() {
|
||||||
async function simulateParticipants() {
|
for (const { teacher, watcher, students } of rooms.values()) {
|
||||||
const conversationPromises = Object.entries(roomAssociations).map(async ([roomId, participants]) => {
|
[teacher, watcher, ...students].forEach(p => p?.disconnect());
|
||||||
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() {
|
async function main() {
|
||||||
try {
|
try {
|
||||||
await createRoomContainers();
|
const token = await attemptLoginOrRegister(config.baseUrl, config.auth.username, config.auth.password);
|
||||||
addRemainingUsers();
|
if (!token) throw new Error('Authentication failed');
|
||||||
await connectRemainingParticipants(BASE_URL);
|
|
||||||
|
|
||||||
// Wait for initial baseline metrics
|
console.log('Creating rooms...');
|
||||||
console.log('Collecting baseline metrics...');
|
const roomIds = await Promise.all(
|
||||||
await wait(5000);
|
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...');
|
console.log('Retrieving baseline metrics...');
|
||||||
await wait(5000); // 5 second delay
|
await new Promise(resolve => setTimeout(resolve, 10000)); // Baseline metrics
|
||||||
|
|
||||||
await generateExecutionData();
|
console.log('Starting simulation across all rooms...');
|
||||||
console.log('All tasks completed successfully!');
|
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) {
|
} catch (error) {
|
||||||
console.error('Error:', error.message);
|
console.error('Error:', error.message);
|
||||||
|
} finally {
|
||||||
|
cleanup();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Graceful shutdown handlers
|
['SIGINT', 'exit', 'uncaughtException', 'unhandledRejection'].forEach(event => {
|
||||||
process.on('SIGINT', () => {
|
process.on(event, cleanup);
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
main();
|
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