""" Classe Node : Représentation d'un nœud capteur dans le réseau WSN """ import math import random from config import ( E_ELEC, E_FS, E_MP, D0, E_DA, FIELD_WIDTH, FIELD_HEIGHT, MAX_DISPLACEMENT_PER_ROUND ) class Node: """ Représente un nœud (capteur) dans le réseau WSN. Attributes: node_id (int): Identifiant unique du nœud x, y (float): Position en mètres energy (float): Énergie résiduelle en Joules initial_energy (float): Énergie initiale (pour calcul %) is_alive (bool): État du nœud is_cluster_head (bool): Est-ce un CH? cluster_id (int): ID du cluster auquel le nœud appartient packets_to_send (int): Nombre de paquets en attente """ def __init__(self, node_id, x, y, initial_energy): self.node_id = node_id self.x = x self.y = y self.energy = initial_energy self.initial_energy = initial_energy self.is_alive = True self.is_cluster_head = False self.cluster_id = None self.packets_to_send = 0 self.total_packets_sent = 0 def distance_to(self, other_x, other_y): """Calcule la distance euclidienne vers un point.""" return math.sqrt((self.x - other_x) ** 2 + (self.y - other_y) ** 2) def consume_energy(self, energy_amount): """Consomme de l'énergie et met à jour l'état du nœud.""" self.energy -= energy_amount if self.energy <= 0: self.energy = 0 self.is_alive = False def transmit(self, data_bits, distance): """ Calcule et consomme l'énergie de transmission. Args: data_bits (int): Nombre de bits à transmettre distance (float): Distance jusqu'au récepteur Returns: float: Énergie consommée """ if distance <= D0: # Modèle espace libre energy_tx = E_ELEC * data_bits + E_FS * data_bits * (distance ** 2) else: # Modèle multi-trajet energy_tx = E_ELEC * data_bits + E_MP * data_bits * (distance ** 4) self.consume_energy(energy_tx) return energy_tx def receive(self, data_bits): """ Calcule et consomme l'énergie de réception. Args: data_bits (int): Nombre de bits reçus Returns: float: Énergie consommée """ energy_rx = E_ELEC * data_bits self.consume_energy(energy_rx) return energy_rx def aggregate(self, data_bits): """ Calcule et consomme l'énergie d'agrégation de données. Args: data_bits (int): Nombre de bits agrégés Returns: float: Énergie consommée """ energy_agg = E_DA * data_bits self.consume_energy(energy_agg) return energy_agg def move(self): """ Met à jour la position du nœud avec un déplacement aléatoire. Déplacement max = MAX_DISPLACEMENT_PER_ROUND mètres. Reste dans les limites du champ. """ angle = random.uniform(0, 2 * math.pi) distance = random.uniform(0, MAX_DISPLACEMENT_PER_ROUND) new_x = self.x + distance * math.cos(angle) new_y = self.y + distance * math.sin(angle) # Limiter aux bords du champ self.x = max(0, min(FIELD_WIDTH, new_x)) self.y = max(0, min(FIELD_HEIGHT, new_y)) def reset_for_round(self): """Reset les données de ronde (cluster, CH status, etc.)""" self.is_cluster_head = False self.cluster_id = None self.packets_to_send = 0 def get_energy_percentage(self): """Retourne le pourcentage d'énergie restante.""" if self.initial_energy == 0: return 0 return (self.energy / self.initial_energy) * 100 def __repr__(self): return f"Node({self.node_id}, pos=({self.x:.1f}, {self.y:.1f}), E={self.energy:.4f}J, alive={self.is_alive})"