Sesion Laboratorio 12 Practica 5 1 - jesusgpa/2024-2025-CSAAI GitHub Wiki
- Tiempo: 2h
- Fecha: Jueves 06 de Mayo de 2025
-
Objetivos de la sesión:
- Presentar la práctica 5
- Aprender a acceder a los píxeles de las imágenes
- Utilizar los filtros de imágenes para simular el tráfico en la red
- Usar los deslizadores para simular los umbrales de carga en los nodos
- Introducción
- Enunciado de la práctica 5 (ESPECIFICACIONES)
- ¡A practicar!
- Resumen de tareas a realizar
- Conclusiones
- Autores
- Licencia
- Enlaces
Diseñar una aplicación en javascript que simula el envío de paquetes a través de internet.
Vamos a implementar una simulación muy básica y a la vez completa, del envío de un paquete de datos multimedia a través de internet.
Para conseguirlo tenemos por delante las siguientes tareas.
- Diseñar una red compuesta por nodos y conexiones de manera aleatoria.
- Calcular la ruta mínima para llegar del nodo origen al nodo destino.
- Visualizar la ruta mínima.
- Calcular el tiempo total que tarda el paquete en llegar al destino.
- Visualizar el tiempo total.
Tenemos un camino por recorrer, empezaremos por dar un paso.
Para poder enviar paquetes de datos a través de la red, una de las cosas que necesitamos es conocer la red.
Vamos a empezar por generar una red de nodos conectados de manera aleatoria.
Vamos a necesitar un botón para generar la red de manera aleatoria tantas veces como queramos.
Además de un canvas para dibujar la red.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Simulación de envío de paquetes de datos</title>
<link rel="stylesheet" href="net.css">
<script src="net-06.js" defer></script>
</head>
<body>
<div class="controls">
<button id="btnCNet">Generar Red</button>
</div>
<canvas id="networkCanvas" width="800" height="400"></canvas>
</body>
</html>
Vamos a añadir algunas reglas para visualizar el canvas y los controles, de momento un botón.
canvas {
border: 1px solid black;
}
.controls {
display: flex;
gap: 20px;
margin-bottom: 20px;
}
button {
background: #282A3A;
color: #FFF;
border-radius: 2px;
padding: 10px 20px;
border: 0;
cursor: pointer;
font-family: Fredoka;
font-size: 18pt;
}
Para empezar vamos a necesitar algunas variables para trabajar.
// Variables de trabajo
const canvas = document.getElementById('networkCanvas');
const ctx = canvas.getContext('2d');
let redAleatoria;
const nodeRadius = 20;
const numNodos = 5;
const nodeConnect = 2
// Localizando elementos en el DOM
const btnCNet = document.getElementById("btnCNet");
Además de una clase de tipo nodo, donde almacenaremos la información de cada nodo de la red.
// Clase para representar un nodo en el grafo
class Nodo {
constructor(id, x, y, delay) {
this.id = id; // Identificador del nodo
this.x = x; // Coordenada X del nodo
this.y = y; // Coordenada Y del nodo
this.delay = delay; // Retardo del nodo en milisegundos
this.conexiones = []; // Array de conexiones a otros nodos
}
// Método para agregar una conexión desde este nodo a otro nodo con un peso dado
conectar(nodo, peso) {
this.conexiones.push({ nodo, peso });
}
}
Ahora viene la parte en la que generamos los nodos de la red, y a cada uno le asignamos una congestión (retardo) aleatorio.
// Función para generar una red aleatoria con nodos en diferentes estados de congestión
function crearRedAleatoriaConCongestion(numNodos, numConexiones) {
const nodos = [];
let x = 0, y = 0, delay = 0;
let nodoActual = 0, nodoAleatorio = 0, pickNode = 0, peso = 0;
// Generamos los nodos
for (let i = 0; i < numNodos; i++) {
x = Math.random() * canvas.width; // Obtenemos un valor aleatorio para la coordenada x del nodo
y = Math.random() * canvas.height; // Obtenemos un valor aleatorio para la coordenada y del nodo
delay = generarRetardo(); // Retardo aleatorio para simular congestión
nodos.push(new Nodo(i, x, y, delay)); // Generar un nuevo nodo y añadirlo a la lista de nodos de la red
}
// Conectamos los nodos
for (let i = 0; i < numNodos; i++) {
nodoActual = nodos[i];
for (let j = 0; j < numConexiones; j++) {
pickNode = Math.floor(Math.random() * numNodos);
nodoAleatorio = nodos[pickNode];
peso = Math.random() * 100; // Peso aleatorio para simular la distancia entre nodos
nodoActual.conectar(nodoAleatorio, peso);
}
}
return nodos;
}
La función para obtener un valor aleatorio de retardo es la siguiente:
// Función para generar un retardo aleatorio entre 0 y 1000 ms
function generarRetardo() {
return Math.random() * 1000;
}
Además tendremos otra función para dibujar la red.
// Dibujar la red en el canvas
function drawNet(nnodes) {
// Dibujamos las conexiones entre nodos
nnodes.forEach(nodo => {
nodo.conexiones.forEach(({ nodo: conexion, peso }) => {
ctx.beginPath();
ctx.moveTo(nodo.x, nodo.y);
ctx.lineTo(conexion.x, conexion.y);
ctx.stroke();
});
});
// Dibujamos los nodos
nnodes.forEach(nodo => {
ctx.beginPath();
ctx.arc(nodo.x, nodo.y, nodeRadius, 0, 2 * Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.stroke();
ctx.font = '12px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
ctx.fillText(nodo.id, nodo.x, nodo.y + 5);
});
}
Y por último, necesitamos una función de callback para el botón de generar la red.
// Función de calback para generar la red de manera aleatoria
btnCNet.onclick = () => {
// Generar red de nodos con congestión creada de manera aleatoria redAleatoria
// Cada nodo tendrá un delay aleatorio para simular el envío de paquetes de datos
redAleatoria = crearRedAleatoriaConCongestion(numNodos, nodeConnect);
// Limpiamos el canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dibujar la red que hemos generado
drawNet(redAleatoria);
}
Si todo ha salido bien, debes ver algo como lo que se ve en la imagen a continuación.
Cada vez que pulsamos en generar red se genera y dibuja una nueva red de nodos.
Si te fijas, estamos asignado un retardo aleatorio a cada nodo de la red, pero no lo estamos mostrando.
Eso nos impide ver por dónde enviaríamos los paquetes de datos conociendo el retardo en cada nodo.
Por eso estaría bien mostrar el retardo de cada nodo cuando lo pintamos.
Además, de vez en cuando uno o varios nodos quedan situados en los bordes del canvas y no los podemos visualizar bien.
Vamos a ver como se puede solucionar.
Para conseguir el efecto que buscamos no hay que tocar el html ni el css, todo pasa en javascript.
Como voy a mostrar más información, voy a pasar el valor de la constante nodeRadius de 20 a 40.
Además voy a añadir una nueva constante nodeRandomDelay que voy a utilizar en la generación del retardo aleatorio, porque así podré cambiar el valor máximo de retardo con facilidad.
const nodeRadius = 40;
const nodeRandomDelay = 1000;
En lugar de utilizar un máximo para generar la coordenadas x, y de cada nodo, voy a elegir las coordenadas dentro de un rango de valores.
De esta manera conseguiré que los valores x e y de cada nodo estén alejados de los bordes del canvas.
Para conseguirlo tengo que modificar la función crearRedAleatoriaConCongestion de la siguiente manera:
// Generamos los nodos
for (let i = 0; i < numNodos; i++) {
x = randomNumber(nodeRadius, (canvas.width - nodeRadius)); // Generar coordenada x aleatoria
y = randomNumber(nodeRadius, (canvas.height - nodeRadius)); // Generar coordenada y aleatoria
delay = generarRetardo(); // Retardo aleatorio para simular congestión
nodos.push(new Nodo(i, x, y, delay)); // Generar un nuevo nodo y añadirlo a la lista de nodos de la red
}
Además de añadir una nueva función para calcular un valor aleatorio dentro un rango de valores (mínimo, máximo):
// Generar un número aleatorio dentro de un rango
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
Y por último, hay que cambiar un poco la función drawNet para que muestre algo más de información, en concreto, el número de nodo y el delay.
Eso lo conseguiremos con la siguiente modificación:
let nodoDesc; // Descripción del nodo
// Dibujamos los nodos
nnodes.forEach(nodo => {
ctx.beginPath();
ctx.arc(nodo.x, nodo.y, nodeRadius, 0, 2 * Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.stroke();
ctx.font = '12px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
nodoDesc = "N" + nodo.id + " delay " + Math.floor(nodo.delay);
ctx.fillText(nodoDesc, nodo.x, nodo.y + 5);
});
No te olvides de utilizar la nueva constante nodeRandomDelay en la función generarRetardo:
// Función para generar un retardo aleatorio entre 0 y 1000 ms
function generarRetardo() {
return Math.random() * nodeRandomDelay;
}
Ahora sí, ya está todo.
Si todo ha salido bien, ahora debes visualizar así tu red.
Este el código javascript con todos los cambios aplicados:
// Variables de trabajo
const canvas = document.getElementById('networkCanvas');
const ctx = canvas.getContext('2d');
let redAleatoria;
const nodeRadius = 40;
const numNodos = 5;
const nodeConnect = 2;
const nodeRandomDelay = 1000;
// Localizando elementos en el DOM
const btnCNet = document.getElementById("btnCNet");
// Clase para representar un nodo en el grafo
class Nodo {
constructor(id, x, y, delay) {
this.id = id; // Identificador del nodo
this.x = x; // Coordenada X del nodo
this.y = y; // Coordenada Y del nodo
this.delay = delay; // Retardo del nodo en milisegundos
this.conexiones = []; // Array de conexiones a otros nodos
}
// Método para agregar una conexión desde este nodo a otro nodo con un peso dado
conectar(nodo, peso) {
this.conexiones.push({ nodo, peso });
}
}
// Función para generar una red aleatoria con nodos en diferentes estados de congestión
function crearRedAleatoriaConCongestion(numNodos, numConexiones) {
const nodos = [];
let x = 0, y = 0, delay = 0;
let nodoActual = 0, nodoAleatorio = 0, pickNode = 0, peso = 0;
// Generamos los nodos
for (let i = 0; i < numNodos; i++) {
x = randomNumber(nodeRadius, (canvas.width - nodeRadius)); // Generar coordenada x aleatoria
y = randomNumber(nodeRadius, (canvas.height - nodeRadius)); // Generar coordenada y aleatoria
delay = generarRetardo(); // Retardo aleatorio para simular congestión
nodos.push(new Nodo(i, x, y, delay)); // Generar un nuevo nodo y añadirlo a la lista de nodos de la red
}
// Conectamos los nodos
for (let i = 0; i < numNodos; i++) {
nodoActual = nodos[i];
for (let j = 0; j < numConexiones; j++) {
pickNode = Math.floor(Math.random() * numNodos);
nodoAleatorio = nodos[pickNode];
peso = Math.random() * 100; // Peso aleatorio para simular la distancia entre nodos
nodoActual.conectar(nodoAleatorio, peso);
}
}
return nodos;
}
// Función para generar un retardo aleatorio entre 0 y 1000 ms
function generarRetardo() {
return Math.random() * nodeRandomDelay;
}
// Generar un número aleatorio dentro de un rango
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
// Dibujar la red en el canvas
function drawNet(nnodes) {
// Dibujamos las conexiones entre nodos
nnodes.forEach(nodo => {
nodo.conexiones.forEach(({ nodo: conexion, peso }) => {
ctx.beginPath();
ctx.moveTo(nodo.x, nodo.y);
ctx.lineTo(conexion.x, conexion.y);
ctx.stroke();
});
});
let nodoDesc; // Descripción del nodo
// Dibujamos los nodos
nnodes.forEach(nodo => {
ctx.beginPath();
ctx.arc(nodo.x, nodo.y, nodeRadius, 0, 2 * Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.stroke();
ctx.font = '12px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
nodoDesc = "N" + nodo.id + " delay " + Math.floor(nodo.delay);
ctx.fillText(nodoDesc, nodo.x, nodo.y + 5);
});
}
// Función de calback para generar la red de manera aleatoria
btnCNet.onclick = () => {
// Generar red de nodos con congestión creada de manera aleatoria redAleatoria
// Cada nodo tendrá un delay aleatorio para simular el envío de paquetes de datos
redAleatoria = crearRedAleatoriaConCongestion(numNodos, nodeConnect);
// Limpiamos el canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dibujar la red que hemos generado
drawNet(redAleatoria);
}
Si vamos a enviar información por una red que no nos da muchas garantías como internet, nos interesa implementar este tipo de estrategias.
Por ejemplo, si tenemos que enviar paquetes de datos, vamos a tratar de enviarlos por el camino más corto teniendo en cuenta los retardos en cada nodo.
Para eso vamos a utilizar el algoritmo de Dijkstra.
Y para conseguirlo hay que hacer algunas modificaciones al código.
Necesitaremos añadir un botón para asociar la función de callback que calcula el camino mínimo.
Lo añadimos al contenedor de controles:
<div class="controls">
<button id="btnCNet">Generar Red</button>
<button id="btnMinPath">Calcular ruta</button>
</div>
En css no hay que hacer nada, pero fíjate que el nuevo botón tiene estilo nada más llegar.
Vamos a utilizar una implementación del algoritmo que funciona pasándole una red de nodos, el origen y el destino.
Y esta implementación la vamos a poner en un fichero aparte, así la podemos reutilizar para otras implementaciones.
El código es el siguiente:
function dijkstraConRetardos(red, origen, destino) {
const distancia = {}; // Almacena la distancia mínima desde el nodo origen hasta cada nodo
const anterior = {}; // Almacena el nodo anterior en la ruta mínima desde el nodo origen hasta cada nodo
const nodosNoVisitados = new Set(); // Conjunto de nodos no visitados
// Inicializar las distancias a cada nodo como infinito y el nodo anterior como null
for (const nodo of red) {
distancia[nodo.id] = Infinity;
anterior[nodo.id] = null;
nodosNoVisitados.add(nodo.id);
}
// La distancia al nodo origen es 0
distancia[origen.id] = 0;
while (nodosNoVisitados.size > 0) {
// Encontrar el nodo no visitado con la distancia mínima
let nodoActual = null;
for (const nodoId of nodosNoVisitados) {
if (nodoActual === null || distancia[nodoId] < distancia[nodoActual]) {
nodoActual = nodoId;
}
}
// Si no se encuentra un nodo actual, salir del bucle
if (nodoActual === null) break;
nodosNoVisitados.delete(nodoActual); // Marcar el nodo actual como visitado
// Actualizar las distancias a los nodos adyacentes al nodo actual
auxNode = red[nodoActual];
for (const { nodo, peso } of auxNode.conexiones) {
const distanciaTotal = distancia[nodoActual] + peso + nodo.delay; // Considerar el retardo en el procesamiento del nodo
if (distanciaTotal < distancia[nodo.id]) {
distancia[nodo.id] = distanciaTotal;
anterior[nodo.id] = nodoActual;
}
}
}
// Reconstruir la ruta mínima desde el nodo destino hasta el nodo origen
const rutaMinima = [];
let nodoActual = destino.id;
while (anterior[nodoActual] !== null) {
rutaMinima.unshift(nodoActual);
nodoActual = anterior[nodoActual];
}
rutaMinima.unshift(origen.id);
// Devolver la ruta mínima como una lista de nodos
return rutaMinima.map(id => red.find(nodo => nodo.id === id));
}
Asegurate de guardarlo en un fichero .js, y de cargarlo en la cabecera del fichero index.html.
<script src="net-06-dijkstra.js" defer></script>
<script src="net-06.js" defer></script>
Y ahora tendremos que hacer algunos cambios en el fichero javascript principal.
- nodoOrigen la utilizaremos para almacenar el nodo origen.
- nodoDestino la utilizaremos para almacenar el nodo destino.
- rutaMinimaConRetardos para almacenar la ruta mínima calculada con la función dijkstraConRetardos.
- pipeRandomWeight para calcular el peso de cada conexión, que de momento va a ser el mismo para todas las conexiones.
let redAleatoria;
let nodoOrigen = 0, nodoDestino = 0;
let rutaMinimaConRetardos;
const nodeRadius = 40;
const numNodos = 5;
const nodeConnect = 2;
const nodeRandomDelay = 1000;
const pipeRandomWeight = 100; // No hay retardo entre nodos 100
También hay que localizar el nuevo botón en el DOM para enlazar después la función de callback correspondiente:
// Localizando elementos en el DOM
const btnCNet = document.getElementById("btnCNet");
const btnMinPath = document.getElementById("btnMinPath");
Tenemos que hacer un cambio en la función para crearRedAleatoriaConCongestion para asegurarnos de que todas las conexiones tienen el mismo peso. Utilizaremos la constante pipeRandomWeight.
// Conectamos los nodos
for (let i = 0; i < numNodos; i++) {
nodoActual = nodos[i];
for (let j = 0; j < numConexiones; j++) {
pickNode = Math.floor(Math.random() * numNodos);
nodoAleatorio = nodos[pickNode];
//peso = Math.random() * pipeRandomWeight; // Peso aleatorio para simular la distancia entre nodos
peso = pipeRandomWeight; // El mismo peso para todas las conexiones
nodoActual.conectar(nodoAleatorio, peso);
}
}
Y ya solo quedaría añadir la función de callback para obtener la ruta mínima:
btnMinPath.onclick = () => {
// Supongamos que tienes una red de nodos llamada redAleatoria y tienes nodos origen y destino
nodoOrigen = redAleatoria[0]; // Nodo de origen
nodoDestino = redAleatoria[numNodos - 1]; // Nodo de destino
// Calcular la ruta mínima entre el nodo origen y el nodo destino utilizando Dijkstra con retrasos
rutaMinimaConRetardos = dijkstraConRetardos(redAleatoria, nodoOrigen, nodoDestino);
console.log("Ruta mínima con retrasos:", rutaMinimaConRetardos);
}
Si te fijas, estamos seleccionando el primer nodo para la ruta como el origen y el último nodo como destino, pero podrían ser otros.
Después obtenemos la ruta mínima teniendo en cuenta solo los retardos en los nodos y mostramos el resultado por la consola.
Tendremos una nuevo botón para calcular el camino mínimo.
Y al pulsar, nos lo muestra por la consola.
Este es el javascript completo (para los vag@s):
// Variables de trabajo
const canvas = document.getElementById('networkCanvas');
const ctx = canvas.getContext('2d');
let redAleatoria;
let nodoOrigen = 0, nodoDestino = 0;
let rutaMinimaConRetardos;
const nodeRadius = 40;
const numNodos = 5;
const nodeConnect = 2;
const nodeRandomDelay = 1000;
const pipeRandomWeight = 100; // No hay retardo entre nodos 100
// Localizando elementos en el DOM
const btnCNet = document.getElementById("btnCNet");
const btnMinPath = document.getElementById("btnMinPath");
// Clase para representar un nodo en el grafo
class Nodo {
constructor(id, x, y, delay) {
this.id = id; // Identificador del nodo
this.x = x; // Coordenada X del nodo
this.y = y; // Coordenada Y del nodo
this.delay = delay; // Retardo del nodo en milisegundos
this.conexiones = []; // Array de conexiones a otros nodos
}
// Método para agregar una conexión desde este nodo a otro nodo con un peso dado
conectar(nodo, peso) {
this.conexiones.push({ nodo, peso });
}
}
// Función para generar una red aleatoria con nodos en diferentes estados de congestión
function crearRedAleatoriaConCongestion(numNodos, numConexiones) {
const nodos = [];
let x = 0, y = 0, delay = 0;
let nodoActual = 0, nodoAleatorio = 0, pickNode = 0, peso = 0;
// Generamos los nodos
for (let i = 0; i < numNodos; i++) {
x = randomNumber(nodeRadius, (canvas.width - nodeRadius)); // Generar coordenada x aleatoria
y = randomNumber(nodeRadius, (canvas.height - nodeRadius)); // Generar coordenada y aleatoria
delay = generarRetardo(); // Retardo aleatorio para simular congestión
nodos.push(new Nodo(i, x, y, delay)); // Generar un nuevo nodo y añadirlo a la lista de nodos de la red
}
// Conectamos los nodos
for (let i = 0; i < numNodos; i++) {
nodoActual = nodos[i];
for (let j = 0; j < numConexiones; j++) {
pickNode = Math.floor(Math.random() * numNodos);
nodoAleatorio = nodos[pickNode];
//peso = Math.random() * pipeRandomWeight; // Peso aleatorio para simular la distancia entre nodos
peso = pipeRandomWeight; // El mismo peso para todas las conexiones
nodoActual.conectar(nodoAleatorio, peso);
}
}
return nodos;
}
// Función para generar un retardo aleatorio entre 0 y 1000 ms
function generarRetardo() {
return Math.random() * nodeRandomDelay;
}
// Generar un número aleatorio dentro de un rango
function randomNumber(min, max) {
return Math.floor(Math.random() * (max - min) + min);
}
// Dibujar la red en el canvas
function drawNet(nnodes) {
// Dibujamos las conexiones entre nodos
nnodes.forEach(nodo => {
nodo.conexiones.forEach(({ nodo: conexion, peso }) => {
ctx.beginPath();
ctx.moveTo(nodo.x, nodo.y);
ctx.lineTo(conexion.x, conexion.y);
ctx.stroke();
});
});
let nodoDesc; // Descripción del nodo
// Dibujamos los nodos
nnodes.forEach(nodo => {
ctx.beginPath();
ctx.arc(nodo.x, nodo.y, nodeRadius, 0, 2 * Math.PI);
ctx.fillStyle = 'blue';
ctx.fill();
ctx.stroke();
ctx.font = '12px Arial';
ctx.fillStyle = 'white';
ctx.textAlign = 'center';
nodoDesc = "N" + nodo.id + " delay " + Math.floor(nodo.delay);
ctx.fillText(nodoDesc, nodo.x, nodo.y + 5);
});
}
// Función de calback para generar la red de manera aleatoria
btnCNet.onclick = () => {
// Generar red de nodos con congestión creada de manera aleatoria redAleatoria
// Cada nodo tendrá un delay aleatorio para simular el envío de paquetes de datos
redAleatoria = crearRedAleatoriaConCongestion(numNodos, nodeConnect);
// Limpiamos el canvas
ctx.clearRect(0, 0, canvas.width, canvas.height);
// Dibujar la red que hemos generado
drawNet(redAleatoria);
}
btnMinPath.onclick = () => {
// Supongamos que tienes una red de nodos llamada redAleatoria y tienes nodos origen y destino
nodoOrigen = redAleatoria[0]; // Nodo de origen
nodoDestino = redAleatoria[numNodos - 1]; // Nodo de destino
// Calcular la ruta mínima entre el nodo origen y el nodo destino utilizando Dijkstra con retrasos
rutaMinimaConRetardos = dijkstraConRetardos(redAleatoria, nodoOrigen, nodoDestino);
console.log("Ruta mínima con retrasos:", rutaMinimaConRetardos);
}
Continuará ...
Si no lo has hecho ya, practica con lo que hemos visto en esta sesión.
- Implementa los ejemplos de esta sesión y guárdalos en la carpeta P5
Tenemos una primera versión de nuestra red, y podemos hacer mejoras para que sea más parecido a lo que podemos encontrar en la vida real.
Jesús Parrado Alameda (jesusgpa)
- Creado a partir del contenido generado por el profesor Jose María Cañas
- Creado a partir del contenido generado por el profesor Juan Gonzalez-Gomez