Vous êtes sur la page 1sur 100

Traduit de Anglais vers Français - www.onlinedoctranslator.

com

OMNeT++
Manuel de simulation
Variante 5.4.1
Copyright © 2016 András Varga et OpenSim Ltd.
Manuel de simulation OMNeT++ –

Chapitres

Contenu v

1 introduction 1

2 Aperçu 3

3 Le langage NED 11

4 Modules simples 49

5 Messages et paquets 123

6 Définitions des messages 133

sept
La bibliothèque de simulation 157

8 Graphiques et visualisation 209

9 Programmes de simulation de construction 267

10 Configuration des simulations 279

11 Exécution de simulations 303

12 Enregistrement et analyse des résultats 323

13 Journal des événements 337

14 Documentation du NED et des messages 341

15 Essais 349

16 Simulation distribuée parallèle 365

17 Personnalisation et extension d'OMNeT++ 375

iii
18 Intégration du noyau de simulation 385

UNE
Référence NED 395

B Grammaire du langage NED 425

C Liaison XML NED 441

ré Fonctions NED 449

E Grammaire des définitions de message 455

F Afficher les balises de chaîne 461

g Définitions des figures 465

H Options de configuration 469

je Formats de fichier de résultats 485

J Format de fichier du journal des événements 495

Les références 501

Indice 504
Manuel de simulation OMNeT++ –

Contenu

Contenu v

1 introduction 1
1.1 Qu'est-ce qu'OMNeT++ ? .................................... 1
1.2 Organisation de ce manuel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

2 Aperçu 3
2.1 Concepts de modélisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
2.1.1 Modules hiérarchiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.2 Types de modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.1.3 Messages, Portails, Liens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.4 Modélisation des transmissions de paquets . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.5 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.1.6 Méthode de description de la topologie . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.2 Programmation des algorithmes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3 Utilisation d'OMNeT++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
2.3.1 Construction et exécution de simulations . . . . . . . . . . . . . . . . . . . . . . . 6
2.3.2 Contenu de la distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8

3 Le langage NED 11
3.1 Présentation du NDE ....................................... 11
3.2 Démarrage rapide NED ...................................... 12
3.2.1 Le réseau .................................... 12
3.2.2 Présentation d'un canal .............................. 14
3.2.3 Les modules simples d'application, de routage et de file d'attente ................ 14
3.2.4 Le module composé de nœuds . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
3.2.5 Assemblage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.3 Modules simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
3.4 Modules composés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
3.5 Canaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21

v
3.6 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
3.6.1 Affectation d'une valeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
3.6.2 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.6.3 volatile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
3.6.4 Unités . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.6.5 Paramètres XML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
3.7 Portes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
3.8 Sous-modules ........................................ 30
3.9 Connexions ........................................ 32
3.9.1 Spécification du canal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
3.9.2 Noms des chaînes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33
3.10Connexions multiples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.10.1Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34
3.10.2Modèles de connexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
3.11 Sous-module paramétrique et types de connexion . . . . . . . . . . . . . . . . . . . . . 37
3.11.1Types de sous-modules paramétriques . . . . . . . . . . . . . . . . . . . . . . . . . . 37
3.11.2 Sous-modules paramétriques conditionnels . . . . . . . . . . . . . . . . . . . . . . 39
3.11.3Types de connexion paramétrique . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.12Annotations de métadonnées (propriétés) . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3.12.1Indices de propriété . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.12.2Modèle de données . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.12.3Remplacer et étendre les valeurs de propriété . . . . . . . . . . . . . . . . . . . 43
3.13 Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.14Forfaits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.14.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.14.2Résolution de nom, importations ............................ 45
3.14.3Résolution de nom avec "like" ........................... 46
3.14.4 Le package par défaut . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

4 Modules simples 49
4.1 Concepts de simulation ................................... 49
4.1.1 Simulation d'événements discrets ............................ 49
4.1.2 La boucle d'événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
4.1.3 Événements et ordre d'exécution des événements dans OMNeT++ . . . . . . . . . . . . . . . 50
4.1.4 Temps de simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
4.1.5 Mise en œuvre de la FES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
4.2 Composants, modules simples, voies . . . . . . . . . . . . . . . . . . . . . . . . 52
4.3 Définition des types de modules simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.3.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
4.3.2 Constructeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.3.3 Initialisation et finalisation . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4.4 Ajout de fonctionnalités à cSimpleModule . . . . . . . . . . . . . . . . . . . . . . . . 58
4.4.1 handleMessage() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.4.2 activité() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.4.3 Comment éviter les variables globales . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.4.4 Réutilisation du code de module via le sous-classement . . . . . . . . . . . . . . . . . . . . . 68
4.5 Accès aux paramètres du module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.5.1 Paramètres volatils et non volatils . . . . . . . . . . . . . . . . . . . . . . 69
4.5.2 Modification de la valeur d'un paramètre . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.5.3 Autres méthodes cPar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5.4 Emulation de tableaux de paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . 71
4.5.5 handleParameterChange() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
4.6 Accès aux portails et aux connexions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.6.1 Objets Gate . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
4.6.2 Connexions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
4.6.3 Le canal de connexion ............................ 77
4.7 Envoi et réception de messages ............................ 78
4.7.1 Messages personnels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
4.7.2 Envoi de messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
4.7.3 Diffusions et retransmissions . . . . . . . . . . . . . . . . . . . . . . . . 80
4.7.4 Envoi différé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
4.7.5 Envoi de messages directs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
4.7.6 Transmissions de paquets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
4.7.7 Recevoir des messages avec activity() . . . . . . . . . . . . . . . . . . . . . . . . 86
4.8 Canaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.8.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.8.2 L'API de canal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
4.8.3 Exemples de canaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
4.9 Arrêt de la simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.9.1 Terminaison normale . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.9.2 Génération d'erreurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
4.10 Machines à états finis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.10.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
4.11Naviguer dans la hiérarchie des modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.11.1 Vecteurs de modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.11.2Identifiants des composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
4.11.3Monter et descendre dans la hiérarchie des modules . . . . . . . . . . . . . . . . . 96
4.11.4Recherche de modules par chemin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
4.11.5 Itération sur les sous-modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
4.11.6Naviguer dans les connexions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.12 Appels directs de méthode entre modules . . . . . . . . . . . . . . . . . . . . . . . . . 98
4.13Création de modules dynamiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.13.1Quand utiliser . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.13.2Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
4.13.3Créer des modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
4.13.4Suppression de modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101
4.13.5Suppression de module et finition() ........................... 101
4.13.6Création de connexions ............................... 102
4.13.7 Suppression de connexions .............................. 103
4.14Signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 103
4.14.1 Considérations de conception et justification . . . . . . . . . . . . . . . . . . . . . . 104
4.14.2Le mécanisme des signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 105
4.14.3Écouter les modifications du modèle . . . . . . . . . . . . . . . . . . . . . . . . . . . 110
4.15Enregistrement de statistiques basées sur le signal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.15.1 Motivation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
4.15.2Déclarer des statistiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112
4.15.3Enregistrement des statistiques pour les signaux enregistrés dynamiquement . . . . . . . . . . . 117
4.15.4Ajout de filtres de résultats et d'enregistreurs par programmation . . . . . . . . . . . . 118
4.15.5 Émission de signaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
4.15.6Écrire des filtres et des enregistreurs de résultats . . . . . . . . . . . . . . . . . . . . . . 120

5 Messages et paquets 123


5.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
5.2 La classe cMessage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
5.2.1 Utilisation de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
5.2.2 Messages dupliqués . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
5.2.3 ID de message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
5.2.4 Infos de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126
5.2.5 Informations sur la dernière arrivée . . . . . . . . . . . . . . . . . . . . . . . 126
5.2.6 Chaîne d'affichage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
5.3 Messages personnels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
5.3.1 Utilisation d'un message comme message personnel . . . . . . . . . . . . . . . . . . . . . . . . 127
5.3.2 Pointeur de contexte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.4 La classe cPacket . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.4.1 Utilisation de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.4.2 Identification du protocole . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
5.4.3 Informations sur la dernière transmission . . . . . . . . . . . . . . . . . . . 129
5.4.4 Encapsulation de paquets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
5.4.5 Comptage de référence . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
5.4.6 Encapsuler plusieurs paquets . . . . . . . . . . . . . . . . . . . . . . . . . . 130
5.5 Joindre des objets à un message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.5.1 Attacher des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
5.5.2 Attacher des paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131

6 Définitions des messages 133


6.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
6.1.1 La première classe de message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
6.2 Messages et paquets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
6.2.1 Définition des messages et des paquets . . . . . . . . . . . . . . . . . . . . . . . . . 134
6.2.2 Types de données de champ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
6.2.3 Valeurs initiales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.2.4 Énumérations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
6.2.5 Tableaux de taille fixe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
6.2.6 Tableaux de taille variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
6.2.7 Classes et structures en tant que champs . . . . . . . . . . . . . . . . . . . . . . . . . . 138
6.2.8 Champs de pointeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
6.2.9 Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
6.2.10Affectation des champs hérités . . . . . . . . . . . . . . . . . . . . . . . . . . 139
6.3 Cours ........................................... 140
6.4 Structures ........................................... 141
6.5 Blocs C++ littéraux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
6.6 Utilisation des types C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 142
6.6.1 Annonce des types au compilateur de messages . . . . . . . . . . . . . . . . . . 142
6.6.2 Mise à disposition des déclarations C++ . . . . . . . . . . . . . . . . . . . . . 143
6.6.3 Assemblage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
6.7 Personnalisation de la classe générée . . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
6.7.1 Personnalisation des noms de méthodes . . . . . . . . . . . . . . . . . . . . . . . . . . . 144
6.7.2 Personnalisation de la classe via l'héritage . . . . . . . . . . . . . . . . . . . . . 145
6.7.3 Champs abstraits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
6.8 Utilisation des classes de conteneur standard pour les champs . . . . . . . . . . . . . . . . . . . . . 147
6.8.1 Typedefs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
6.8.2 Champs abstraits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
6.9 Espaces de noms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.9.1 Déclaration d'un espace de noms . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
6.9.2 Blocs C++ et espace de noms . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
6.9.3 Annonces de type et espace de noms . . . . . . . . . . . . . . . . . . . . . 151
6.10Classes de descripteurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
6.11Résumé . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153

sept
La bibliothèque de simulation 157
7.1 Fondamentaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
7.1.1 Utilisation de la bibliothèque . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 157
7.1.2 La classe de base cObject . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
7.1.3 Itérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
7.1.4 Erreurs d'exécution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
7.2 Journalisation à partir des modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 160
7.2.1 Sortie du journal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
7.2.2 Niveaux de journalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161
7.2.3 Instructions de journal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
7.2.4 Catégories de journaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
7.2.5 Composition et nouvelles lignes . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
7.2.6 Mise en œuvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
7.3 Générateurs de nombres aléatoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
7.3.1 Implémentations RNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
7.3.2 RNG globaux et composants locaux . . . . . . . . . . . . . . . . . . . . . . . 165
7.3.3 Accès aux RNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
7.4 Génération de variables aléatoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
7.4.1 Méthodes des composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 167
7.4.2 Classes de flux de nombres aléatoires . . . . . . . . . . . . . . . . . . . . . . . . 168
7.4.3 Fonctions du générateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
7.4.4 Nombres aléatoires à partir d'histogrammes . . . . . . . . . . . . . . . . . . . . . . . 169
7.4.5 Ajout de nouvelles distributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
7.5 Classes de conteneurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
7.5.1 Classe de file d'attente : cQueue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
7.5.2 Tableau extensible : cArray . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171
7.6 Prise en charge du routage : cTopologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
7.6.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
7.6.2 Utilisation de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 172
7.6.3 Chemins les plus courts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174
7.6.4 Manipulation du graphique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 175
7.7 Correspondance de modèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
7.7.1 cPatternMatcher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
7.7.2 cMatchExpression . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
7.8 Collecte de statistiques récapitulatives et d'histogrammes . . . . . . . . . . . . . . . . . . . . 179
7.8.1 cStdDev . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
7.8.2 cHistogramme . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
7.8.3 cPSquare . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
7.8.4 cKSplit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
7.9 Enregistrement des résultats de la simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
7.9.1 Vecteurs de sortie : cOutVector . . . . . . . . . . . . . . . . . . . . . . . . . . . 187
7.9.2 Scalaires de sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 188
7.10Veilles et instantanés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.10.1Montres de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
7.10.2 Surveillances de lecture-écriture ................................ 190
7.10.3 Montres structurées ................................ 191
7.10.4Montres STL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.10.5Instantanés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
7.10.6Obtenir l'utilisation de la pile de coroutines . . . . . . . . . . . . . . . . . . . . . . . . . . 193
7.11Définition de nouvelles fonctions NED .............................. 194
7.11.1Définir_Fonction_NED() .............................. 194
7.11.2Définir_NED_Math_Fonction() .......................... 198
7.12 Dérivation de nouvelles classes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
7.12.1cObjet ou pas ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 199
7.12.2cMéthodes virtuelles d'objet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 200
7.12.3 Inscription aux cours . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
7.12.4Détails . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 201
7.13Gestion de la propriété des objets . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.13.1 L'arbre de propriété . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 204
7.13.2Gestion de la propriété . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206

8 Graphiques et visualisation 209


8.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 209
8.2 Emplacement du code de visualisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.2.1 La méthode refreshDisplay() . . . . . . . . . . . . . . . . . . . . . . . . . . . 210
8.2.2 Avantages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 211
8.2.3 PourquoirafraîchirAffichage()const? . . . . . . . . . . . . . . . . . . . . . . . . 211
8.3 Animation fluide . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
8.3.1 Notions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 212
8.3.2 Animation fluide ou traditionnelle . . . . . . . . . . . . . . . . . . . . . . . . 212
8.3.3 Le choix de la vitesse d'animation . . . . . . . . . . . . . . . . . . . . . . . . . 213
8.3.4 Prises . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 213
8.3.5 Désactivation des animations intégrées . . . . . . . . . . . . . . . . . . . . . . . . . . 214
8.4 Afficher les chaînes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214
8.4.1 Syntaxe et emplacement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
8.4.2 Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 215
8.4.3 Variables de sous-module . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 216
8.4.4 Balises d'arrière-plan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221
8.4.5 Chaînes d'affichage de connexion . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
8.4.6 Chaînes d'affichage de message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 222
8.4.7 Remplacement de paramètre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
8.4.8 Couleurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 224
8.4.9 Icônes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 225
8.4.10Mise en page . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226
8.4.11Modification des chaînes d'affichage lors de l'exécution . . . . . . . . . . . . . . . . . . . . . . 227
8.5 Bulles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 227
8.6 La toile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
8.6.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 228
8.6.2 Création, accès et affichage des canevas . . . . . . . . . . . . . . . . . . . 229
8.6.3 Classes de figurines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 229
8.6.4 L'arborescence des figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230
8.6.5 Création et manipulation de figures à partir de NED et C++ . . . . . . . . . . . 230
8.6.6 Ordre d'empilement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
8.6.7 Transformations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 231
8.6.8 Afficher/masquer les chiffres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 233
8.6.9 Info-bulle de la figure, objet associé . . . . . . . . . . . . . . . . . . . . . . . . 233
8.6.10Spécification des positions, des couleurs, des polices et d'autres propriétés . . . . . . . . . . . 234
8.6.11Figures primitives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 237
8.6.12 Figures composées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
8.6.13Chiffres auto-rafraîchissants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250
8.6.14Figures avec rendus personnalisés . . . . . . . . . . . . . . . . . . . . . . . . . 251
8.7 Visualisation 3D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
8.7.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 251
8.7.2 L'API OMNeT++ pour OpenSceneGraph . . . . . . . . . . . . . . . . . . . . 252
8.7.3 Utilisation de l'OSG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 256
8.7.4 Utilisation d'osgEarth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 262
8.7.5 Ressources de programmation OpenSceneGraph/osgEarth . . . . . . . . . . . . . 265

9 Programmes de simulation de construction 267


9.1 Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 267
9.2 Utiliser opp_makemake et Makefiles . . . . . . . . . . . . . . . . . . . . . . . . . . 268
9.2.1 Options de ligne de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
9.2.2 Utilisation de base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 269
9.2.3 Versions de débogage et de publication . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
9.2.4 Débogage du Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 270
9.2.5 Utilisation de bibliothèques C/C++ externes . . . . . . . . . . . . . . . . . . . . . . . . . 270
9.2.6 Création d'arborescences de répertoires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
9.2.7 Gestion des dépendances . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
9.2.8 Construction hors répertoire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 271
9.2.9 Création de bibliothèques partagées et statiques . . . . . . . . . . . . . . . . . . . . . . 272
9.2.10Constructions récursives . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
9.2.11Personnalisation du Makefile . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 272
9.2.12Projets avec plusieurs arborescences sources . . . . . . . . . . . . . . . . . . . . . . . 273
9.2.13A Exemple multi-annuaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . 273
9.3 Caractéristiques du projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
9.3.1 Qu'est-ce qu'une fonctionnalité de projet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 274
9.3.2 Le programme opp_featuretool . . . . . . . . . . . . . . . . . . . . . . . . . . . 275
9.3.3 Le fichier .oppfeatures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 276
9.3.4 Comment introduire une fonctionnalité de projet . . . . . . . . . . . . . . . . . . . . . . . 276

10 Configuration des simulations 279


10.1Le fichier de configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
10.1.1Un exemple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 279
10.1.2 Syntaxe du fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 280
10.1.3Inclusion de fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
10.2Sections . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
10.2.1La section [Général] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 281
10.2.2Configurations nommées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
10.2.3Section Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 282
10.3 Affectation des paramètres du module .............................. 284
10.3.1Utilisation de modèles génériques ............................. 284
10.3.2Utilisation des valeurs par défaut ............................. 285
10.4Études paramétriques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 286
10.4.1Itérations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.4.2 Variables d'itération nommées . . . . . . . . . . . . . . . . . . . . . . . . . . . . 288
10.4.3 Itération parallèle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
10.4.4Variables prédéfinies, ID d'exécution . . . . . . . . . . . . . . . . . . . . . . . . . . . 290
10.4.5 Expression de contrainte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 291
10.4.6 Répétition d'exécutions avec différentes graines . . . . . . . . . . . . . . . . . . . . . . 291
10.4.7Expérience-Mesure-Réplication . . . . . . . . . . . . . . . . . . . . . 292
10.5Configuration des générateurs de nombres aléatoires . . . . . . . . . . . . . . . . . . . . . 294
10.5.1Nombre de RNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
10.5.2Choix RNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 294
10.5.3 Cartographie RNG . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
10.5.4Sélection de graine automatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 295
10.5.5Configuration manuelle des semences . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
10.6Journalisation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296
10.6.1Filtrage au moment de la compilation .............................. 296
10.6.2Filtrage d'exécution ................................. 297
10.6.3Format du préfixe de journal ................................. 297
10.6.4Configuration de Cmdenv . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 300
10.6.5Configuration de Tkenv et Qtenv . . . . . . . . . . . . . . . . . . . . . . . . . . 301

11 Exécution de simulations 303


11.1Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
11.2 Exécutables de simulation vs bibliothèques . . . . . . . . . . . . . . . . . . . . . . . . . . 303
11.3 Options de ligne de commande . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 303
11.4Options de configuration sur la ligne de commande . . . . . . . . . . . . . . . . . . . . . 304
11.5Spécification des fichiers ini . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 304
11.6Spécification du chemin NED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
11.7Sélection d'une interface utilisateur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 305
11.8Sélection des configurations et des exécutions ........................... 306
11.8.1 Exécuter la syntaxe du filtre ................................. 306
11.8.2 L'option de requête ................................. 306
11.9Chargement de bibliothèques supplémentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 307
11.10Condition d'arrêt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
11.11Contrôle de la sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 308
11.12Débogage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
11.13Débogage des fuites de messages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309
11.14Débogage d'autres problèmes de mémoire . . . . . . . . . . . . . . . . . . . . . . . . . . . 310
11.15Profilage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 311
11.16Point de contrôle ....................................... 311
11.17Utiliser Cmdenv ....................................... 312
11.17.1Exemple de sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 312
11.17.2Sélection d'exécutions, opération par lots . . . . . . . . . . . . . . . . . . . . . . . . 313
11.17.3Mode express. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 313
11.17.4Autres options . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
11.18L'interface utilisateur graphique de Qtenv . . . . . . . . . . . . . . . . . . . . . . . . . . . 315
11.18.1Options de ligne de commande et de configuration . . . . . . . . . . . . . . . . . . 315
11.19L'interface utilisateur graphique Tkenv . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
11.19.1Options de ligne de commande et de configuration . . . . . . . . . . . . . . . . . . 316
11.20Exécuter des campagnes de simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 316
11.20.1L'approche naïve. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 317
11.20.2Utilisation de opp_runall . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 318
11.20.3Exploitation des grappes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 319
11.21Assistance Akaroa : plusieurs réplications en parallèle . . . . . . . . . . . . . . . . . . . 320
11.21.1Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
11.21.2Qu'est-ce qu'Akaroa ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 320
11.21.3Utilisation d'Akaroa avec OMNeT++ . . . . . . . . . . . . . . . . . . . . . . . . . . . 321

12 Enregistrement et analyse des résultats 323


12.1Enregistrement des résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 323
12.1.1Utilisation des signaux et des statistiques déclarées . . . . . . . . . . . . . . . . . . . . . . 323
12.1.2Enregistrement direct des résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
12.2Configuration de la collecte des résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
12.2.1 Noms des fichiers de résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324
12.2.2Activation/désactivation des éléments de résultat . . . . . . . . . . . . . . . . . . . . . . . . 325
12.2.3Sélection des modes d'enregistrement pour les statistiques basées sur le signal . . . . . . . . . . . . 326
12.2.4Période d'échauffement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327
12.2.5 Intervalles d'enregistrement des vecteurs de sortie . . . . . . . . . . . . . . . . . . . . . . . 327
12.2.6Enregistrement des numéros d'événement dans les vecteurs de sortie . . . . . . . . . . . . . . . . . 328
12.2.7Enregistrement des paramètres sous forme de scalaires . . . . . . . . . . . . . . . . . . . . . . . . . . 328
12.2.8 Précision d'enregistrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329
12.3Le format de fichier de résultats OMNeT++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
12.3.1 Fichiers vectoriels de sortie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
12.3.2Fichiers de résultats scalaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 330
12.4 Fichiers de résultats SQLite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
12.5Savetool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
12.5.1Commandes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 331
12.5.2Exemples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 332
12.6 Analyse des résultats . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
12.6.1 L'outil d'analyse dans l'IDE de simulation. . . . . . . . . . . . . . . . . . . . 333
12.6.2 Feuilles de calcul . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 333
12.6.3Utilisation de Python pour l'analyse des résultats . . . . . . . . . . . . . . . . . . . . . . . . 334
12.6.4Utilisation d'autres logiciels . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 334

13 Journal des événements 337


13.1Présentation ........................................ 337
13.2Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 337
13.2.1Nom de fichier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
13.2.2 Intervalles d'enregistrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
13.2.3Modules d'enregistrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
13.2.4Enregistrement des données de message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 338
13.3 Outil Journal des événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
13.3.1Filtre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339
13.3.2 Écho . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 339

14 Documentation du NED et des messages 341


14.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
14.2 Commentaires sur la documentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 341
14.2.1 Commentaires privés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 342
14.2.2Plus d'informations sur le placement des commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . 342
14.3 Se référant à d'autres types de NED et de messages . . . . . . . . . . . . . . . . . . . . . . 343
14.3.1 Liaison automatique . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 343
14.3.2 Liaison Tilde . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
14.4 Disposition et formatage du texte ............................... 344
14.4.1Paragraphes et listes ............................... 344
14.4.2 Balises spéciales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 344
14.4.3Formatage du texte à l'aide de HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . 345
14.4.4Échappement des balises HTML . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
14.5Personnalisation et ajout de pages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
14.5.1Ajout d'une page de titre personnalisée . . . . . . . . . . . . . . . . . . . . . . . . . . . 346
14.5.2Ajout de pages supplémentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 347
14.5.3Incorporation de pages créées en externe . . . . . . . . . . . . . . . . . . . . . 348
14.6Inclusion de fichiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 348

15 Essais 349
15.1Aperçu .......................................... 349
15.1.1Vérification, Validation .............................. 349
15.1.2Tests unitaires, tests de régression . . . . . . . . . . . . . . . . . . . . . . . . . 349
15.2 L'outil opp_test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
15.2.1Présentation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 350
15.2.2 Terminologie . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
15.2.3Tester la syntaxe du fichier .................................. 353
15.2.4Description de l'essai .................................. 353
15.2.5 Génération de code de test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 353
15.2.6Critères de réussite . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 355
15.2.7Étapes de traitement supplémentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357
15.2.8Non résolu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
15.2.9 Synopsis opp_test . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358
15.2.10Rédaction du script de contrôle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
15.3 Essais de fumée . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 359
15.4 Tests d'empreintes digitales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
15.4.1 Calcul d'empreintes digitales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 360
15.4.2 Tests d'empreintes digitales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
15.5Tests unitaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
15.6Tests des modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
15.7Tests statistiques . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 362
15.7.1 Essais de validation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
15.7.2Tests de régression statistique . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363
15.7.3Mise en œuvre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 363

16 Simulation distribuée parallèle 365


16.1Introduction à la simulation d'événements discrets parallèles . . . . . . . . . . . . . . . . . . 365
16.2Évaluation du parallélisme disponible dans un modèle de simulation . . . . . . . . . . . . . . . . 366
16.3 Prise en charge de la simulation distribuée parallèle dans OMNeT++ . . . . . . . . . . . . . . . . 367
16.3.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 367
16.3.2 Exemple de simulation parallèle . . . . . . . . . . . . . . . . . . . . . . . . . . . 368
16.3.3Modules d'espace réservé, portails proxy . . . . . . . . . . . . . . . . . . . . . . . . 369
16.3.4Configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 370
16.3.5Conception du support PDES dans OMNeT++ . . . . . . . . . . . . . . . . . . . . . . 372

17 Personnalisation et extension d'OMNeT++ 375


17.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375
17.2Ajout d'une nouvelle option de configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
17.2.1Enregistrement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 376
17.2.2Lecture de la valeur . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 377
17.3 Auditeurs à vie de simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 378
17.4cÉvénement . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379
17.5Définir un nouveau générateur de nombres aléatoires . . . . . . . . . . . . . . . . . . . . . . 379
17.6Définir un nouveau planificateur d'événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
17.7Définition d'une nouvelle structure de données FES . . . . . . . . . . . . . . . . . . . . . . . . . . . 381
17.8Définition d'un nouvel algorithme d'empreintes digitales . . . . . . . . . . . . . . . . . . . . . . . . . . 381
17.9Définition d'un nouveau gestionnaire scalaire de sortie . . . . . . . . . . . . . . . . . . . . . . . . 381
17.10Définition d'un nouveau gestionnaire de vecteurs de sortie . . . . . . . . . . . . . . . . . . . . . . . . . 381
17.11Définition d'un nouveau gestionnaire de journal des événements . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
17.12Définition d'un nouveau gestionnaire d'instantanés . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
17.13Définition d'un nouveau fournisseur de configuration . . . . . . . . . . . . . . . . . . . . . . . . . 382
17.13.1Aperçu . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
17.13.2La séquence de démarrage. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 382
17.13.3Fournir une classe de configuration personnalisée . . . . . . . . . . . . . . . . . . . . 383
17.13.4Fournir un lecteur personnalisé pour SectionBasedConfiguration . . . . . . . . . 383
17.14 Implémentation d'une nouvelle interface utilisateur . . . . . . . . . . . . . . . . . . . . . . . . . . . 384

18 Intégration du noyau de simulation 385


18.1Architecture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385
18.2Intégrer le noyau de simulation OMNeT++ . . . . . . . . . . . . . . . . . . . . . 386
18.2.1La fonction main() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
18.2.2La fonction simuler() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 387
18.2.3Fournir un objet d'environnement ........................ 389
18.2.4Fournir un objet de configuration ........................ 390
18.2.5Chargement des fichiers NED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 391
18.2.6Comment supprimer les fichiers NED ........................... 391
18.2.7 Affectation des paramètres du module .......................... 391
18.2.8 Extraction des statistiques du modèle ...................... 392
18.2.9La boucle de simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 393
18.2.10Simulations multiples et coexistantes . . . . . . . . . . . . . . . . . . . . . . . . . 393
18.2.11Installation d'un planificateur personnalisé . . . . . . . . . . . . . . . . . . . . . . . . . . 394
18.2.12Programmes multithread . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394

UNE
Référence NED 395
A.1 Syntaxe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
A.1.1 Extension du nom de fichier NED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
A.1.2 Codage de fichier NED . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
A.1.3 Mots réservés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 395
A.1.4 Identifiants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
A.1.5 Sensibilité à la casse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
A.1.6 Littéraux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
A.1.7 Commentaires . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
A.1.8 Grammaire . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 396
A.2 Définitions intégrées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 397
A.3 Forfaits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
A.3.1 Déclaration de paquet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 398
A.3.2 Structure des répertoires, package.ned . . . . . . . . . . . . . . . . . . . . . . . . 398
A.4 Composants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
A.4.1 Modules simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
A.4.2 Modules composés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 399
A.4.3 Réseaux ...................................... 399
A.4.4 Canaux ...................................... 400
A.4.5 Interfaces des modules ................................. 400
A.4.6 Interfaces de canaux . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 400
A.4.7 Résolution de la classe d'implémentation C++ . . . . . . . . . . . . . . . . . . . . 401
A.4.8 Propriétés . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 401
A.4.9 Paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403
A.4.10Affectations de modèles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 404
A.4.11Portes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 405
A.4.12Sous-modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 406
A.4.13Connexions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 407
A.4.14Connexions conditionnelles et en boucle, groupes de connexion . . . . . . . . . . . 411
A.4.15Types internes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
A.4.16Unicité du nom . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 411
A.4.17Ordre d'affectation des paramètres . . . . . . . . . . . . . . . . . . . . . . . . . . . 412
A.4.18Résolution de nom de type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414
A.4.19Résolution des types paramétriques . . . . . . . . . . . . . . . . . . . . . . . . . 414
A.4.20Implémentation d'une interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
A.4.21 Héritage . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 417
A.4.22 Ordre de construction du réseau . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418
A.5 Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
A.5.1 Constantes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
A.5.2 Opérateurs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 419
A.5.3 Paramètres de référencement et variables de boucle . . . . . . . . . . . . . . . . . . 420
A.5.4 Lenom de typeOpérateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 420
A.5.5 LeindiceOpérateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
A.5.6 Leexiste()Opérateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
A.5.7 Letaille de()Opérateur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
A.5.8 Fonctions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 421
A.5.9 Unités de mesure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 422

B Grammaire du langage NED 425

C Liaison XML NED 441

ré Fonctions NED 449


D.1 Catégorie "conversion": .................................. 449
D.2 Catégorie "mathématiques" : . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 449
D.3 Catégorie "divers": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 450
D.4 Catégorie "nd": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
D.5 Catégorie "aléatoire/continu": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 451
D.6 Catégorie "aléatoire/discret": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
D.7 Catégorie "chaînes": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 452
D.8 Catégorie "unités": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
D.9 Catégorie "unités/conversion": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 453
D.10Catégorie "xml": . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 454

Grammaire des définitions des messages E 455

F Afficher les balises de chaîne 461


F.1 Étiquettes de chaîne d'affichage de module et de connexion . . . . . . . . . . . . . . . . . . . . . . 461
F.2 Balises de chaîne d'affichage de message . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463

g Définitions des figures 465


G.1 Types de figures intégrées . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
G.2 Types d'attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 465
G.3 Attributs des figures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 467

H Options de configuration 469


H.1 Options de configuration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 469
H.2 Variables prédéfinies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482

je Formats de fichier de résultats 485


I.1 Fichiers de résultats natifs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . I.1.1 485
Version . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Exécutez la 486
I.1.2 déclaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Les 486
I.1.3 attributs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Paramètres des 487
I.1.4 modules . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Données 487
I.1.5 scalaires. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Déclaration de 488
I.1.6 vecteur. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Données 488
I.1.7 vectorielles. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . En-tête 489
I.1.8 d'index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Données 489
I.1.9 d'index . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489
I.1.10 Objet Statistiques .................................. 490
I.1.11 Champ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 490
I.1.12 Casier d'histogramme ................................... 491
I.2 Fichiers de résultats SQLite .................................... 492

J Format de fichier du journal des événements 495


J.1 Types d'entrée pris en charge et leurs attributs . . . . . . . . . . . . . . . . . . . . . . 496
Les références 501

Indice 504
Manuel de simulation OMNeT++ – Introduction

Chapitre 1

introduction

1.1 Qu'est-ce qu'OMNeT++ ?

OMNeT++ est un cadre de simulation de réseau à événements discrets modulaire orienté objet. Il a une
architecture générique, il peut donc être (et a été) utilisé dans divers domaines problématiques :

• modélisation des réseaux de communication filaires et sans fil


• modélisation de protocole

• modélisation des réseaux de file d'attente

• modélisation de multiprocesseurs et autres systèmes matériels distribués


• validation des architectures matérielles
• évaluer les aspects de performance des systèmes logiciels complexes
• en général, la modélisation et la simulation de tout système où l'approche par événements discrets
est appropriée et peut être facilement mappée en entités communiquant en échangeant des
messages.

OMNeT++ lui-même n'est pas un simulateur de quelque chose de concret, mais fournit plutôt une infrastructure et des
outils pour l'écriture simulations. L'un des ingrédients fondamentaux de cette infrastructure est une architecture de
composants pour les modèles de simulation. Les modèles sont assemblés à partir de composants réutilisables appelés
modules. Les modules bien écrits sont vraiment réutilisables et peuvent être combinés de différentes manières comme
les blocs LEGO.

Les modules peuvent être connectés les uns aux autres via des portes (d'autres systèmes les appelleraient des ports) et
combinés pour former des modules composés. La profondeur d'imbrication des modules n'est pas limitée. Les modules
communiquent par transmission de messages, où les messages peuvent transporter des structures de données
arbitraires. Les modules peuvent transmettre des messages le long de chemins prédéfinis via des portes et des
connexions, ou directement vers leur destination ; ce dernier est utile pour les simulations sans fil, par exemple. Les
modules peuvent avoir des paramètres qui peuvent être utilisés pour personnaliser le comportement du module et/ou
pour paramétrer la topologie du modèle. Les modules au niveau le plus bas de la hiérarchie des modules sont appelés
modules simples et ils encapsulent le comportement du modèle. Des modules simples sont programmés en C++ et
utilisent la bibliothèque de simulation.

Les simulations OMNeT++ peuvent être exécutées sous diverses interfaces utilisateur. Les interfaces utilisateur
graphiques et animées sont très utiles à des fins de démonstration et de débogage, et les interfaces utilisateur de ligne
de commande sont idéales pour l'exécution par lots.

1
Manuel de simulation OMNeT++ – Introduction

Le simulateur ainsi que les interfaces utilisateur et les outils sont hautement portables. Ils sont testés sur les systèmes
d'exploitation les plus courants (Linux, Mac OS/X, Windows), et ils peuvent être compilés prêts à l'emploi ou après des
modifications insignifiantes sur la plupart des systèmes d'exploitation de type Unix.

OMNeT++ prend également en charge la simulation distribuée parallèle. OMNeT++ peut utiliser plusieurs
mécanismes de communication entre les partitions d'une simulation distribuée parallèle, par exemple MPI ou des
canaux nommés. L'algorithme de simulation parallèle peut facilement être étendu ou de nouveaux peuvent être
ajoutés. Les modèles n'ont besoin d'aucune instrumentation spéciale pour fonctionner en parallèle - c'est juste
une question de configuration. OMNeT++ peut même être utilisé pour la présentation en classe d'algorithmes de
simulation parallèle, car les simulations peuvent être exécutées en parallèle même sous l'interface graphique qui
fournit des informations détaillées sur ce qui se passe.
OMNEST est la version commerciale d'OMNeT++. OMNeT++ est gratuit uniquement pour une utilisation
académique et à but non lucratif ; à des fins commerciales, il faut obtenir des licences OMNEST auprès de
Simulcraft Inc.

1.2 Organisation de ce manuel

Le manuel est organisé comme suit :

• Les chapitres 1 et 2 contiennent des éléments d'introduction.


• Le deuxième groupe de chapitres, 3, 4 et 7 sont le guide de programmation. Ils présentent le langage
NED, décrivent les concepts de simulation et leur implémentation dans OMNeT++, expliquent
comment écrire des modules simples et décrivent la bibliothèque de classes.

• Les chapitres 8 et 14 expliquent comment personnaliser les graphiques du réseau et comment écrire des
commentaires de code source NED à partir desquels la documentation peut être générée.

• Les chapitres 9, 10, 11 et 12 traitent de questions pratiques telles que la construction et l'exécution de simulations
et l'analyse des résultats, et décrivent les outils fournis par OMNeT++ pour prendre en charge ces tâches.

• Le chapitre 16 est consacré au support de l'exécution distribuée.


• Les chapitres 17 et 18 expliquent l'architecture et les éléments internes d'OMNeT++, ainsi que les moyens de
l'étendre et de l'intégrer dans des applications plus importantes.

• Les annexes fournissent une référence sur le langage NED, les options de configuration, les formats de
fichiers et d'autres détails.

2
Manuel de simulation OMNeT++ – Présentation

Chapitre 2

Aperçu

2.1 Concepts de modélisation

Un modèle OMNeT++ se compose de modules qui communiquent avec la transmission de messages. Les modules
actifs sont appelésmodules simples; ils sont écrits en C++, en utilisant la bibliothèque de classes de simulation. Les
modules simples peuvent être regroupés enmodules composéset ainsi de suite; le nombre de niveaux
hiérarchiques est illimité. L'ensemble du modèle, appelé réseau dans OMNeT++, est lui-même un module
composé. Les messages peuvent être envoyés via des connexions qui s'étendent sur des modules ou directement
à d'autres modules. Le concept de modules simples et composés est similaire aux modèles DEVS atomiques et
couplés.
Dans la Fig. 2.1, les cases représentent les modules simples (fond gris) et les modules composés. Les
flèches reliant les petites cases représentent les connexions et les portes.

Réseau
Modules simples

Module composé

Figure 2.1 : Modules simples et composés

Les modules communiquent avec des messages qui peuvent contenir des données arbitraires, en plus des attributs
habituels tels qu'un horodatage. Les modules simples envoient généralement des messages via des portes, mais il est
également possible de les envoyer directement à leurs modules de destination. Les portes sont les interfaces d'entrée et
de sortie des modules : les messages sont envoyés via des portes de sortie et arrivent via des portes d'entrée. Une porte
d'entrée et une porte de sortie peuvent être liées par une connexion. Les connexions sont créées au sein d'un seul
niveau de hiérarchie de modules ; à l'intérieur d'un module composé, des portes correspondantes de deux sous-
modules, ou une porte d'un sous-module et une porte du module composé peuvent être connectées. Les connexions
couvrant des niveaux hiérarchiques ne sont pas autorisées, car elles

3
Manuel de simulation OMNeT++ – Présentation

gêner la réutilisation du modèle. En raison de la structure hiérarchique du modèle, les messages voyagent généralement
à travers une chaîne de connexions, commençant et arrivant dans des modules simples. Les modules composés agissent
comme des "boîtes en carton" dans le modèle, relayant de manière transparente les messages entre leur domaine
intérieur et le monde extérieur. Des paramètres tels que le délai de propagation, le débit de données et le taux d'erreur
binaire peuvent être affectés aux connexions. On peut également définir des types de connexion avec des propriétés
spécifiques (appelées canaux) et les réutiliser à plusieurs endroits. Les modules peuvent avoir des paramètres. Les
paramètres sont principalement utilisés pour transmettre des données de configuration à des modules simples et pour
aider à définir la topologie du modèle. Les paramètres peuvent prendre des valeurs de chaîne, numériques ou
booléennes. Comme les paramètres sont représentés comme des objets dans le programme, les paramètres - en plus de
maintenir des constantes - peuvent agir de manière transparente comme des sources de nombres aléatoires, les
distributions réelles étant fournies avec la configuration du modèle. Ils peuvent inviter interactivement l'utilisateur à
entrer la valeur, et ils peuvent également contenir des expressions faisant référence à d'autres paramètres. Les modules
composés peuvent transmettre des paramètres ou des expressions de paramètres à leurs sous-modules.

OMNeT++ fournit des outils efficaces permettant à l'utilisateur de décrire la structure du système réel. Certaines
des principales caractéristiques sont les suivantes :

• modules imbriqués hiérarchiquement

• les modules sont des instances de types de modules

• les modules communiquent avec des messages via des canaux


• flparamètres de module flexibles

• langage de description de la topologie

2.1.1 Modules hiérarchiques

Un modèle OMNeT++ se compose de modules hiérarchiquement imbriqués qui communiquent en se


transmettant des messages. Les modèles OMNeT++ sont souvent appelésréseaux. Le module de niveau supérieur
est lemodule système. Le module système contientsous-modules qui peuvent également contenir eux-mêmes des
sous-modules (Fig. 2.1). La profondeur d'imbrication des modules est illimitée, ce qui permet à l'utilisateur de
refléter la structure logique du système réel dans la structure du modèle.
La structure du modèle est décrite dans le langage NED d'OMNeT++.
Les modules qui contiennent des sous-modules sont appelésmodules composés, par opposition àmodules
simplesau niveau le plus bas de la hiérarchie des modules. Les modules simples contiennent les algorithmes du
modèle. L'utilisateur implémente les modules simples en C++, en utilisant la bibliothèque de classes de simulation
OMNeT++.

2.1.2 Types de modules

Les modules simples et composés sont des instances detypes de modules. Lors de la description du modèle,
l'utilisateur définit les types de modules ; les instances de ces types de modules servent de composants pour des
types de modules plus complexes. Enfin, l'utilisateur crée le module système en tant qu'instance d'un type de
module préalablement défini ; tous les modules du réseau sont instanciés en tant que sous-modules et sous-sous-
modules du module système.
Lorsqu'un type de module est utilisé comme bloc de construction, peu importe qu'il s'agisse d'un
module simple ou composé. Cela permet à l'utilisateur de diviser un module simple en plusieurs
modules simples intégrés dans un module composé, ou vice versa, pour agréger les fonctionnalités
d'un module composé en un seul module simple, sans affecter les utilisateurs existants du type de
module.

4
Manuel de simulation OMNeT++ – Présentation

Les types de modules peuvent être stockés dans des fichiers séparément du lieu de leur utilisation réelle. Cela signifie
que l'utilisateur peut regrouper les types de modules existants et créerbibliothèques de composants. Cette fonctionnalité
sera abordée plus tard, au chapitre 11.

2.1.3 Messages, Portails, Liens

Les modules communiquent en échangeant messages. Dans une simulation réelle, les messages peuvent représenter
des trames ou des paquets dans un réseau informatique, des emplois ou des clients dans un réseau de file d'attente ou
d'autres types d'entités mobiles. Les messages peuvent contenir des structures de données arbitrairement complexes.
Des modules simples peuvent envoyer des messages soit directement à leur destination, soit le long d'un chemin
prédéfini, via des portes et des connexions.

Le "temps de simulation locale" d'un module avance lorsque le module reçoit un message. Le message peut
provenir d'un autre module ou du même module (auto-messages sont utilisés pour implémenter des
temporisateurs).
portes sont les interfaces d'entrée et de sortie des modules ; les messages sont envoyés par les portes de
sortie et arrivent par les portes d'entrée.
Chaque lien (aussi appelé lien) est créé au sein d'un seul niveau de la hiérarchie des modules : au sein
d'un module composé, on peut connecter les portes correspondantes de deux sous-modules, ou une
porte d'un sous-module et une porte du module composé (Fig. 2.1).
En raison de la structure hiérarchique du modèle, les messages voyagent généralement à travers une série de
connexions, commençant et arrivant dans des modules simples. Les modules composés agissent comme des
"boîtes en carton" dans le modèle, relayant de manière transparente les messages entre leur domaine intérieur et
le monde extérieur.

2.1.4 Modélisation des transmissions de paquets

Pour faciliter la modélisation des réseaux de communication, les connexions peuvent être utilisées pour
modéliser des liens physiques. Les connexions prennent en charge les paramètres suivants :débit de données,
délai de propagation, le taux d'erreur binaire et taux d'erreur sur les paquets, et peut être désactivé. Ces
paramètres et les algorithmes sous-jacents sont encapsulés danscanaliser objets. L'utilisateur peut paramétrer les
types de canaux fournis par OMNeT++, et également en créer de nouveaux.
Lorsque des débits de données sont utilisés, un objet paquet est par défaut livré au module cible au
moment de la simulation qui correspond à la fin de la réception du paquet. Ce comportement n'étant
pas adapté à la modélisation de certains protocoles (par exemple Ethernet semi-duplex), OMNeT++
offre la possibilité au module cible de spécifier qu'il souhaite que l'objet paquet lui soit livré au début
de la réception du paquet.

2.1.5 Paramètres

Les modules peuvent avoir des paramètres. Les paramètres peuvent être affectés soit dans les fichiers NED, soit dans le
fichier de configurationomnetpp.ini.

Les paramètres peuvent être utilisés pour personnaliser le comportement d'un module simple et pour paramétrer la topologie
du modèle.

Les paramètres peuvent prendre des valeurs de chaîne, numériques ou booléennes, ou peuvent contenir des arbres de données
XML. Les valeurs numériques incluent des expressions utilisant d'autres paramètres et appelant des fonctions C, des variables
aléatoires de différentes distributions et des valeurs saisies de manière interactive par l'utilisateur.

5
Manuel de simulation OMNeT++ – Présentation

Les paramètres à valeur numérique peuvent être utilisés pour construire des topologies de manière flexible. Dans
un module composé, les paramètres peuvent définir le nombre de sous-modules, le nombre de portes et la
manière dont les connexions internes sont établies.

2.1.6 Méthode de description de la topologie

L'utilisateur définit la structure du modèle dans les descriptions du langage NED (Network
Description). Le langage NED sera discuté en détail dans le chapitre 3.

2.2 Programmation des algorithmes

Les modules simples d'un modèle contiennent des algorithmes sous forme de fonctions C++. La flexibilité et la puissance
totales du langage de programmation peuvent être utilisées, prises en charge par la bibliothèque de classes de
simulation OMNeT++. Le programmeur de simulation peut choisir entre une description axée sur les événements et une
description de style de processus, et utiliser librement des concepts orientés objet (héritage, polymorphisme, etc.) et des
modèles de conception pour étendre les fonctionnalités du simulateur.

Les objets de simulation (messages, modules, files d'attente, etc.) sont représentés par des classes C++. Ils ont été
conçus pour fonctionner ensemble efficacement, créant un puissant cadre de programmation de simulation. Les
classes suivantes font partie de la bibliothèque de classes de simulation :

• module, porte, paramètre, canal

• message, paquet

• classes de conteneur (par exemple file d'attente, tableau)

• cours de collecte de données

• classes statistiques et d'estimation de distribution (histogrammes, P2 algorithme de calcul des


quantiles, etc.)

Les classes sont également spécialement instrumentées, permettant de parcourir les objets d'une simulation en cours
d'exécution et d'afficher des informations à leur sujet telles que le nom, le nom de la classe, les variables d'état ou le
contenu. Cette fonctionnalité permet de créer une interface graphique de simulation où tous les éléments internes de la
simulation sont visibles.

2.3 Utiliser OMNeT++

2.3.1 Construire et exécuter des simulations

Cette section fournit des informations sur le travail avec OMNeT++ dans la pratique. Des questions telles que les fichiers
modèles et la compilation et l'exécution de simulations sont abordées.

Un modèle OMNeT++ se compose des parties suivantes :

• Description(s) de la topologie du langage NED (.ned fifichiers) qui décrivent la structure du module avec des paramètres,
des portes, etc. Les fichiers NED peuvent être écrits à l'aide de n'importe quel éditeur de texte, mais l'IDE OMNeT++
fournit un excellent support pour l'édition graphique et textuelle bidirectionnelle.

6
Manuel de simulation OMNeT++ – Présentation

• Les définitions de messages (.msg files) qui permettent de définir des types de messages et d'y ajouter des
champs de données. OMNeT++ traduira les définitions de message en classes C++ complètes.

• Sources de modules simples. Ce sont des fichiers C++, avec .h/.ccsuffixe.

Le système de simulation fournit les composants suivants :

• Noyau de simulation. Celui-ci contient le code qui gère la simulation et la bibliothèque de classes de
simulation. Il est écrit en C++, compilé dans une bibliothèque partagée ou statique.

• Les interfaces des utilisateurs. Les interfaces utilisateur OMNeT++ sont utilisées dans l'exécution de la simulation,
pour faciliter le débogage, la démonstration ou l'exécution par lots des simulations. Ils sont écrits en C++,
compilés dans des bibliothèques.

Les programmes de simulation sont construits à partir des composants ci-dessus. D'abord, .msg fiLes fichiers sont traduits en
code C++ à l'aide deopp_msgc.programme. Ensuite, toutes les sources C++ sont compilées et liées au noyau de simulation et à
une bibliothèque d'interface utilisateur pour former un exécutable de simulation ou une bibliothèque partagée. Les fichiers NED
sont chargés dynamiquement dans leurs formes de texte d'origine lorsque le programme de simulation démarre.

Exécution de la simulation et analyse des résultats

La simulation peut être compilée en tant qu'exécutable de programme autonome ou en tant que bibliothèque partagée
à exécuter à l'aide d'OMNeT++.opp_runutilitaire. Lorsque le programme est lancé, il lit d'abord les fichiers NED, puis le
fichier de configuration généralement appeléomnetpp.ini.Le fichier de configuration contient des paramètres qui
contrôlent la manière dont la simulation est exécutée, les valeurs des paramètres du modèle, etc. Le fichier de
configuration peut également prescrire plusieurs exécutions de simulation ; dans le cas le plus simple, ils seront exécutés
par le programme de simulation les uns après les autres.

La sortie de la simulation est écrite dans des fichiers de résultats : fichiers vectoriels de sortie, fichiers
scalaires de sortie et éventuellement les propres fichiers de sortie de l'utilisateur. OMNeT++ contient un
environnement de développement intégré (IDE) qui fournit un environnement riche pour analyser ces
fichiers. Les fichiers de sortie sont des fichiers texte orientés ligne qui permettent de les traiter avec une
variété d'outils et de langages de programmation, y compris Matlab, GNU R, Perl, Python et des tableurs.

Les interfaces des utilisateurs

L'objectif principal des interfaces utilisateur est de rendre les éléments internes du modèle visibles pour l'utilisateur, de
contrôler l'exécution de la simulation et éventuellement de permettre à l'utilisateur d'intervenir en modifiant des
variables/objets à l'intérieur du modèle. Ceci est très important dans la phase de développement/débogage du projet de
simulation. Tout aussi important, une expérience pratique permet à l'utilisateur de se faire une idée du comportement
du modèle. L'interface utilisateur graphique peut également être utilisée pour démontrer le fonctionnement d'un
modèle.

Le même modèle de simulation peut être exécuté avec différentes interfaces utilisateur, sans modification des fichiers de
modèle eux-mêmes. L'utilisateur testerait et déboguerait généralement la simulation avec une interface utilisateur
graphique puissante, et l'exécuterait finalement avec une interface utilisateur simple et rapide qui prend en charge
l'exécution par lots.

sept
Manuel de simulation OMNeT++ – Présentation

Bibliothèques de composants

Les types de modules peuvent être stockés dans des fichiers séparés du lieu de leur utilisation réelle, permettant à l'utilisateur de
regrouper les types de modules existants et de créer des bibliothèques de composants.

Programmes de simulation autonomes universels

Un exécutable de simulation peut stocker plusieurs modèles indépendants qui utilisent le même ensemble de
modules simples. L'utilisateur peut spécifier dans le fichier de configuration quel modèle doit être exécuté. Cela
permet de créer un grand exécutable contenant plusieurs modèles de simulation et de le distribuer en tant
qu'outil de simulation autonome. La flexibilité du langage de description de la topologie prend également en
charge cette approche.

2.3.2 Contenu de la distribution

Une installation OMNeT++ contient les sous-répertoires suivants. Selon la plate-forme, des répertoires
supplémentaires peuvent également être présents, contenant des logiciels fournis avec OMNeT++.)
Le système de simulation lui-même :

omnetpp/ Répertoire racine OMNeT++


poubelle/ Exécutables OMNeT++
inclure/ fichiers d'en-tête pour les fichiers de bibliothèque de
lib/ modèles de simulation
images/ icônes et arrière-plans pour les manuels graphiques
doc/ réseau, les fichiers Lisez-moi, la licence, les API, etc.
ide-guide-de-personnalisation/ comment écrire de nouveaux assistants pour
l'IDE ide-developersguide/ écrire des extensions pour l'IDE Manuel/
manuel en HTML
ned2/ Définition DTD de la syntaxe XML pour les fichiers NED
tuto-tictoc/ introduction à l'utilisation de la référence de
API/ l'API OMNeT++ en HTML
nedxml-api/ Référence API pour la bibliothèque NEDXML
parsim-api/ Référence API pour les sources de la bibliothèque de simulation
src/ parallèle OMNeT++
sim/ noyau de simulation
parsim/ fichiers pour une exécution distribuée
créateur de réseau/fichiers pour la lecture dynamique des fichiers NED
envir/ code commun pour les interfaces utilisateur
cmdenv/ interface utilisateur en ligne de commande
tkenv/ interface utilisateur basée sur Tcl/Tk interface
qtenv/ utilisateur basée sur Qt
nedxml/ Bibliothèque NEDXML, nedtool, bibliothèque
sauver/ d'analyse des résultats opp_msgc
journal des événements/ bibliothèque de traitement des journaux d'événements mise en
disposition/ page graphique pour la bibliothèque commune de graphiques
commun/ réseau
utilitaires/ opp_makemake, opp_test, etc. suite
test/ de tests de régression
coeur/ tests pour la bibliothèque de simulation
anim/ tests pour le graphisme et l'animation

8
Manuel de simulation OMNeT++ – Présentation

distance/ tests pour les distributions intégrées tests pour


faire faire / opp_makemake
...

L'IDE de simulation basé sur Eclipse est dans le idée annuaire.

idée/ EDI de simulation


caractéristiques/ Définitions des fonctionnalités d'Eclipse
plugins/ Plugins IDE (les extensions de l'IDE peuvent être déposées ici)
...

La version Windows d'OMNeT++ contient une redistribution du compilateur MinGW gcc, ainsi qu'une copie
de MSYS qui fournit les outils Unix couramment utilisés dans les Makefiles. Le répertoire MSYS contient
également diverses bibliothèques open source tierces nécessaires pour compiler et exécuter OMNeT++.

outils/ Outils et compilateurs spécifiques à la plate-forme (par exemple MinGW/MSYS sous Windows)

Des exemples de simulations sont dans leéchantillonsannuaire.

échantillons/ répertoires pour les exemples de simulations


aloha/ modélise le protocole Aloha
cqn/ Réseau de file d'attente fermée
...

lecontributionLe répertoire contient du matériel de la communauté OMNeT++.

contribution/ répertoire pour le matériel contribué


Akaroa/ Patch pour compiler akaroa sur les nouveaux systèmes gcc
topologieexport/ Exporter la topologie d'un modèle en runtime
...

9
Manuel de simulation OMNeT++ – Présentation

dix
Manuel de simulation OMNeT++ – Le langage NED

chapitre 3

Le langage NED

3.1 Présentation du NDE

L'utilisateur décrit la structure d'un modèle de simulation dans le langage NED. NED signifie description de
réseau. NED permet à l'utilisateur de déclarer des modules simples, de les connecter et de les assembler en
modules composés. L'utilisateur peut étiqueter certains modules composés commeréseaux; c'est-à-dire des
modèles de simulation autonomes. Les canaux sont un autre type de composant, dont les instances peuvent
également être utilisées dans des modules composés.
Le langage NED possède plusieurs fonctionnalités qui lui permettent de bien s'adapter aux grands projets :

Hiérarchique.La manière traditionnelle de gérer la complexité consiste à introduire des hiérarchies. Dans
OMNeT++, tout module qui serait trop complexe en tant qu'entité unique peut être décomposé
en modules plus petits et utilisé comme module composé.

Basé sur les composants.Les modules simples et les modules composés sont intrinsèquement réutilisables, ce qui
non seulement réduit la copie de code, mais plus important encore, permet aux bibliothèques de
composants (comme INET Framework, MiXiM, Castalia, etc.) d'exister.

Interfaces.Les interfaces de module et de canal peuvent être utilisées comme espace réservé là où normalement
un type de module ou de canal serait utilisé, et le type de module ou de canal concret est
déterminé au moment de l'établissement du réseau par un paramètre. Les types de modules
concrets doivent "implémenter" l'interface qu'ils peuvent remplacer. Par exemple, étant donné
un type de module composé nomméMobileHostcontient unmobilitésous-module du type
Mobilité (où Mobilitéest une interface de module), le type réel demobilitépeut être choisi parmi
les types de modules qui ont mis en œuvreIMobility (RandomWalkMobility, TurtleMobility, etc.)

Héritage.Les modules et les canaux peuvent être sous-classés. Les modules et canaux dérivés peuvent
ajouter de nouveaux paramètres, portes et (dans le cas des modules composés) de nouveaux sous-
modules et connexions. Ils peuvent définir des paramètres existants sur une valeur spécifique et
également définir la taille de porte d'un vecteur de porte. Cela permet, par exemple, de prendre une
GenericTCPCClientApp module et dériver un Application Client FTP de celui-ci en fixant certains
paramètres à une valeur fixe ; ou pour dériver unWebClientHost module composé d'un Hôte de base
module composé en ajoutant un WebClientApp sous-module et le connecter au hérité TCP sous-
module.

Paquets. Le langage NED dispose d'une structure de package de type Java, pour réduire le risque de

11
Manuel de simulation OMNeT++ – Le langage NED

conflits de noms entre différents modèles. NEDPATH (semblable à celui de Java CLASSPATH) a également
été introduit pour faciliter la spécification des dépendances entre les modèles de simulation.

Types intérieurs. Les types de canaux et les types de modules utilisés localement par un module composé peuvent être
défini dans le module composé, afin de réduire la pollution de l'espace de noms.

Annotations de métadonnées. Il est possible d'annoter les types de module ou de voie, les paramètres, les portes
et sous-modules en ajoutant des propriétés. Les métadonnées ne sont pas utilisées directement par le noyau de
simulation, mais elles peuvent contenir des informations supplémentaires pour divers outils, l'environnement
d'exécution ou même pour d'autres modules du modèle. Par exemple, la représentation graphique d'un module
(icône, etc.) ou la chaîne d'invite et l'unité de mesure (milliwatt, etc.) d'un paramètre sont déjà spécifiées en tant
qu'annotations de métadonnées.

REMARQUE: Le langage NED a considérablement changé dans la version 4.0. L'héritage, les interfaces, les
packages, les types internes, les annotations de métadonnées, les portes inout ont tous été ajoutés dans la version
4.0, ainsi que de nombreuses autres fonctionnalités. Étant donné que la syntaxe de base a également changé, les
anciens fichiers NED doivent être convertis à la nouvelle syntaxe. Il existe des outils automatisés à cette fin, de
sorte que l'édition manuelle n'est nécessaire que pour tirer parti des nouvelles fonctionnalités NED.

Le langage NED a une représentation arborescente équivalente qui peut être sérialisée en XML ; c'est-à-dire que
les fichiers NED peuvent être convertis en XML et inversement sans perte de données, y compris les
commentaires. Cela réduit la barrière pour la manipulation par programme des fichiers NED ; par exemple,
extraire des informations, refactoriser et transformer NED, générer NED à partir d'informations stockées dans
d'autres systèmes tels que des bases de données SQL, etc.

REMARQUE: Ce chapitre va expliquer le langage NED progressivement, via des exemples. Un


traitement plus formel et concis se trouve à l'annexe B.

3.2 Démarrage rapide NED

Dans cette section nous introduisons le langage NED via un exemple complet et raisonnablement concret : un
réseau de communication.
Notre réseau hypothétique est constitué de nœuds. Sur chaque nœud, il y a une application en cours d'exécution qui
génère des paquets à intervalles aléatoires. Les nœuds sont également des routeurs eux-mêmes. Nous supposons que
l'application utilise une communication basée sur les datagrammes, de sorte que nous pouvons omettre la couche de
transport du modèle.

3.2.1 Le réseau

Nous définirons d'abord le réseau, puis dans les sections suivantes, nous continuerons à définir les nœuds du
réseau.
Soit la topologie du réseau comme dans la Figure 3.1.

La description NED correspondante ressemblerait à ceci :


//
// Un réseau
//
réseau Réseau
{

12
Manuel de simulation OMNeT++ – Le langage NED

Figure 3.1 : Le réseau

sous-modules:
nœud1 : nœud ;
nœud2 : nœud ;
nœud3 : nœud ;
...
Connexions:
node1.port++ <--> {datarate=100Mbps;} <--> node2.port++; node2.port++
<--> {datarate=100Mbps;} <--> node4.port++; node4.port++ <-->
{datarate=100Mbps;} <--> node6.port++; . . .

Le code ci-dessus définit un type de réseau nommé Réseau. Notez que le langage NED utilise la syntaxe
familière des accolades et "//" pour indiquer les commentaires.

REMARQUE: Les commentaires dans NED rendent non seulement le code source plus lisible,
mais dans l'IDE OMNeT++, ils sont également affichés à divers endroits (info-bulles, aide au
contenu, etc.) et font partie de la documentation extraite des fichiers NED. Le système de
documentation NED, un peu commeJavaDoc ou Doxygène, sera décrit au chapitre 14.

Le réseau contient plusieurs nœuds, nommés nœud1, nœud2, etc. du type de module NED Nœud. Nous
définirons Nœud dans les sections suivantes.
La seconde moitié de la déclaration définit comment les nœuds doivent être connectés. La double flèche
signifie une connexion bidirectionnelle. Les points de connexion des modules sont appelés portes, et les
port++notation ajoute une nouvelle porte à laPort[]vecteur de porte. Les portes et les connexions seront
traitées plus en détail dans les sections 3.7 et 3.9. Les nœuds sont connectés à un canal qui a un débit de
données de 100 Mbps.

REMARQUE: Dans de nombreux autres systèmes, l'équivalent des portes OMNeT++ sont appeléesports.
Nous avons retenu le termeportailpour réduire les collisions avec d'autres utilisations du mot autrement
surchargéPort: port routeur, port TCP, port E/S, etc.

13
Manuel de simulation OMNeT++ – Le langage NED

Le code ci-dessus serait placé dans un fichier nomméNet6.ned.C'est une convention de mettre chaque définition
NED dans son propre fichier et de nommer le fichier en conséquence, mais ce n'est pas obligatoire de le faire.

On peut définir n'importe quel nombre de réseaux dans les fichiers NED, et pour chaque simulation,
l'utilisateur doit spécifier le réseau à configurer. La façon habituelle de spécifier le réseau est de mettre le
réseau option dans la configuration (par défaut l'optionomnetpp.ini file):

[Général]
réseau = Réseau

3.2.2 Présentation d'un canal

Il est fastidieux de devoir répéter le débit de données pour chaque connexion. Heureusement, NED fournit
une solution pratique : on peut créer un nouveau type de canal qui encapsule le paramètre de débit de
données, et ce type de canal peut être défini à l'intérieur du réseau afin qu'il n'encombre pas l'espace de
noms global.
Le réseau amélioré ressemblera à ceci :
//
// Un réseau
//
réseau Réseau
{
les types:
canaliser Cs'étendned.DatarateChannel {
débit de données = 100 Mbps ;
}
sous-modules:
nœud1 : nœud ;
nœud2 : nœud ;
nœud3 : nœud ;
...
Connexions:
nœud1.port++ <--> C <--> nœud2.port++ ;
node2.port++ <--> C <--> node4.port++;
nœud4.port++ <--> C <--> nœud6.port++ ; . . .

Les sections suivantes couvriront les concepts utilisés (types internes, canaux, Canal de débit de données
type intégré, héritage) en détail.

3.2.3 Les modules simples d'application, de routage et de file d'attente

Les modules simples sont les blocs de construction de base pour d'autres modules (composés), désignés
par le Facilemot-clé. Tout comportement actif dans le modèle est encapsulé dansFacilemodules. Le
comportement est défini avec une classe C++ ; Les fichiers NED ne déclarent que l'interface visible de
l'extérieur du module (portes, paramètres).
Dans notre exemple, nous pourrions définir Nœud sous forme de module simple. Cependant, ses fonctionnalités sont
assez complexes (génération de trafic, routage, etc.), il est donc préférable de l'implémenter avec plusieurs plus petits

14
Manuel de simulation OMNeT++ – Le langage NED

types de modules simples que nous allons assembler dans un module composé. Nous aurons un module simple pour la
génération de trafic (application),un pour le routage (Routage),et un pour la mise en file d'attente des paquets à envoyer
(File d'attente).Par souci de brièveté, nous omettons les corps de ces deux derniers dans le code ci-dessous.

FacileApplication
{
paramètres:
entier destAddress ;

...
@display("i=bloc/navigateur"); portes:

saisir dans;
sortir en dehors;

FacileRoutage
{
...
}

FacileFile d'attente
{
...
}

Par convention, les déclarations de module simples ci-dessus vont dans leApp.ned, Routage.nedet
Queue.ned files.

REMARQUE: Notez que les noms de type de module (Application, routage, file d'attente)commencent par une
lettre majuscule, et les noms de paramètres et de portes commencent par une minuscule - c'est la convention de
nommage recommandée. La capitalisation est importante car la langue est sensible à la casse.

Examinons la première déclaration de type de module simple.Applicationa un paramètre appelé


adressedest (d'autres ont été omis pour l'instant), et deux portes nomméesen dehorset danspour envoyer
et recevoir des paquets d'application.
L'argument de @affichage()s'appelle unchaîne d'affichage, et il définit le rendu du module dans les
environnements graphiques ; "je=..."définit l'icône par défaut.
Généralement, @-mots comme @affichagesont appelésPropriétésdans NED, et ils sont utilisés pour annoter divers objets
avec des métadonnées. Les propriétés peuvent être attachées à des fichiers, des modules, des paramètres, des portes,
des connexions et d'autres objets, et les valeurs des paramètres ont une syntaxe très flexible.

3.2.4 Le module composé de nœuds

Maintenant, nous pouvons assemblerApplication, Routageet File d'attentedans le module composéNœud. Un module
composé peut être considéré comme une « boîte en carton » qui regroupe d'autres modules dans une unité plus grande,
qui peut en outre être utilisée comme élément de base pour d'autres modules ; les réseaux sont aussi une sorte de
module composé.

moduleNœud
{

15
Manuel de simulation OMNeT++ – Le langage NED

Figure 3.2 : Le module composé Node

paramètres:
entieradresse;
@display("i=misc/node_vs,gold"); portes:

inoutPort[];
sous-modules:
application : application ;

routage : Routage ;
file d'attente[taille de(port)] : file
d'attente ; Connexions:
routage.localOut --> app.in ;
routage.localIn <-- app.out ; pour
je=0..taille de(port)-1 { routing.out[i] -->
queue[i].in ; routage.in[i] <-- queue[i].out ;
queue[i].line <--> port[i] ;

}
}

Les modules composés, comme les modules simples, peuvent avoir des paramètres et des portes. Notre
Nœud module contient unadresseparamètre, plus unporte vecteurde taille indéterminée, nomméPort.La
taille réelle du vecteur de porte sera déterminée implicitement par le nombre de voisins lorsque nous
créerons un réseau à partir de nœuds de ce type. Le type dePort[]estinout,qui permet des connexions
bidirectionnelles.
Les modules qui composent le module composé sont répertoriés soussous-modules. NotreNœud le type
de module composé a unapplicationet unroutagesous-module, plus unfile d'attente[]vecteur de sous-
modulequi contient unFile d'attentemodule pour chaque port, comme spécifié par [sizeof(port)]. (Il est légal
de se référer à [taillede(port)]car le réseau est construit dans l'ordre descendant, et le nœud est déjà créé et
connecté au niveau du réseau lorsque sa structure de sous-module est construite.)

Dans leConnexionssection, les sous-modules sont connectés les uns aux autres et au module parent. Les flèches
simples sont utilisées pour connecter les portes d'entrée et de sortie, et les doubles flèches connectent les portes
d'entrée, et unpourboucle est utilisée pour connecter leroutagemodule à chacunfile d'attentemodule, et de
connecter la liaison sortante/entrante (ligneporte) de chaque file d'attente au correspondant

16
Manuel de simulation OMNeT++ – Le langage NED

port du module englobant.

3.2.5 Assemblage

Nous avons créé les définitions NED pour cet exemple, mais comment sont-elles utilisées par OMNeT++ ?
Lorsque le programme de simulation est lancé, il charge les fichiers NED. Le programme doit déjà contenir
les classes C++ qui implémentent les modules simples nécessaires,Application, Routageet File d'attente;
leur code C++ fait partie de l'exécutable ou est chargé à partir d'une bibliothèque partagée. Le programme
de simulation charge également la configuration (omnetpp.ini),et en déduit que le modèle de simulation à
exécuter est leRéseauréseau. Ensuite, le réseau est instancié pour la simulation.

Le modèle de simulation est construit selon un mode de précommande descendant. Cela signifie qu'à partir d'un module
système vide, tous les sous-modules sont créés, leurs paramètres et tailles de vecteurs de porte sont affectés, et ils sont
entièrement connectés avant que les éléments internes du sous-module ne soient construits.

***

Dans les sections suivantes, nous passerons en revue les éléments du langage NED et les examinerons plus
en détail.

3.3 Modules simples

Les modules simples sont les composants actifs du modèle. Les modules simples sont définis avec les
Facilemot-clé.
Un exemple de module simple :

FacileFile d'attente
{
paramètres:
entiercapacité;
@display("i=bloc/file"); portes:

saisir dans;
sortir en dehors;

Les deuxparamètreset portesLes sections sont facultatives, c'est-à-dire qu'elles peuvent être omises s'il n'y a pas
de paramètre ou de porte. De plus, leparamètresle mot-clé lui-même est également facultatif ; il peut être omis
même s'il existe des paramètres ou des propriétés.
Notez que la définition NED ne contient aucun code pour définir le fonctionnement du module : cette
partie est exprimée en C++. Par défaut, OMNeT++ recherche les classes C++ du même nom que le
type NED (donc ici,File d'attente).
On peut spécifier explicitement la classe C++ avec le @classerpropriété. Les classes avec des qualificateurs d'espace de
noms sont également acceptées, comme illustré dans l'exemple suivant qui utilise lemalib :: File d'attente classer:

17
Manuel de simulation OMNeT++ – Le langage NED

FacileFile d'attente
{
paramètres:
entiercapacité;
@class(mylib::Queue);
@display("i=bloc/file"); portes:

saisir dans;
sortir en dehors;

S'il existe plusieurs modules dont les classes d'implémentation C++ sont dans le même espace de noms,
une meilleure alternative à @classerest le @espace de noms propriété. L'espace de noms C++ donné avec
@espace de nomssera ajouté au nom de la classe normale. Dans l'exemple suivant, les classes C++ seront
mylib::App, mylib::Routeur et malib :: File d'attente :
@namespace(mylib);

Facile Application {

...
}

Facile Routeur {
...
}

Facile File d'attente {


...
}

Le @espace de noms La propriété peut non seulement être spécifiée au niveau du fichier comme dans l'exemple ci-dessus, mais
également pour les packages. Lorsqu'il est placé dans un fichier appelépackage.ned, l'espace de noms s'appliquera à tous les
composants de ce package et ci-dessous.

Les classes d'implémentation C++ doivent être sous-classées à partir de cModuleSimple classe de
bibliothèque ; le chapitre 4 de ce manuel décrit en détail comment les écrire.
Les modules simples peuvent être étendus (ou spécialisés) via le sous-classement. La motivation du sous-
classement peut être de définir certains paramètres ouverts ou tailles de porte à une valeur fixe (voir 3.6 et 3.7),
ou de remplacer la classe C++ par une autre. Maintenant, par défaut, le type de module NED dérivé sera hériter la
classe C++ à partir de sa base, il est donc important de se rappeler que vous devez écrire @classer si vous voulez
qu'il utilise la nouvelle classe.
L'exemple suivant montre comment spécialiser un module en définissant un paramètre sur une
valeur fixe (et en laissant la classe C++ inchangée) :
Facile File d'attente

{
entier capacité;
...
}

FacileFile d'attente délimitées'étendFile d'attente {

capacité = 10 ;
}

18
Manuel de simulation OMNeT++ – Le langage NED

Dans l'exemple suivant, l'auteur a écrit unFile d'attente de prioritéclasse C++ et veut avoir un type NED
correspondant, dérivé deFile d'attente.Cependant, cela ne fonctionne pas comme prévu :
FacileFile d'attente de priorités'étendFile d'attente// tort! utilise toujours la classe Queue C++ {

La bonne solution est d'ajouter un @classerpour remplacer la classe C++ héritée :


FacileFile d'attente de priorités'étendFile d'attente {

@class(PriorityQueue);
}

L'héritage en général sera discuté dans la section 3.13.

3.4 Modules composés

Un module composé regroupe d'autres modules dans une unité plus grande. Un module composé peut avoir des portes
et des paramètres comme un module simple, mais aucun comportement actif ne lui est associé.1

REMARQUE: Lorsqu'il est tentant d'ajouter du code à un module composé, encapsulez le code dans
un module simple et ajoutez-le en tant que sous-module.

Une déclaration de module composé peut contenir plusieurs sections, toutes facultatives :
moduleHéberger
{
les types:
...
paramètres:
...
portes:
...
sous-modules:
...
Connexions:
...
}

Les modules contenus dans un module composé sont appelés sous-modules, et ils sont listés dans le sous-
modules section. On peut créer des tableaux de sous-modules (c'est-à-dire des vecteurs de sous-modules), et le
type de sous-module peut provenir d'un paramètre.
Les connexions sont répertoriées sous leConnexionspartie de la déclaration. On peut créer des connexions
en utilisant des constructions de programmation simples (boucle, conditionnel). Le comportement de
connexion peut être défini en associant un canal à la connexion ; le type de canal peut également provenir
d'un paramètre.
Les types de module et de voie utilisés uniquement localement peuvent être définis dans leles typessection en tant que
types internes, afin qu'ils ne polluent pas l'espace de noms.

1Bien que la classe C++ d'un module composé puisse être remplacée par le @classerpropriété, il s'agit d'une fonctionnalité qui ne
devrait probablement jamais être utilisée. Encapsulez le code dans un module simple et ajoutez-le en tant que sous-module.

19
Manuel de simulation OMNeT++ – Le langage NED

Les modules composés peuvent être étendus via le sous-classement. L'héritage peut également ajouter de nouveaux
sous-modules et de nouvelles connexions, pas seulement des paramètres et des portes. De plus, on peut se référer à des
sous-modules hérités, à des types hérités, etc. Ce qui n'est pas possible, c'est de "déshériter" ou de modifier des sous-
modules ou des connexions.

Dans l'exemple suivant, nous montrons comment assembler des protocoles communs dans un "stub" pour les hôtes sans fil, et
ajouter des agents utilisateurs via des sous-classes.2

moduleWirelessHostBase {

portes:
saisir radioIn ;
sous-modules:
tcp : TCP ;
IP : IP ;
réseau local sans fil : Ieee80211 ;
Connexions:
tcp.ipOut --> ip.tcpIn ; tcp.ipIn <--
ip.tcpOut ; ip.nicOut++ -->
wlan.ipIn ; ip.nicIn++ <--
wlan.ipOut ; wlan.radioIn <--
radioIn ;
}

moduleHôte sans fil s'étendWirelessHostBase {

sous-modules:
WebAgent : WebAgent ;
Connexions:
webAgent.tcpOut --> tcp.appIn++ ;
webAgent.tcpIn <-- tcp.appOut++ ;
}

leHôte sans fil module composé peut encore être étendu, par exemple avec un port Ethernet :

moduleHôte de bureau s'étendHôte sans fil {

portes:
inoutethg ;
sous-modules:
eth : EthernetNic ;
Connexions:
ip.nicOut++ --> eth.ipIn ; ip.nicIn+
+ <-- eth.ipOut ; eth.phy <--> ethg;

2Les types de module, les noms de porte, etc. utilisés dans l'exemple sont fictifs et ne sont pas basés sur un cadre de modèle réel basé sur
OMNeT ++

20
Manuel de simulation OMNeT++ – Le langage NED

3.5 Canaux

Les canaux encapsulent les paramètres et le comportement associés aux connexions. Les canaux sont
comme de simples modules, dans le sens où il y a des classes C++ derrière eux. Les règles pour trouver la
classe C++ pour un type de canal NED sont les mêmes que pour les modules simples : le nom de classe par
défaut est le nom du type NED sauf s'il y a un @classerpropriété (@espace de noms est également
reconnu), et la classe C++ est héritée lorsque le canal est sous-classé.
Ainsi, le type de canal suivant s'attendrait à unCanal personnaliséClasse C++ à présenter :
canaliser Canal personnalisé// nécessite une classe CustomChannel C++ {

La différence pratique par rapport aux modules est que l'on a rarement besoin d'écrire une classe C++
de canal personnalisée car il existe des types de canaux prédéfinis à partir desquels on peut sous-
classer, héritant de leur code C++. Les types prédéfinis sont :ned.IdealChannel, ned.DelayChannelet
ned.DatarateChannel. ("ne"est le nom du package ; on peut s'en débarrasser en important les types
avec leimport ned.*directif. Les packages et les importations sont décrits dans la section 3.14.)
IdéalChanneln'a pas de paramètres et laisse passer tous les messages sans délai ni effet secondaire. Une
connexion sans objet de voie et une connexion avec unIdéalChannelse comporter de la même manière.
Toujours,IdéalChannela ses utilisations, par exemple lorsqu'un objet canal est requis pour qu'il puisse
transporter une nouvelle propriété ou un nouveau paramètre qui va être lu par d'autres parties du modèle
de simulation.
DelayChannela deux paramètres :

• retardest undoubleparamètre qui représente le délai de propagation du message. Les valeurs


doivent être spécifiées avec une unité de temps (s, mme, nous,etc.)

• désactivéeest un paramètre booléen dont la valeur par défaut estfaux;lorsqu'il est réglé survrai,l'objet canal
supprimera tous les messages.

Canal de débit de donnéesa quelques paramètres supplémentaires par rapport àRetard Canal :

• débit de donnéesest undoubleparamètre qui représente le débit de données du canal. Les valeurs doivent
être spécifiées en bits par seconde ou ses multiples comme unité (bps, kbps, Mbps, Gbps, etc.) Zéro est
traité spécialement et se traduit par une durée de transmission nulle, c'est-à-dire qu'il représente une
bande passante infinie. Zéro est également la valeur par défaut. Le débit de données est utilisé pour
calculer la durée de transmission des paquets.

• breet parsignifie Bit Error Rate et Packet Error Rate, et permettent une modélisation basique des
erreurs. Ils s'attendent à undoubledans le [0, 1]intervalle. Lorsque le canal décide (sur la base de
nombres aléatoires) qu'une erreur s'est produite lors de la transmission d'un paquet, il définit un
indicateur d'erreur dans l'objet paquet. Le module récepteur est censé vérifier le drapeau et rejeter le
paquet comme étant corrompu s'il est défini. Le défautbreet parsont nuls.

REMARQUE: Il n'y a pas de paramètre de canal qui précise si le canal délivre l'objet message au
module destinataire en fin ou en début de réception ; cela est décidé par le code C++ du module
simple cible. Voir lesetDeliverOnReception-Start() méthode de cGate.

L'exemple suivant montre comment créer un nouveau type de canal en se spécialisant Datarate-
Channel :

21
Manuel de simulation OMNeT++ – Le langage NED

canaliser Ethernet100 s'étendned.DatarateChannel {

débit de données = 100 Mbps ;


retard = 100us ;
ber = 1e-10 ;
}

REMARQUE: Les trois types de canaux intégrés sont également utilisés pour les connexions où le type de canal
n'est pas explicitement spécifié.

On peut ajouter des paramètres et des propriétés aux canaux via le sous-classement et modifier ceux qui
existent déjà. Dans l'exemple suivant, nous introduisons le calcul basé sur la distance du délai de
propagation :
canaliser DatarateChannel2 s'étendned.DatarateChannel {

doubledistance @unité(m);
retard = cette.distance / 200000km * 1s;
}

Les paramètres sont principalement destinés à être lus par la classe C++ sous-jacente, mais de nouveaux paramètres peuvent
également être ajoutés en tant qu'annotations à utiliser par d'autres parties du modèle. Par exemple, unCoût Le paramètre peut
être utilisé pour les décisions de routage dans le module de routage, comme illustré dans l'exemple ci-dessous. L'exemple
montre également des annotations utilisant des propriétés (@colonne vertébrale).

canaliser Colonne vertébrales'étendned.DatarateChannel {

@colonne vertébrale;

doublecoût =défaut(1);
}

3.6 Paramètres

Les paramètres sont des variables qui appartiennent à un module. Les paramètres peuvent être utilisés dans la
construction de la topologie (nombre de nœuds, etc.) et pour fournir une entrée au code C++ qui implémente des
modules et des canaux simples.

Les paramètres peuvent être de typedouble, entier, bourdonner, chaîne de caractèreset XML; ils peuvent également être
déclarés volatil. Pour les types numériques, une unité de mesure peut également être spécifiée (@unitépropriété), pour
augmenter la sécurité du type.

Les paramètres peuvent obtenir leur valeur à partir des fichiers NED ou de la configuration (omnetpp.ini).Une
valeur par défaut peut également être donnée (défaut(...)),qui est utilisé si le paramètre n'est pas affecté
autrement.
L'exemple suivant montre un module simple qui a cinq paramètres, dont trois ont des valeurs par
défaut :
FacileApplication
{
paramètres:
chaîne de caractèresprotocole; // protocole à utiliser : "UDP" / "IP" / "ICMP" / ... // adresse de
entierdestAddress ; destination

22
Manuel de simulation OMNeT++ – Le langage NED

double volatilsendInterval @unité(s) =défaut(exponentiel(1s));


// temps entre la génération des paquets
entier volatilpaquetLength @unit(byte) =défaut(100B);
// longueur d'un paquet
entier volatiltimeToLive =défaut(32);
// nombre maximum de sauts de réseau pour survivre
portes:
saisir dans;
sortir en dehors;

3.6.1 Affectation d'une valeur

Les paramètres peuvent obtenir leurs valeurs de plusieurs manières : à partir du code NED, à partir de la
configuration (omnetpp.ini),ou même, interactivement de l'utilisateur. NED permet d'assigner des paramètres à
plusieurs endroits : dans les sous-classes via l'héritage ; dans les définitions de sous-module et de connexion où le
type NED est instancié ; et dans les réseaux et les modules composés qui contiennent directement ou
indirectement le sous-module ou la connexion correspondante.
Par exemple, on pourrait spécialiser ce qui précède Applicationtype de module via héritage avec la définition
suivante :

FacilePingApp s'étendApplication {

paramètres:
protocole = "ICMP/ECHO"
sendInterval = défaut(1s);
longueurpaquet = défaut(64 octets);
}

Cette définition définit la protocole paramètre à une valeur fixe ("ICMP/ECHO"), et modifie les valeurs
par défaut du sendInterval et paquetLongueur paramètres. protocole est maintenant enfermé dans
PingApp, sa valeur ne peut pas être modifiée par d'autres sous-classes ou par d'autres moyens.
sendInterval et paquetLongueur ne sont toujours pas affectés ici, seules leurs valeurs par défaut ont
été écrasées.
Voyons maintenant la définition d'un Héberger module composé qui utilise PingApp comme sous-module :

moduleHéberger
{
sous-modules:
ping : PingApp {
paquetLength = 128B ; // ping toujours avec des paquets de 128 octets
}
...
}

Cette définition définit la paquetLongueur paramètre à une valeur fixe. Il est maintenant codé en dur que Hébergers
envoyer des paquets ping de 128 octets ; ce paramètre ne peut pas être modifié à partir de NED ou de la configuration.

Il est non seulement possible de définir un paramètre à partir du module composé qui contient le sous-module, mais
également à partir de modules plus haut dans l'arborescence des modules. Un réseau qui emploie plusieurs Héberger
modules pourraient être définis comme ceci :

23
Manuel de simulation OMNeT++ – Le langage NED

réseau Réseau
{
sous-modules:
hôte[100] : Hôte {
ping.timeToLive = défaut(3);
ping.destAddress = défaut(0);
}
...
}

Le paramétrage peut également être placé dans le paramètresbloc du module composé parent, qui offre une
flexibilité supplémentaire. La définition suivante configure les hôtes de sorte que la moitié d'entre eux envoie un
ping à l'hôte n°50 et l'autre moitié envoie un ping à l'hôte n°0 :

réseau Réseau
{
paramètres:
hôte[*].ping.timeToLive = défaut(3);
hôte[0..49].ping.destAddress = défaut(50);
hôte[50..].ping.destAddress = défaut(0);

sous-modules:
hôte[100] : hôte ;
...
}

Notez l'utilisation de l'astérisque pour correspondre à n'importe quel index et .. pour correspondre aux plages d'index.

S'il y avait un certain nombre d'hôtes individuels au lieu d'un vecteur de sous-module, la définition du réseau
pourrait ressembler à ceci :

réseau Réseau
{
paramètres:
hôte*.ping.timeToLive = défaut(3);
hôte{0..49}.ping.destAddress = défaut(50);
hôte{50..}.ping.destAddress = défaut(0);

sous-modules:
host0 : hôte ;
hôte1 : hôte ;
hôte2 : hôte ;
...
host99 : hôte ;
}

Un astérisque correspond à toute sous-chaîne ne contenant pas de point, et un .. dans une paire d'accolades
correspond à un nombre naturel intégré dans une chaîne.
Dans la plupart des affectations que nous avons vues ci-dessus, le côté gauche du signe égal contenait un point et
souvent également un caractère générique (astérisque ou plage numérique) ; nous appelons ces devoirsaffectations de
modèles ou missions profondes.
Il y a un autre caractère générique qui peut être utilisé dans les affectations de modèle, et c'est le double astérisque ; il
correspond à n'importe quelle séquence de caractères, y compris les points, de sorte qu'il peut correspondre à plusieurs
éléments de chemin. Un exemple:

24
Manuel de simulation OMNeT++ – Le langage NED

réseau Réseau
{
paramètres:
* * . timeToLive = défaut(3);
* * . adresse_dest = défaut(0);
sous-modules:
host0 : hôte ;
hôte1 : hôte ;
...
}

Notez que certaines affectations dans les exemples ci-dessus ont modifié les valeurs par défaut, tandis que d'autres définissent
des paramètres sur des valeurs fixes. Les paramètres qui n'ont pas reçu de valeur fixe dans les fichiers NED peuvent être affectés
à partir de la configuration (omnetpp.ini).

IMPORTANT: Une valeur non par défaut attribuée à partir de NED ne peut pas être écrasée ultérieurement dans
NED ou à partir de fichiers ini ; il devient "codé en dur" en ce qui concerne les fichiers ini et l'utilisation de NED. En
revanche, les valeurs par défaut peuvent être écrasées.

Un paramètre peut être affecté dans la configuration en utilisant une syntaxe similaire à celle des affectations de
modèles NED (en fait, il serait plus exact historiquement de le dire dans l'autre sens, que les affectations de
modèles NED utilisent une syntaxe similaire aux fichiers ini) :

Réseau.hôte[*].ping.sendInterval = 500ms # pour l'exemple host[100]


Réseau.hôte*.ping.sendInterval = 500ms # pour l'exemple host0,host1,...
* * . intervalle d'envoi = 500 ms

On utilise souvent le double astérisque pour économiser la frappe. On peut écrire

* * . ping.sendInterval = 500ms

Ou si l'on est certain que seuls les modules ping ont sendInterval paramètres, ce qui suit suffira :

* * . intervalle d'envoi = 500 ms

Les paramétrages dans la configuration sont décrits au chapitre 10.3.


On peut également écrire des expressions, y compris des expressions stochastiques, dans des fichiers NED ainsi que
dans des fichiers ini. Par exemple, voici comment on peut ajouter de la gigue à l'envoi de paquets ping :

* * . sendInterval = 1s + normal(0s, 0.001s)# ou juste : normal(1s, 0.001s)

S'il n'y a pas d'affectation pour un paramètre dans NED ou dans le fichier ini, la valeur par défaut (donnée
avec =défaut(...)dans NED) seront appliqués implicitement. S'il n'y a pas de valeur par défaut, l'utilisateur
sera invité, à condition que le programme de simulation soit autorisé à le faire ; sinon il y aura une erreur.
(Le mode interactif est généralement désactivé pour les exécutions par lots où il ferait plus de mal que de
bien.)
Il est aussi possible d'appliquer explicitement la valeur par défaut (cela peut parfois être utile) :

* * . sendInterval = par défaut

Enfin, on peut explicitement demander au simulateur d'inviter l'utilisateur de manière interactive pour la valeur
(là encore, à condition que l'interactivité soit activée, sinon cela entraînera une erreur) :

* * . sendInterval = demander

25
Manuel de simulation OMNeT++ – Le langage NED

REMARQUE: Comment décider d'assigner un paramètre depuis NED ou depuis un fichier ini ?
L'avantage des fichiers ini est qu'ils permettent une séparation plus nette desmaquetteet
expériences. Les fichiers NED (avec le code C++) sont considérés comme faisant partie du modèle
et comme étant plus ou moins constants. Les fichiers ini, en revanche, servent à expérimenter le
modèle en l'exécutant plusieurs fois avec différents paramètres. Ainsi, les paramètres censés
changer (ou avoir un sens à changer) pendant l'expérimentation doivent être placés dans des
fichiers ini.

3.6.2 Expressions

Les valeurs des paramètres peuvent être données avec des expressions. Les expressions du langage NED ont une
syntaxe de type C, avec quelques variations sur les noms d'opérateurs : les XOR binaires et logiques sont # et ##,
tandis que ˆa été réaffecté àle pouvoir deplutôt. L'opérateur + effectue la concaténation de chaînes ainsi que
l'addition numérique. Les expressions peuvent utiliser diverses fonctions numériques, de chaîne, stochastiques et
autres (fabs(), toUpper(), uniform(), erlang_k(),etc.).

REMARQUE: La liste des fonctions NED se trouve dans l'annexe D. L'utilisateur peut également étendre NED avec
de nouvelles fonctions.

Les expressions peuvent faire référence aux paramètres de module, au vecteur de porte et aux tailles de vecteur de
module (en utilisant le taille de) et l'indice du module courant dans un vecteur de sous-module (indice).

Les expressions peuvent faire référence à des paramètres du module composé en cours de définition,
du module courant (avec lacette.préfixe), et aux paramètres des sous-modules déjà définis, avec la
syntaxesubmodule.parametername (ou sous-module[index].nomparamètre).

3.6.3 volatile

levolatilLe modificateur entraîne l'évaluation de l'expression de la valeur du paramètre à chaque


lecture du paramètre. Cela a une signification si l'expression n'est pas constante, par exemple s'il
s'agit de nombres tirés d'un générateur de nombres aléatoires. En revanche, les paramètres non
volatils ne sont évalués qu'une seule fois. (Cela signifie pratiquement qu'ils sont évalués et remplacés
par la constante résultante au début de la simulation.)
Pour mieux comprendrevolatil, supposons que nous ayons unFile d'attentemodule simple qui a un
double volatilparamètre nommétemps de service.
FacileFile d'attente
{
paramètres:
double volatiltemps de service;
}

En raison de lvolatilmodificateur, l'implémentation C++ du module de file d'attente est censée relire letemps de
serviceparamètre chaque fois qu'une valeur est nécessaire ; c'est-à-dire pour chaque travail desservi. Ainsi, si
temps de servicese voit attribuer une expression comme uniforme(0.5s, 1.5s), chaque travail aura un temps de
service différent et aléatoire. Pour mettre en évidence cet effet, voici comment on peut avoir un paramètre variant
dans le temps en exploitant lesimHeure() Fonction NED qui renvoie le temps de simulation actuel :

* * . serviceTime = simTime()<1000s ? 1s : 2s# file d'attente qui ralentit après 1000s

En pratique, les paramètres volatils sont généralement utilisés comme source configurable de nombres aléatoires pour
les modules.

26
Manuel de simulation OMNeT++ – Le langage NED

REMARQUE: Cela ne signifie pas qu'un paramètre non volatil ne peut pas se voir attribuer une valeur
aléatoire telle que uniforme (0,5 s, 1,5 s). C'est possible, mais cela aurait un effet totalement différent :
la simulation utiliserait un temps de service constant, disons 1.2975367s, choisi au hasard au début de
la simulation.

3.6.4 Unités

On peut déclarer un paramètre pour avoir une unité de mesure associée, en ajoutant le @unité
propriété. Un exemple:
FacileApplication
{
paramètres:
double volatilsendInterval @unité(s) =défaut(exponentiel(350ms)); entier volatil
paquetLength @unit(byte) =défaut(4Ko);
...
}

Le @unités) et @unité (octet) les déclarations spécifient l'unité de mesure du paramètre. Les valeurs affectées aux
paramètres doivent avoir la même unité ou une unité compatible, c'est-à-dire @unités) accepte les millisecondes,
les nanosecondes, les minutes, les heures, etc., et @unité (octet) accepte également les kilo-octets, mégaoctets,
etc.

REMARQUE: La liste des unités acceptées par OMNeT++ est listée en annexe, voir A.5.9. Unités inconnues (
bogomips, etc.) peuvent également être utilisés, mais il n'y a pas de conversions pour eux, c'est-à-dire que
les préfixes décimaux ne seront pas reconnus.

Le runtime OMNeT++ effectue une vérification unitaire complète et rigoureuse des paramètres pour garantir la « sécurité
unitaire » des modèles. Les constantes doivent toujours inclure l'unité de mesure.

Le @unitéLa propriété d'un paramètre ne peut pas être ajoutée ou remplacée dans les sous-classes ou dans les
déclarations de sous-module.

3.6.5 Paramètres XML

Parfois, les modules ont besoin de structures de données complexes en entrée, ce qui ne peut pas être bien fait avec les
paramètres de module. Une solution consiste à placer les données d'entrée dans un fichier de configuration
personnalisé, à transmettre le nom du fichier au module dans un paramètre de chaîne et à laisser le module lire et
analyser le fichier.

C'est un peu plus facile si la configuration utilise la syntaxe XML, car OMNeT++ contient un support intégré pour
les fichiers XML. À l'aide d'un analyseur XML (LibXML2 ou Expat), OMNeT++ lit et valide le fichier par DTD (si le
document XML contient un DOCTYPE), met le fichier en cache (afin que les références à celui-ci à partir de
plusieurs modules entraînent le chargement du fichier une seule fois) , permet la sélection de parties du
document à l'aide d'une notation de sous-ensemble XPath et présente le contenu dans une arborescence d'objets
de type DOM.
Cette capacité est accessible via le type de paramètre NED XML, et le xmldoc() une fonction. On peut
pointerXML-type paramètres de module à un fichier XML spécifique (ou à un élément à l'intérieur
d'un fichier XML) via lexmldoc() une fonction. On peut attribuerXMLparamètres de NED et de
omnetpp.ini.
L'exemple suivant déclare unXMLparamètre et lui attribue un fichier XML. Le nom du fichier s'entend
comme étant relatif au répertoire de travail.

27
Manuel de simulation OMNeT++ – Le langage NED

FacileGénérateur de trafic {
paramètres:
XMLprofil;
portes:
sortiren dehors;
}

moduleNœud {
sous-modules:
TrafGen1 : TrafGen {
profil =xmldoc("data.xml");
}
...
}

xmldoc() permet aussi de sélectionner un élémentdansun fichier XML. Dans le cas où l'on a un modèle qui
contient de nombreux modules nécessitant une entrée XML, cette fonctionnalité permet à l'utilisateur de se
débarrasser des innombrables petits fichiers XML en les agrégeant dans un seul fichier XML. Par exemple,
le fichier XML suivant contient deux profils identifiés avec les IDgen1et gen2:
<?xml>
<racine>
<identifiant de profil="gen1">
<param>3</param>
<param>5</param>
</profil>
<identifiant de profil="gen2">
<param>9</param>
</profil>
</racine>

Et on peut assigner chaque profil à un sous-module correspondant en utilisant une expression de type
XPath :
moduleNœud {
sous-modules:
TrafGen1 : TrafGen {
profil =xmldoc("all.xml", "/root/profile[@id='gen1']");
}
TrafGen2 : TrafGen {
profil =xmldoc("all.xml", "/root/profile[@id='gen2']");
}
}

Il est également possible de créer un document XML à partir d'une constante chaîne, en utilisant laxml()une
fonction. Ceci est particulièrement utile pour créer une valeur par défaut pourXMLparamètres. Un exemple:
FacileGénérateur de trafic {
paramètres:
XMLprofil =XML("<racine/>");// document vide par défaut
...
}

lexml()fonction, commexmldoc(), prend également en charge un deuxième paramètre XPath facultatif pour sélectionner une
sous-arborescence.

28
Manuel de simulation OMNeT++ – Le langage NED

3.7 Portes

Les portes sont les points de connexion des modules. OMNeT++ a trois types de portes :saisir, sortir et inout, cette
dernière étant essentiellement une porte d'entrée et une porte de sortie collées ensemble.
Une porte, qu'elle soit d'entrée ou de sortie, ne peut être connectée qu'à une seule autre porte. (Pour les portes
de modules composés, cela signifie une connexion "extérieure" et une "intérieure".) Il est possible, bien que
généralement déconseillé, de connecter séparément les côtés entrée et sortie d'une porte inout (voir section 3.9).

On peut créer des portes simples et des vecteurs de porte. La taille d'un vecteur de porte peut être donnée entre
crochets dans la déclaration, mais il est également possible de le laisser ouvert en écrivant simplement une paire
de crochets vides ("[]").
Lorsque la taille du vecteur de porte est laissée ouverte, on peut toujours la spécifier plus tard, lors de la sous-
classe du module ou lors de l'utilisation du module pour un sous-module dans un module composé. Cependant, il
n'a pas besoin d'être précisé car on peut créer des connexions avec leportail++ opérateur qui développe
automatiquement le vecteur de porte.
La taille de la porte peut être interrogée à partir de diverses expressions NED avec letaille de()opérateur.
NED exige normalement que toutes les portes soient connectées. Pour assouplir cette exigence, on peut annoter
les portes sélectionnées avec le @lâchepropriété, qui désactive la vérification de la connectivité pour cette porte.
De plus, les portes d'entrée qui existent uniquement pour que le module puisse recevoir des messages via
envoyer-Direct() (voir 4.7.5) doit être annoté avec @entrée directe.Il est également possible de désactiver la
vérification de la connectivité pour toutes les portes d'un module composé en spécifiant leautoriser non
connectémot-clé dans la section des connexions du module.
Voyons quelques exemples.
Dans l'exemple suivant, leClassificateurmodule a une entrée pour recevoir des travaux, qu'il enverra à
l'une des sorties. Le nombre de sorties est déterminé par un paramètre du module :
FacileClassificateur {
paramètres:
entiernumCatégories ;
portes:
saisir dans;
sortir out[numCatégories] ;
}

Ce qui suitÉviermodule a également sondans[]porte définie comme un vecteur, afin qu'elle puisse être connectée
à plusieurs modules :
FacileÉvier {
portes:
saisir dans[];
}

Les lignes suivantes définissent un nœud pour construire une grille carrée. Les portes autour des
bords de la grille devraient rester non connectées, d'où le @lâcheannotation:
FacileNoeudGrille {
portes:
inoutvoisin[4] @loose ;
}

WirelessNodeci-dessous est censé recevoir des messages (transmissions radio) via l'envoi direct, donc son
radioInla porte est marquée d'un @entrée directe.

29
Manuel de simulation OMNeT++ – Le langage NED

FacileNœud sans fil {


portes:
saisir radioIn @directIn;
}

Dans l'exemple suivant, nous définissonsTreeNodecomme ayant des portes pour connecter n'importe quel nombre d'enfants,
puis sous-classez-le pour obtenir unBinaryTreeNodepour régler la taille de la porte sur deux :

FacileArbreNoeud {
portes:
inout parent;
inout enfants[];
}

FacileBinaryTreeNodes'étendArbreNoeud {
portes:
enfants[2] ;
}

Un exemple pour définir la taille du vecteur de porte dans un sous-module, en utilisant le mêmeTreeNodetype de module
comme ci-dessus :

moduleArbreBinaire {
sous-modules:
nœuds[31] : TreeNode {
portes:
enfants[2] ;
}
Connexions:
...
}

3.8 Sous-modules

Les modules dont un module composé est composé sont appelés ses sous-modules. Un sous-module a un nom,
et c'est une instance d'un type de module composé ou simple. Dans la définition NED d'un sous-module, ce type
de module est généralement donné de manière statique, mais il est également possible de spécifier le type avec
une expression de chaîne. (Cette dernière caractéristique,types de sous-modules paramétriques, sera discuté
dans la section 3.11.1.)
NED prend également en charge les tableaux de sous-modules (vecteurs) et les sous-modules conditionnels. La taille du vecteur de sous-
module, contrairement à la taille du vecteur de porte, doit toujours être spécifiée et ne peut pas être laissée ouverte comme avec les
portes.

Il est possible d'ajouter de nouveaux sous-modules à un module composé existant via le sous-classement ;
cela a été décrit dans la section 3.4.
La syntaxe de base des sous-modules est illustrée ci-dessous :

moduleNœud
{
sous-modules:
routage : Routage ; // un sous-module
file d'attente[taille de(port)] : file d'attente ;// vecteur de sous-module

30
Manuel de simulation OMNeT++ – Le langage NED

...
}

Comme déjà vu dans les exemples de code précédents, un sous-module peut également avoir un bloc d'accolades
comme corps, où l'on peut affecter des paramètres, définir la taille des vecteurs de porte et ajouter/modifier des
propriétés comme la chaîne d'affichage (@affichage). Il n'est pas possible d'ajouter de nouveaux paramètres et portes.

Les chaînes d'affichage spécifiées ici seront fusionnées avec la chaîne d'affichage du type pour obtenir la
chaîne d'affichage effective. L'algorithme de fusion est décrit au chapitre 8.

moduleNœud
{
portes:
inout Port[];
sous-modules:
routage : Routage {
paramètres: // ce mot clé est facultatif
table de routage = "table de routage.txt" ; // attribuer un paramètre
portes:
dans[taille de(Port)]; // définir la taille du vecteur de porte en
dehors[taille de(Port)];
}
file d'attente[taille de(port)] : file d'attente {
@display("t=identifiant file d'attente $id"); // modifier la chaîne d'affichage
identifiant = 1000+indice; // utiliser l'index de sous-module pour générer différents identifiants
}
Connexions:
...
}

Un corps vide peut être omis, c'est-à-dire

file d'attente : file d'attente ;

est le même que

file d'attente : file d'attente {


}

Un sous-module ou un vecteur de sous-module peut être conditionnel. lesimot-clé et la condition elle-même va


après le type de sous-module, comme dans l'exemple ci-dessous :

moduleHéberger
{
paramètres:
bourdonneravecTCP =défaut(vrai);
sous-modules:
tcp : TCPsiavecTCP ; . . .

Notez qu'avec les vecteurs de sous-module, la définition d'une taille de vecteur nulle peut être utilisée comme alternative
à la siétat.

31
Manuel de simulation OMNeT++ – Le langage NED

3.9 Connexions

Les connexions sont définies dans leConnexionssection des modules composés. Les connexions ne peuvent pas
s'étendre sur plusieurs niveaux de hiérarchie ; on peut connecter deux portes de sous-module, une porte de sous-
module et "l'intérieur" des portes du module parent (composé), ou deux portes du module parent (bien que cela
soit rarement utile), mais il n'est pas possible de se connecter à n'importe quelle porte à l'extérieur le module
parent, ou à l'intérieur des sous-modules composés.
Les portes d'entrée et de sortie sont reliées par une flèche normale et les portes d'entrée par une double
flèche "<-->". Pour connecter les deux portes avec un canal, utilisez deux flèches et placez la spécification du
canal entre les deux. La même syntaxe est utilisée pour ajouter des propriétés telles que @affichageà la
connexion.
Quelques exemples ont déjà été montrés dans la section NED Quickstart (3.2); voyons un peu plus.

Il a été mentionné qu'une porte d'entrée est essentiellement une porte d'entrée et une porte de sortie collées
ensemble. Ces sous-portes peuvent également être adressées (et connectées) individuellement si nécessaire,
comme port$iet port$o (ou pour les portes vectorielles, commeport$i[k]et port$o[k]).
Les portes sont spécifiées commemodulespec.gatespec(pour connecter un sous-module), ou commegatespec(pour
connecter le module composé).spécification de moduleest soit un nom de sous-module (pour les sous-modules
scalaires), soit un nom de sous-module plus un index entre crochets (pour les vecteurs de sous-module). Pour les portes
scalaires,gatespecest le nom de la porte ; pour les vecteurs de porte, il s'agit soit du nom de la porte plus un index entre
crochets, soitnom de la porte++.

lenom de la porte++ la notation entraîne l'utilisation du premier index de porte non connecté. Si toutes les portes
du vecteur de porte donné sont connectées, le comportement est différent pour les sous-modules et pour le
module composé englobant. Pour les sous-modules, le vecteur de porte se développe de un. Pour un module
composé, une fois la dernière porte connectée, ++ s'arrêtera avec une erreur.

REMARQUE: Pourquoi n'est-il pas possible de développer un vecteur porte du module composé ? La structure du modèle
est construite dans l'ordre descendant, de sorte que les nouvelles portes seraient laissées non connectées à l'extérieur, car
il n'y a aucun moyen dans NED de "revenir en arrière" et de les connecter par la suite.

Lorsque l'opérateur ++ est utilisé avec $je ou $o (par exemple g$i++ ou g$o++, voir plus loin), il ajoutera en fait une paire
de portes (entrée + sortie) pour maintenir des tailles de porte égales pour les deux directions.

3.9.1 Spécification du canal

Spécifications du canal (-->spécification de canal--> à l'intérieur d'une connexion) sont similaires aux sous-modules à bien
des égards. Voyons quelques exemples !

Les connexions suivantes utilisent deux types de canaux définis par l'utilisateur, Ethernet100 et Colonne
vertébrale. Le code montre la syntaxe d'affectation des paramètres (Coût et longueur) et en spécifiant une chaîne
d'affichage (et les propriétés NED en général) :

a.g++ <--> Ethernet100 <--> b.g++ ;


a.g++ <--> Backbone {coût=100 ; longueur=52km; ber=1e-8;} <--> b.g++;
a.g++ <--> Backbone {@display("ls=green,2");} <--> b.g++ ;

Lors de l'utilisation de types de canaux intégrés, le nom du type peut être omis ; il sera déduit des noms de
paramètres.

a.g++ <--> {délai=10ms;} <--> b.g++;

32
Manuel de simulation OMNeT++ – Le langage NED

a.g++ <--> {délai=10ms ; ber=1e-8;} <--> b.g++;


a.g++ <--> {@display("ls=red");} <--> b.g++;

Si débit de données, ber ou parest assigné, ned.DatarateChannel sera choisi. Sinon, siretardou
désactivéeest présent, il sera ned.DelayChannel ; sinon c'est ned.IdealChannel. Naturellement, si
d'autres noms de paramètres sont affectés dans une connexion sans type de canal explicite, ce sera
une erreur (avec "ned.DelayChannel n'a pas un tel paramètre » ou message similaire).
Les paramètres de connexion, de la même manière que les paramètres de sous-module, peuvent
également être affectés à l'aide d'affectations de motifs, bien que les noms de canaux à associer aux motifs
soient un peu plus compliqués et moins pratiques à utiliser. Un canal peut être identifié par le nom de sa
porte source plus le nom du canal ; le nom de la chaîne est actuellement toujourscanaliser. Elle est illustrée
par l'exemple suivant :
moduleFile d'attente
{
paramètres:
source.out.canaliser.délai = 10ms ;
queue.out.canaliser.délai = 20ms ; sous-
modules:
source : source ;
file d'attente : file d'attente ;

évier : évier ;
Connexions:
source.out --> ned.DelayChannel --> queue.in ; queue.out -->
ned.DelayChannel <--> sink.in ;

L'utilisation de connexions bidirectionnelles est un peu plus délicate, car les deux directions doivent être couvertes
séparément :

réseau Réseau
{
paramètres:
hostA.g$o[0].canaliser.datarate = 100Mbps ;// la connexion A -> B hostB.g$o[0].
canaliser.datarate = 100Mbps ;// la connexion B -> A hostA.g$o[1].canaliser.datarate =
// la connexion A -> C // la
1Gbps ; hostC.g$o[0].canaliser.datarate = 1Gbps ; sous-modules:
connexion C -> A

hôteA : hôte ;
hôteB : hôte ;
hôteC : hôte ;
Connexions:
hostA.g++ <--> ned.DatarateChannel <--> hostB.g++ ; hostA.g++ <-->
ned.DatarateChannel <--> hostC.g++ ;

De plus, avec la syntaxe ++, il n'est pas toujours facile de déterminer quels indices de porte correspondent
aux connexions à configurer. Si les objets de connexion pouvaient recevoir des noms pour remplacer le
nom par défaut "canaliser",cela faciliterait l'identification des connexions dans les modèles. Cette
fonctionnalité est décrite dans la section suivante.

3.9.2 Noms des canaux

Le nom par défaut donné aux objets canal est "canaliser".Depuis OMNeT++ 4.3, il est possible de
spécifier explicitement le nom et également de remplacer le nom par défaut par type de canal. le

33
Manuel de simulation OMNeT++ – Le langage NED

Le but des noms de canaux personnalisés est de faciliter l'adressage lorsque les paramètres de canal sont
attribués à partir de fichiers ini.
La syntaxe pour nommer un canal dans une connexion est similaire à la syntaxe du sous-module :nom :
genre. Puisque les deuxNomet tapersont facultatifs, les deux-points doivent être là aprèsNommême sitaper
manque, afin de lever l'ambiguïté.
Exemples:
r1.pppg++ <--> eth1 : EthernetChannel <--> r2.pppg++ ; a.out --> foo :
{delay=1ms;} --> b.in ;
a.out --> barre : --> b.in ;

En l'absence de nom explicite, le nom du canal vient du @nom par défaut propriété du type de canal si
elle existe.
canaliser Eth10G s'étendned.DatarateChannel Comme IEth {
@defaultname(eth10G);
}

Il y a un hic avec @nom par défaut cependant : si le type de canal est spécifié avec un **.canaliser- Nom
.liketype= ligne dans un fichier ini, puis le @ du type de canalnom par défaut ne peut pas être utilisé comme
nom du canal dans cette ligne de configuration, car le type de canal ne serait connu qu'à la suite de
l'utilisation de cette même ligne de configuration. Pour illustrer le problème, considérons ce qui précède
Eth10G channel, et un module composé contenant la connexion suivante :
r1.pppg++ <--> <> Comme IEth <--> r2.pppg++ ;

Considérez ensuite le fichier ini suivant :

* * . eth10G.typename = "Eth10G" # Ne correspondra pas ! Le nom eth10G viendrait de


# le type Eth10G - catch-22 !
* * . channel.typename = "Eth10G" # OK, car la recherche suppose le nom "canal"
* * . eth10G.datarate = 10.01Gbps # OK, le canal existe déjà avec le nom "eth10G"

L'anomalie peut être évitée en utilisant un nom de canal explicite dans la connexion, sans utiliser
@defaultname, soit en spécifiant le type via un paramètre de module (par exemple en écrivant <param>
commeau .lieu
. . de <> Comme ...).

3.10 Connexions multiples

Des constructions de programmation simples (boucle, conditionnelle) permettent de créer facilement plusieurs

connexions. Cela sera montré dans les exemples suivants.

3.10.1 Exemples

Chaîne

On peut créer une chaîne de modules comme ceci :

moduleChaîne
paramètres:
entiercompter;
sous-modules:

34
Manuel de simulation OMNeT++ – Le langage NED

nœud[compte] : nœud {
portes:
port[2] ;
}
connexions autoriséesnon connecté:
pourje = 0..count-2 {
nœud[i].port[1] <--> nœud[i+1].port[0] ;
}
}

Arbre binaire

On peut construire un arbre binaire de la manière suivante :

FacileBinaryTreeNode {
portes:
inout à gauche;
inout à droite;
inout parent;
}

moduleArbreBinaire {
paramètres:
entierla taille;
sous-modules:
nœud[2^hauteur-1] : BinaryTreeNode ;
connexions autoriséesnon connecté:
pourje=0..2^(hauteur-1)-2 {
nœud[i].left <--> nœud[2*i+1].parent ;
nœud[i].right <--> nœud[2*i+2].parent ;
}
}

Notez que toutes les portes des modules ne seront pas connectées. Par défaut, une porte non connectée produit
un message d'erreur d'exécution au démarrage de la simulation, mais ce message d'erreur est désactivé ici avec
leautoriser non connectémodificateur. Par conséquent, il est de la responsabilité des modules simples de ne pas
émettre sur une porte non connectée.

Graphique aléatoire

Les connexions conditionnelles peuvent être utilisées pour générer des topologies aléatoires, par exemple. Le code
suivant génère un sous-graphe aléatoire d'un graphe complet :

moduleGraphique aléatoire {
paramètres:
entiercompter;
doubleconnexité ; // 0.0<x<1.0 sous-modules
:
nœud[compte] : nœud {
portes:
dans[compter] ;
out[compter] ;

35
Manuel de simulation OMNeT++ – Le langage NED

}
connexions autoriséesnon connecté:
pourje=0..compte-1, pourj=0..count-1 {
nœud[i].out[j] --> nœud[j].in[i]
sii!=j && uniform(0,1)<connexité ;
}
}

Notez l'utilisation de la autoriser non connectémodificateur ici aussi, pour désactiver les messages d'erreur produits par le code
de configuration du réseau pour les portes non connectées.

3.10.2 Modèles de connexion

Plusieurs approches peuvent être utilisées pour créer des topologies complexes qui ont une structure régulière ; trois
d'entre eux sont décrits ci-dessous.

"Sous-graphe d'un graphe complet"

Ce modèle prend un sous-ensemble des connexions d'un graphe complet. Une condition est utilisée pour
"extraire" l'interconnexion nécessaire du graphe complet :
pouri=0..N-1, pourj=0..N-1 {
nœud[i].out[...] --> nœud[j].in[...] sicondition(i,j);
}

Le module composé RandomGraph (présenté précédemment) est un exemple de ce modèle, mais le


modèle peut générer n'importe quel graphique lorsqu'un état(je, j) peut être formulé. Par exemple,
lors de la génération d'une arborescence, la condition renverrait si le nœudj est un enfant du nœud je
ou vice versa.
Bien que ce modèle soit très général, son utilisation peut être rédhibitoire si le nombre de nœuds N
est élevé et le graphe est clairsemé (il a beaucoup moins de N2 Connexions). Les deux modèles
suivants ne souffrent pas de cet inconvénient.

"Connexions de chaque nœud"

Le modèle parcourt tous les nœuds et crée les connexions nécessaires pour chacun d'eux. Il peut être
généralisé ainsi :
pouri=0..Nnœuds, pourj=0..Nconns(i)-1 {
nœud[i].out[j] --> nœud[rightNodeIndex(i,j)].in[j] ;
}

Le module composé Hypercube (qui sera présenté plus tard) est un exemple clair de cette approche.
BinaryTree peut également être considéré comme un exemple de ce modèle où la boucle j interne est
déroulée.
L'applicabilité de ce modèle dépend de la facilité avec laquelle le rightNodeIndex(je, j) fonction peut
être formulée.

"Énumérer toutes les connexions"

Un troisième modèle consiste à répertorier toutes les connexions dans une boucle :

36
Manuel de simulation OMNeT++ – Le langage NED

pouri=0..Nconnexions-1 {
nœud[leftNodeIndex(i)].out[...] --> nœud[rightNodeIndex(i)].in[...] ;
}

Ce modèle peut être utilisé si leftNodeIndex(je) et rightNodeIndex(je) les fonctions de mappage peuvent être
suffisamment formulées.
leChaîne module est un exemple de cette approche où les fonctions de mappage sont extrêmement simples :
leftNodeIndex(je) = je et rightNodeIndex(je) = je + 1. Le modèle peut également être utilisé pour créer un sous-
ensemble aléatoire d'un graphique complet avec un nombre fixe de connexions.
Dans le cas de structures irrégulières où aucun des modèles ci-dessus ne peut être utilisé, on peut recourir à la
liste de toutes les connexions, comme on le ferait dans la plupart des simulateurs existants.

3.11 Sous-module paramétrique et types de connexion

3.11.1 Types de sous-modules paramétriques

Un type de sous-module peut être spécifié avec un paramètre de module du type chaîne de caractères, ou en général,
avec n'importe quelle expression de type chaîne. La syntaxe utilise leComme mot-clé.

Commençons par un exemple :


réseau Net6
{
paramètres:
chaîne de caractèresnodeType ;
sous-modules:
nœud[6] : <type de nœud>Comme INode {
adresse =indice;
}
Connexions:
...
}

Il crée un vecteur de sous-module dont le type de module proviendra dunodeTypeparamètre. Par exemple,
sinodeTypeest réglé sur "SensorNode",alors le vecteur de module sera composé de nœuds de capteur, à
condition que ce type de module existe et qu'il se qualifie. Ce que cela signifie, c'est que leINode doit être
un existantinterfaces de modules, lequel àSensorNodele type de module doit implémenter (plus à ce sujet
plus tard).
Comme déjà mentionné, on peut écrire une expression entre les crochets angulaires. L'expression peut
utiliser les paramètres du module parent et des sous-modules définis précédemment, et doit produire une
valeur de chaîne. Par exemple, le code suivant est également valide :

réseau Net6
{
paramètres:
chaîne de caractèresnodeTypePrefix ;
entierune variante;
sous-modules:
node[6] : <nodeTypePrefix + "Node" + chaîne de caractères(variante)> Comme INode {
...
}

37
Manuel de simulation OMNeT++ – Le langage NED

Les déclarations NED correspondantes :


interface de module INode
{
paramètres:
entieradresse;
portes:
inoutPort[];
}

moduleSensorNodeComme INode {

paramètres:
entier adresse;

...
portes:
inout Port[];
...
}

Le "<nodeType> comme INode” La syntaxe a un problème lorsqu'elle est utilisée avec des vecteurs de sous-
module : ne permet pas de spécifier différents types pour différents indices. La syntaxe suivante est mieux
adaptée aux vecteurs de sous-module :
L'expression entre les crochets angulaires peut être complètement omise, laissant une paire de crochets
angulaires vides, <> :

moduleNœud
{
sous-modules:
nic : <> Comme INic; // expression de nom de type laissée non spécifiée
...
}

Maintenant, le nom du type de sous-module devrait être défini via des affectations de modèle de nom de type. Les
affectations de modèle de nom de type ressemblent aux affectations de modèle pour les paramètres du sous-module,
seul le nom du paramètre est remplacé par lenom de typemot-clé. Les affectations de modèle de nom de type peuvent
également être écrites dans le fichier de configuration. Dans un réseau qui utilise ce qui précède Nœud Type NED, les
affectations de modèles de nom de type ressembleraient à ceci :

réseau Réseau
{
paramètres:
nœud[*].nic.nom de type= "Ieee80211g" ; sous-
modules:
nœud : nœud[100] ;
}

Une valeur par défaut peut également être spécifiée entre les chevrons ; il sera utilisé s'il n'y a pas
d'affectation de nom de type pour le module :

moduleNœud
{
sous-modules:
nic : <défaut("Ieee80211b")> Comme INic;

38
Manuel de simulation OMNeT++ – Le langage NED

...
}

Il doit y avoir exactement un type de module qui porte le nom simple Ieee80211b et implémente
également l'interface du module INic, sinon un message d'erreur sera émis. (Les importations en
Nœud's le fichier NED ne joue aucun rôle dans la résolution de type.) S'il existe deux types de ce type
ou plus, on peut lever l'ambiguïté en spécifiant le nom complet du type de module, c'est-à-dire un
nom qui inclut également le nom du package :
moduleNœud
{
sous-modules:
nic : <défaut("acme.wireless.Ieee80211b")> Comme INic; // nom inventé
...
}

3.11.2 Sous-modules paramétriques conditionnels

Lors de la création de modules composés réutilisables, il est souvent utile de pouvoir rendre un sous-module
paramétrique également facultatif. Une solution consiste à laisser l'utilisateur définir le type de sous-module avec
un paramètre de chaîne et de ne pas créer le module lorsque le paramètre est défini sur la chaîne vide. Comme
ça:

moduleNœud
{
paramètres:
chaîne de caractèrestcpType = défaut("TCP");
sous-modules:
tcp : <tcpType> Comme ITcp sitcpType !="" ;
}

Cependant, ce modèle, lorsqu'il est utilisé de manière intensive, peut conduire à un grand nombre de paramètres de chaîne.
Heureusement, il est également possible d'obtenir le même effet avecnom de type, sans utiliser de paramètres
supplémentaires :

moduleNœud
{
sous-modules:
TCP : <défaut("TCP")> Comme ITcp si nom de type!="";
}

lenom de typeopérateur dans un sous-module sicondition évalue le type potentiel du sous-module. En


utilisant lenomtype !="" condition, nous pouvons laisser l'utilisateur éliminer le TCP sous-module en
définissant son nom de type sur la chaîne vide. Par exemple, dans un réseau qui utilise le type NED ci-
dessus, les affectations de modèle de nom de type pourraient ressembler à ceci :

réseau Réseau
{
paramètres:
nœud1.tcp.nom de type= "TcpExt" ; // laissez node1 utiliser un TCP personnalisé
node2.tcp.nom de type= "" ; // pas de TCP dans node2 sous-modules:

nœud1 : nœud ;

39
Manuel de simulation OMNeT++ – Le langage NED

nœud2 : nœud ;
}

Notez que cette astuce ne fonctionne pas avec les vecteurs de sous-module. La raison en est que la condition s'applique
au vecteur dans son ensemble, tandis que le type est par élément.

Il est également souvent utile de pouvoir vérifier, par exemple dans la section des connexions, si un
sous-module conditionnel a été créé ou non. Ceci peut être fait avec leexiste()opérateur. Un exemple:

moduleNœud
{
...
Connexions:
ip.tcpOut --> tcp.ipIn si existe(ip) && existe(tcp);
}

Limitation: existe()ne peut être utilisé que après l'occurrence du sous-module dans le module
composé.

3.11.3 Types de connexion paramétrique

Les types de connexion paramétriques fonctionnent de la même manière que les types de sous-modules paramétriques, et la
syntaxe est également similaire. Un exemple basique qui utilise un paramètre du module parent :

a.g++ <--> <channelType>Comme IMyChannel <--> b.g++ ;


a.g++ <--> <channelType>Comme IMyChannel {@display("ls=red");} <--> b.g++;

L'expression peut utiliser des variables de boucle, des paramètres du module parent ainsi que des paramètres de
sous-modules (par exemplehôte[2].channelType).
L'expression de type peut également être absente, puis le type devrait être spécifié à l'aide des affectations
de modèle de nom de type :
a.g++ <--> <>Comme IMyChannel <--> b.g++ ;
a.g++ <--> <>Comme IMyChannel {@display("ls=red");} <--> b.g++;

Une valeur par défaut peut également être donnée :

a.g++ <--> <défaut("Ethernet100")>Comme IMyChannel <--> b.g++ ;


a.g++ <--> <défaut(type de canal)>Comme IMyChannel <--> b.g++ ;

Les affectations de modèle de type correspondantes :

ag$o[0].canaliser.nom de type= "Ethernet1000" ; // A -> canal B // B ->


bg$o[0].canaliser.nom de type= "Ethernet1000" ; canal A

3.12 Annotations des métadonnées (propriétés)

Les propriétés NED sont des annotations de métadonnées qui peuvent être ajoutées aux modules,
paramètres, portes, connexions, fichiers NED, packages et pratiquement tout dans NED. @affichage,
@class, @namespace, @unit, @prompt, @loose, @directIn sont toutes des propriétés qui ont été
mentionnées dans les sections précédentes, mais ces exemples ne font qu'effleurer la surface de
l'utilisation des propriétés.

40
Manuel de simulation OMNeT++ – Le langage NED

En utilisant les propriétés, on peut attacher des informations supplémentaires aux éléments NED. Certaines propriétés
sont interprétées par NED, par le noyau de simulation ; d'autres propriétés peuvent être lues et utilisées à partir du
modèle de simulation, ou fournir des conseils pour les outils d'édition NED.

Les propriétés sont attachées au type, donc on ne peut pas avoir différentes propriétés définies par instance.
Toutes les instances de modules, connexions, paramètres, etc. créés à partir d'un emplacement particulier dans
les fichiers NED ont des propriétés identiques.
L'exemple suivant montre la syntaxe d'annotation de divers éléments NED :
@namespace(foo); // propriété de fichier

moduleExemple
{
paramètres:
@nœud; // propriété du module
@display("i=appareil/pc"); // propriété du module
entierune @unité(s) = défaut(1); // propriété de paramètre portes:

sortirout @loose @labels(pk); sous- // propriétés de la porte


modules:
source : source {
paramètres:
@display("p=150,100"); // propriété du sous-module
count @prompt("Entrez count:"); // ajouter une propriété à un paramètre portes:

out[] @loose ; // ajouter une propriété à un portail


}
...
Connexions:
src.out++ --> { @display("ls=vert,2"); } --> évier1.in ;// accessoire de connexion. src.out++ --> Canal
{ @display("ls=green,2"); } --> sink2.in ;
}

3.12.1 Indices de propriété

Il est parfois utile d'avoir plusieurs propriétés portant le même nom, par exemple pour déclarer
plusieurs statistiques produites par un module simple. Indices immobiliers rendre cela possible.
Un index de propriété est un identificateur ou un nombre entre crochets après le nom de la propriété, tel
que besoin et gigue dans l'exemple suivant :
FacileApplication {
@statistic[eed](title="délai de bout en bout des paquets reçus";unit=s); @statistic[jitter]
(title="jitter des paquets reçus");
}

Cet exemple déclare deux statistiques comme @statistique Propriétés, @statistique[eed] et @statistique
[jitter]. Les valeurs de propriété entre parenthèses sont utilisées pour fournir des informations
supplémentaires, comme un nom plus descriptif (titre="..." ou une unité (unité=s). Les index de propriété sont
également facilement accessibles à partir de l'API C++ ; par exemple il est possible de se demander quels
indices existent pour le "statistique" propriété, et il renverra une liste contenant "j'ai besoin" et "gigue").
Dans le @statistique Par exemple, l'index était textuel et significatif, mais aucun n'est réellement
requis. L'exemple factice suivant montre l'utilisation d'indices numériques qui peuvent être

41
Manuel de simulation OMNeT++ – Le langage NED

complètement ignoré par le code qui interprète les propriétés :


FacileFactice {
@foo[1](what="pommes";montant=2);
@foo[2](what="oranges";montant=5);
}

Notez que sans l'index, les lignes définiraient en fait le même @fou propriété et écraseraient les
valeurs de l'autre.
Les index permettent également de remplacer les entrées par héritage :

FacilePoste Factice s'étendFactice {


@foo[2](what="pamplemousses"); // 5 pamplemousses au lieu de 5 oranges
}

3.12.2 Modèle de données

Les propriétés peuvent contenir des données, données entre parenthèses ; le modèle de données est assez flexible. Pour commencer, les
propriétés peuvent ne contenir aucune valeur ou une seule valeur :

@nœud;
@nœud(); // identique à @node
@class(FtpApp2);

Les propriétés peuvent contenir des listes :

@foo (Sneezy, Sleepy, Dopey, Doc, Happy, Bashful, Grumpy);

Ils peuvent contenir des paires clé-valeur, séparées par des points-virgules :

@foo(x=10.31 ; y=30.2 ; unité=km);

Dans les paires clé-valeur, chaque valeur peut être une liste (séparée par des virgules) :

@foo(coords=47.549,19.034;labels=vehicle,router,critical);

Les exemples ci-dessus sont des cas particuliers du modèle de données général. Selon le modèle de données, les
propriétés contiennentliste de valeurs clés paires, séparées par des points-virgules. Articles dansliste de valeurs sont
séparés par des virgules. Partout oùclé est manquant, les valeurs vont sur la liste de valeurs du clé par défaut, la chaîne
vide.

Les éléments de valeur peuvent contenir des mots, des nombres, des constantes de chaîne et d'autres caractères,
mais pas des chaînes arbitraires. Chaque fois que la syntaxe ne permet pas une certaine valeur, elle doit être
placée entre guillemets. Cette citation n'affecte pas la valeur car l'analyseur supprime automatiquement une
couche de guillemets ; Donc, @classe (TCP) et @classe("TCP") sont exactement les mêmes. Si les guillemets eux-
mêmes doivent faire partie de la valeur, une couche supplémentaire de guillemets et d'échappement est la
solution : @foo("\"une chaîne\"").
Il y a aussi quelques conventions. On peut utiliser des propriétés pour étiqueter les éléments NED ; par exemple, un @héberger
La propriété pourrait être utilisée pour marquer tous les types de module qui représentent divers hôtes. Cette propriété pourrait
être reconnue par exemple par des outils d'édition, par un code de découverte de topologie à l'intérieur du modèle de
simulation, etc.

La convention pour une telle propriété "marqueur" est que toute donnée supplémentaire qu'elle contient (c'est-à-
dire entre parenthèses) est ignorée, à l'exception d'un seul mot faux, qui a la signification spéciale de « désactiver
» la propriété. Ainsi, tout modèle ou outil de simulation qui interprète les propriétés doit gérer toutes les

42
Manuel de simulation OMNeT++ – Le langage NED

les formes suivantes comme équivalent à @hôte : @host(), @host(true), @host(anything-butfalse),


@host(a=1;b=2);et @hôte (faux)doit être interprété comme l'absence du @héberger étiqueter.

3.12.3 Remplacer et étendre les valeurs de propriété

Les propriétés définies sur un type de module ou de canal peuvent être mises à jour à la fois par sous-classement et lors
de l'utilisation du type comme sous-module ou canal de connexion. On peut ajouter de nouvelles propriétés, et aussi
modifier celles existantes.

Lors de la modification d'une propriété, la nouvelle propriété est fusionnée avec l'ancienne. Les règles de fusion sont
assez simples. De nouvelles clés sont simplement ajoutées. Si une clé existe déjà dans l'ancienne propriété, les éléments
de sa liste de valeurs écrasent les éléments à la même position dans l'ancienne propriété. Un seul trait d'union (−) en tant
qu'élément de la liste de valeurs sert d '«anti-valeur», il supprime l'élément à la position correspondante.

Quelques exemples:
base @soutenir
Nouveau @prop(a)
résultat @prop(a)
base @prop(a,b,c)
Nouveau @soutenir(,-)
résultat @prop(a,,c)
base @prop(foo=a,b)
Nouveau @prop(foo=A,,c;bar=1,2)
résultat @prop(foo=A,b,c;bar=1,2)

REMARQUE: Les règles de fusion ci-dessus font partie de NED, mais le code qui interprète les propriétés
peut avoir des règles spéciales pour certaines propriétés. Par exemple, le @unitéla propriété des
paramètres n'est pas autorisée à être remplacée, et @affichageest fusionnée avec des règles spéciales bien
que similaires (voir chapitre 8).

3.13 Héritage

La prise en charge de l'héritage dans le langage NED n'est décrite que brièvement ici, car plusieurs détails
et exemples ont déjà été présentés dans les sections précédentes.
Dans NED, un type ne peut s'étendre que (s'étendmot-clé) un élément du même type de composant :
un module simple peut étendre un module simple, un canal peut étendre un canal, une interface de
module peut étendre une interface de module, etc. Il y a cependant une irrégularité : un module
composé peut étendre un module simple (et hériter de sa classe C++), mais pas vice versa.

L'héritage unique est pris en charge pour les modules et les canaux, et l'héritage multiple est pris en charge pour
les interfaces de module et les interfaces de canal. Un réseau est un raccourci pour un module composé avec le @
estRéseau ensemble de propriétés, donc les mêmes règles s'appliquent à lui qu'aux modules composés.

Cependant, un type de module simple ou composé peut implémenter (Comme mot-clé) plusieurs interfaces de
module ; de même, un type de canal peut implémenter plusieurs interfaces de canal.

43
Manuel de simulation OMNeT++ – Le langage NED

IMPORTANT: Lorsque vous étendez un type de module simple à la fois dans NED et dans C++, vous
devez utiliser le @classerpour dire à NED d'utiliser la nouvelle classe C++ – sinon le nouveau type de
module hérite de la classe C++ de la base !

La succession peut :

• ajouter de nouvelles propriétés, paramètres, portes, types internes, sous-modules, connexions, tant que les
noms n'entrent pas en conflit avec les noms hérités

• modifier les propriétés héritées et les propriétés des paramètres et des portes hérités
• il ne peut pas modifier les sous-modules hérités, les connexions et les types internes

Pour des détails et des exemples, voir les sections correspondantes de ce chapitre (modules simples
3.3, modules composés 3.4, canaux 3.5, paramètres 3.6, portes 3.7, sous-modules 3.8, connexions 3.9,
interfaces de module et interfaces de canal 3.11.1).

3.14 Forfaits

Avoir tous les fichiers NED dans un seul répertoire convient aux petits projets de simulation. Cependant, lorsqu'un
projet grandit, il devient tôt ou tard nécessaire d'introduire une structure de répertoires et d'y trier les fichiers
NED. NED prend en charge nativement les arborescences de répertoires avec les fichiers NED et appelle les
répertoirespaquets. Les packages sont également utiles pour réduire les conflits de noms, car les noms peuvent
être qualifiés avec le nom du package.

REMARQUE: Les packages NED sont basés sur le concept de package Java, avec des améliorations mineures.
Si vous êtes familier avec Java, vous trouverez peu de surprise dans cette section.

3.14.1 Aperçu

Lorsqu'une simulation est exécutée, il faut indiquer au noyau de simulation le répertoire qui est la racine de
l'arborescence des packages ; appelons-leDossier source NED. Le noyau de simulation traversera toute l'arborescence
des répertoires et chargera tous les fichiers NED de chaque répertoire. On peut avoir plusieurs arborescences de
répertoires NED, et leurs racines (les dossiers source NED) doivent être données au noyau de simulation dans leChemin
NEDvariable. Le chemin NED peut être spécifié de plusieurs manières : en tant que variable d'environnement (NEDPATH),
comme option de configuration (chemin-ned), ou en tant qu'option de ligne de commande pour l'environnement
d'exécution de la simulation (-n). NEDPATHest décrit en détail au chapitre 11.

Les répertoires d'une arborescence source NED correspondent à des packages. Si les fichiers NED sont dans le <
racine>/a/b/c répertoire (où <racine>est répertorié dans le chemin NED), le nom du package estabcLe nom du
package doit également être explicitement déclaré en haut des fichiers NED, comme ceci :

paquetabc;

Le nom du package qui suit le nom du répertoire et le package déclaré doivent correspondre ; c'est
une erreur s'ils ne le font pas. (La seule exception est la racinepaquet.ned file, comme décrit ci-
dessous.)
Par convention, les noms de packages sont tous en minuscules et commencent soit par le nom du projet
(mon projet),ou le nom de domaine inversé plus le nom du projet (org.exemple.monprojet). Cette dernière
convention ferait commencer l'arborescence de répertoires par quelques niveaux de répertoires vides, mais
cela peut être éliminé avec un toplevelpackage.ned.

44
Manuel de simulation OMNeT++ – Le langage NED

Fichiers NED appeléspackage.ned ont un rôle particulier, car ils sont censés représenter l'ensemble du paquet.
Par exemple, les commentaires danspackage.ned sont traités comme la documentation du paquet. Aussi un @
espace de noms propriété dans un paquet.ned file affecte tous les fichiers NED dans ce répertoire et tous les
répertoires en dessous.
Le niveau supérieur paquet.ned file peut être utilisé pour désigner le package racine, ce qui est utile pour éliminer
quelques niveaux de répertoires vides résultant de la convention de nommage des packages. Par exemple, étant
donné un projet où tous les types NED sont sous leorg.acme.foosim package, on peut éliminer les niveaux de
répertoires vides org, acmé et imbécile en créant un package.ned fichier dans le répertoire racine source avec la
déclaration de package org.exemple.monprojet. Cela créera un répertoire fou sous la racine à interpréter comme
package org.exemple.monprojet.foo, et les fichiers NED qu'ils contiennent doivent contenir cela en tant que
déclaration de package. Seule la racinepackage.ned peut définir le package, paquet.ned files fichiers des sous-
répertoires doivent le suivre.
Prenons l'exemple du framework INET, qui contient des centaines de fichiers NED dans plusieurs dizaines
de packages. La structure du répertoire ressemble à ceci :

INET/
src/
base/
transport/
TCP/
udp/
...
couche réseau/
couche de liaison/
...
exemples/
ad hoc/
Ethernet/
...

lesrc et exemples les sous-répertoires sont désignés comme des dossiers source NED, donc NEDPATH est le
suivant (à condition qu'INET ait été décompressé dans /maison/joe):

/home/joe/INET/src;/home/joe/INET/exemples

Les deux src et exemples contenir paquet.ned fifichiers pour définir le package racine :
// INET/src/paquet.ned : paquet
inet;

// INET/exemples/paquet.ned : paquet
inet.exemples ;

Et d'autres fichiers NED suivent le package défini dans package.ned :


// INET/src/transport/tcp/TCP.ned : paquet
inet.transport.tcp;

3.14.2 Résolution de noms, importations

Nous avons déjà mentionné que les packages peuvent être utilisés pour distinguer les types NED portant des
noms similaires. Le nom qui inclut le nom du package (abcQueuepour unFile d'attentemodules dans leabc

45
Manuel de simulation OMNeT++ – Le langage NED

paquet) s'appellenom complet; sans le nom du package (File d'attente)on l'appellenom simple.

Les noms simples seuls ne suffisent pas à identifier sans ambiguïté un type. Voici comment on peut se
référer à un type existant :

1. Par nom pleinement qualifié. C'est souvent fastidieux, car les noms ont tendance à être trop longs ;

2. Importez le type, puis le nom simple suffira ;

3. Si le type se trouve dans le même package, il n'est pas nécessaire de l'importer ; il peut être désigné
par un simple nom

Les types peuvent être importés avec leimportermot-clé soit par un nom complet, soit par un modèle générique.
Dans les modèles génériques, un astérisque ("*") signifie "toute séquence de caractères ne contenant pas de
point", et deux astérisques ("**") signifient "toute séquence de caractères pouvant contenir un point".

Ainsi, n'importe laquelle des lignes suivantes peut être utilisée pour importer un type appelé
inet.protocols.networklayer.ip.RoutingTable :

importer inet.protocols.networklayer.ip.RoutingTable ;
importer inet.protocols.networklayer.ip.* ;
importer inet.protocols.networklayer.ip.Ro*Ta* ;
importer inet.protocols.*.ip.*;
importer inet.**.RoutingTable ;

Si une importation nomme explicitement un type avec son nom complet exact, alors ce type doit exister, sinon il
s'agit d'une erreur. Les importations contenant des caractères génériques sont plus permissives, il leur est permis
de ne correspondre à aucun type NED existant (bien que cela puisse générer un avertissement.)
Les types internes ne peuvent pas être référencés en dehors de leurs types englobants, ils ne peuvent donc pas non plus être importés.

3.14.3 Résolution de nom avec "like"

La situation est un peu différente pour les spécifications de sous-module et de voie de connexion utilisant le
Comme mot-clé, lorsque le nom du type provient d'une expression de valeur de chaîne (voir la section 3.11.1 sur
les types de sous-module et de voie en tant que paramètres). Les importations ne sont pas très utiles ici : au
moment de l'écriture du fichier NED, on ne sait pas encore quels types de NED pourront être « branchés » là-bas,
ils ne peuvent donc pas être importés à l'avance.
Il n'y a pas de problème avec les noms pleinement qualifiés, mais les noms simples doivent être
résolus différemment. Voici ce que fait NED : il détermine quelle interface le type de module ou de
canal doit implémenter (c'est-à-dire ...comme INode),puis collecte les types qui ont le nom simple
donné ET implémentent l'interface donnée. Il doit y avoir exactement un type de ce type, qui est
ensuite utilisé. S'il n'y en a pas ou s'il y en a plusieurs, cela sera signalé comme une erreur.
Voyons l'exemple suivant :
moduleMobileHost
{
paramètres:
chaîne de caractèrestype de mobilité ;
sous-modules:
mobilité : <mobilityType> Comme Mobilité ;

46
Manuel de simulation OMNeT++ – Le langage NED

...
}

et supposons que les modules suivants implémentent le Mobilitéinterfaces des modules :


inet.mobility.RandomWalk, inet.adhoc.RandomWalk, inet.mobility.MassMobility. Supposons également
qu'il existe un type appelé inet.examples.adhoc.MassMobility mais il n'implémente pas l'interface.

Donc si mobilityType="Mobilité de Masse", ensuite inet.mobility.MassMobility sera sélectionné ; L'autre


Mobilité de masse n'interfère pas. Toutefois, simobilityType="RandomWalk", alors c'est une erreur car il y a
deux correspondances Marche aléatoire les types. Les deuxMarche aléatoire's peuvent toujours être
utilisés, mais il faut en choisir explicitement un en fournissant un nom de package : mobilité-
Type="inet.adhoc.RandomWalk".

3.14.4 Le package par défaut

Il n'est pas obligatoire d'utiliser des packages : si tous les fichiers NED se trouvent dans un seul répertoire répertorié sur
le NEDPATH, les déclarations de packages (et les importations) peuvent être omises. On dit que ces fichiers se trouvent
dans lepaquet par défaut.

47
Manuel de simulation OMNeT++ – Le langage NED

48
Manuel de simulation OMNeT++ – Modules simples

Chapitre 4

Modules simples

Modules simples sont les composants actifs du modèle. Des modules simples sont programmés en C++, en utilisant la
bibliothèque de classes OMNeT++. Les sections suivantes contiennent une brève introduction à la simulation
d'événements discrets en général, expliquent comment ses concepts sont implémentés dans OMNeT++, et donnent un
aperçu et des conseils pratiques sur la façon de concevoir et de coder des modules simples.

4.1 Concepts de simulation

Cette section contient une très brève introduction sur le fonctionnement de la simulation à événements discrets (DES),
afin d'introduire les termes que nous utiliserons pour expliquer les concepts et l'implémentation d'OMNeT++.

4.1.1 Simulation d'événements discrets

UNEsystème à événements discretsest un système où les changements d'état (événements) se produisent à des instances
discrètes dans le temps, et les événements ne prennent aucun temps pour se produire. On suppose qu'il ne se passe rien (c'est-
à-dire rien d'intéressant) entre deux événements consécutifs, c'est-à-dire qu'aucun changement d'état n'a lieu dans le système
entre les événements. Ceci est en contraste aveccontinusystèmes où les changements d'état sont continus. Les systèmes qui
peuvent être considérés comme des systèmes à événements discrets peuvent être modélisés à l'aide de la simulation à
événements discrets, DES.

Par exemple, les réseaux informatiques sont généralement considérés comme des systèmes à événements discrets. Certains des
événements sont:

• début d'une transmission de paquets

• fin d'une transmission de paquets

• expiration d'un délai de retransmission

Cela implique qu'entre deux événements tels que début d'une transmission de paquets et fin d'une
transmission de paquets, il ne se passe rien d'intéressant. Autrement dit, l'état du paquet resteêtre
transmis. Notez que la définition d'événements et d'états « intéressants » dépend toujours de l'intention et
des objectifs du modélisateur. Si nous étions intéressés par la transmission de bits individuels, nous aurions
inclus quelque chose commedébut de la transmission des bits et fin de transmission des bits parmi nos
événements.

49
Manuel de simulation OMNeT++ – Modules simples

Le moment où les événements se produisent est souvent appelé horodatage de l'événement; avec OMNeT++ nous
utilisons le terme heure d'arrivée (car dans la bibliothèque de classes, le mot « horodatage » est réservé à un attribut
configurable par l'utilisateur dans la classe d'événements). Le temps dans le modèle est souvent appelétemps de
simulation, temps de modèle ou temps virtuel par opposition au temps réel ou au temps CPU qui font référence à la
durée d'exécution du programme de simulation et au temps CPU qu'il a consommé.

4.1.2 La boucle d'événements

La simulation d'événements discrets maintient l'ensemble des événements futurs dans une structure de données
souvent appelée FES (Future Event Set) ou FEL (Future Event List). Ces simulateurs fonctionnent généralement
selon le pseudocode suivant :

initialiser - cela inclut la construction du modèle et


insertion d'événements initiaux dans FES

while (FES non vide et simulation pas encore terminée) {

récupérer le premier événement de FES t :=


horodatage de cet événement événement
de processus
(le traitement peut insérer de nouveaux événements dans FES ou supprimer des événements existants)
}
terminer la simulation (écrire les résultats statistiques, etc.)

L'étape d'initialisation construit généralement les structures de données représentant le modèle de simulation, appelle
tout code d'initialisation défini par l'utilisateur et insère des événements initiaux dans le FES pour s'assurer que la
simulation peut démarrer. Les stratégies d'initialisation peuvent différer considérablement d'un simulateur à l'autre.

La boucle suivante consomme les événements du FES et les traite. Les événements sont traités dans un
ordre d'horodatage strict pour maintenir la causalité, c'est-à-dire pour garantir qu'aucun événement en
cours n'ait d'effet sur les événements antérieurs.
Le traitement d'un événement implique des appels au code fourni par l'utilisateur. Par exemple, en utilisant l'exemple de
simulation de réseau informatique, le traitement d'un événement "timeout expiré" peut consister à renvoyer une copie
du paquet réseau, à mettre à jour le nombre de tentatives, à programmer un autre événement "timeout", etc. Le code
utilisateur peut également supprimer des événements du FES, par exemple lors de l'annulation de temporisations.

La simulation s'arrête lorsqu'il n'y a plus d'événements (cela arrive rarement en pratique), ou lorsqu'il n'est pas
nécessaire de poursuivre la simulation parce que le temps modèle ou le temps CPU a atteint une limite donnée,
ou parce que les statistiques ont atteint la précision souhaitée. À ce stade, avant la fermeture du programme,
l'utilisateur souhaitera généralement enregistrer des statistiques dans des fichiers de sortie.

4.1.3 Événements et ordre d'exécution des événements dans OMNeT++

OMNeT++ utilise des messages pour représenter des événements.1 Les messages sont représentés par des
instances de cMessage classe et ses sous-classes. Les messages sont envoyés d'un module à l'autre - cela
signifie que l'endroit où "l'événement se produira" est lemodule de destination du message, et

1À toutes fins pratiques. Notez qu'il existe une classe appeléecÉvénement cette cMessage sous-classes de, mais il n'est utilisé
qu'en interne au noyau de simulation.

50
Manuel de simulation OMNeT++ – Modules simples

l'heure du modèle à laquelle l'événement se produit est la heure d'arrivée du message. Des événements tels que «
timeout expiré » sont implémentés par le module qui s'envoie un message à lui-même.

Les événements sont consommés à partir de la FES dans l'ordre des heures d'arrivée, pour maintenir la causalité. Plus précisément, étant
donné deux messages, les règles suivantes s'appliquent :

1. Le message avec le heure d'arrivée plus tôt est exécuté en premier. Si les heures d'arrivée sont égales,

2. celui avec le priorité de planification plus élevée (petite valeur numérique) est exécuté en premier. Si les
priorités sont les mêmes,

3. celuiprogrammé/envoyé plus tôtest exécuté en premier.

Priorité de planificationest un attribut entier de messages affecté par l'utilisateur.

4.1.4 Temps de simulation

Le temps de simulation actuel peut être obtenu avec lesimHeure() une fonction.
Le temps de simulation dans OMNeT++ est représenté par le type C++simtime_t,qui est par défaut un
typedef pour leSimTimeclasser.SimTimeLa classe stocke le temps de simulation dans un entier 64 bits, en
utilisant une représentation décimale en virgule fixe. La résolution est contrôlée par leexposant d'échelle
variable de configuration globale ; C'est,SimTimeinstances ont la même résolution. L'exposant peut être
choisi entre -18 (résolution attoseconde) et 0 (secondes). Certains exposants avec les plages qu'ils
fournissent sont indiqués dans le tableau suivant.

Exposant Résolution Environ. Varier


- 18 dix−18s (1as) ±9.22s
- 15 dix−15s (1fs) ±153.72minutes
- 12 dix−12s (1ps) ±106.75jours
-9 dix−9s (1ns) ±292.27ans ±
-6 dix−6s (1us) 292271ans
-3 dix−3s (1 ms) ±2.9227e8ans ±2.
0 1s 9227e11ans

Notez que bien que le temps de simulation ne puisse pas être négatif, il est toujours utile de pouvoir représenter
des nombres négatifs, car ils surviennent souvent lors de l'évaluation d'expressions arithmétiques.
Il n'y a pas de conversion implicite deSimTimeàdouble,principalement parce que cela entrerait en
conflit avec des opérations arithmétiques surchargées deSimTime ;Utilisez ledbl()méthode de
SimTimeou laSIM-TIME_DBL()macro à convertir. Pour réduire le besoin dedb(),plusieurs fonctions et
méthodes ont des variantes surchargées qui acceptent directementSimTime,par examplefabs(),
fmod(), div(), ceil(), floor(), uniform(), exponentiel(),et Ordinaire().
D'autres méthodes utiles deSimTimeinclurechaîne(),qui renvoie la valeur sous forme de chaîne ;
analyser (), qui convertit une chaîne enSimTime ; cru(),qui renvoie l'entier 64 bits sous-jacent ;
getScaleExp(),qui renvoie l'exposant de l'échelle globale ; est zéro(), qui teste si le temps de simulation
est 0 ; etgetMaxTime(), qui renvoie le temps de simulation maximal pouvant être représenté à
l'exposant de l'échelle actuelle. Le zéro et le temps maximum de simulation sont également
accessibles via leSIMTIME_ZERO et SIMTIME_MAX macros.
// 340 microsecondes dans le futur, tronqué à la limite de la milliseconde simtime_t timeout =
(simTime() + SimTime(340, SIMTIME_US)).trunc(SIMTIME_MS);

51
Manuel de simulation OMNeT++ – Modules simples

REMARQUE: Convertir un SimTimeàdoublepeut perdre en précision, car doublen'a qu'une mantisse


52 bits. Versions antérieures d'OMNeT++ utiliséesdoublepour le temps de simulation, mais cela a
causé des problèmes dans les longues simulations qui reposaient sur un minutage fin, par exemple
les protocoles MAC. D'autres problèmes étaient l'accumulation d'erreurs d'arrondi et la non-
associativité (souvent (X + y) + z 6= X + (y + z), voir [Gol91]) ce qui signifiait que deux double les temps
de simulation n'ont pas pu être comparés de manière fiable pour l'égalité.

4.1.5 Mise en œuvre de la FES

La mise en œuvre du FES est un facteur crucial dans la performance d'un simulateur à événements discrets. Dans
OMNeT++, le FES est remplaçable et l'implémentation FES par défaut utilisetas binaire comme structure de données. Le
tas binaire est généralement considéré comme le meilleur algorithme FES pour la simulation d'événements discrets, car
il offre de bonnes performances équilibrées pour la plupart des charges de travail. (Des structures de données exotiques
commeliste de sauts peut être plus performant que le tas dans certains cas.)

4.2 Composants, modules simples, canaux

Les modèles de simulation OMNeT++ sont composés de modules et de connexions. Les modules peuvent être des
modules simples (atomiques) ou des modules composés ; les modules simples sont les composants actifs d'un modèle et
leur comportement est défini par l'utilisateur en tant que code C++. Les connexions peuvent avoir des objets de canal
associés. Les objets de canal encapsulent le comportement du canal : modélisation des temps de propagation et de
transmission, modélisation des erreurs, et éventuellement d'autres. Les canaux sont également programmables en C++
par l'utilisateur.

Les modules et les canaux sont représentés par lecModuleet cCanal classes, respectivement. cModule
et cCanal sont tous deux issus de lacComposant classer.
L'utilisateur définit des types de modules simples en sous-classantcModuleSimple.Les modules composés
sont instanciés aveccModule,bien que l'utilisateur puisse le remplacer par @classerdans le fichier NED, et
peut même utiliser une simple classe de module C++ (c'est-à-dire dérivée decSimpleModule)pour un
module composé.
lecCanal'Les sous-classes incluent les trois types de canaux intégrés : cIdealChannel, cDelayChannel et
cDatarateChannel. L'utilisateur peut créer de nouveaux types de canaux en sous-classant cCanal ou
toute autre classe de canal.
Le diagramme d'héritage suivant illustre la relation entre les classes mentionnées ci-dessus.
Des modules et des canaux simples peuvent être programmés en redéfinissant certaines fonctions
membres et en y fournissant votre propre code. Certaines de ces fonctions membres sont déclarées sur
cComposant, la classe de base commune des canaux et des modules.
cComposant a les fonctions membres suivantes destinées à la redéfinition dans les sous-classes :

• initialiser(). Cette méthode est invoquée après qu'OMNeT++ ait configuré le réseau (c'est-à-dire créé
des modules et les ait connectés conformément aux définitions), et fournit un emplacement pour le
code d'initialisation ;

• terminer() est appelé lorsque la simulation s'est terminée avec succès, et son utilisation
recommandée est l'enregistrement de statistiques récapitulatives.

initialiser()et terminer(),ensemble avecinitialiser()'s variantes pour l'initialisation multi-étapes, seront


traitées en détail dans la section 4.3.3.

52
Manuel de simulation OMNeT++ – Modules simples

cObjet

...

cComposant

cModule cCanal

cModuleSimple cIdealChannel cDelayChannel cDatarateChannel

Figure 4.1 : Héritage des classes de composants, de modules et de canaux

Dans OMNeT++, les événements se produisent à l'intérieur de modules simples. Des modules simples encapsulent du code C++
qui génère des événements et réagit aux événements, implémentant le comportement du module.

Pour définir le comportement dynamique d'un module simple, l'une des fonctions membres suivantes doit
être remplacée :

• handleMessage(cMessage *msg).Il est invoqué avec le message en paramètre chaque fois que
le module reçoit un message.handleMessage()devraittraiterle message, puis revenir. Le temps
de simulation ne s'écoule jamais à l'intérieurhandleMessage()appels, uniquement entre eux.

• activité()est lancé comme une coroutine2 au début de la simulation, et il s'exécute jusqu'à la fin de la
simulation (ou jusqu'à ce que la fonction revienne ou se termine autrement). Les messages sont
obtenus avecrecevoir()appels. Le temps de simulation s'écoule à l'intérieurrecevoir() appels.

Modules écrits avecactivité()et handleMessage()peuvent être mélangés librement dans un modèle de


simulation. En général,handleMessage()devrait être préféré àactivité(),en raison de l'évolutivité et
d'autres raisons pratiques. Les deux fonctions seront décrites en détail dans les sections 4.4.1 et 4.4.2,
y compris leurs avantages et inconvénients.
Le comportement des canaux peut également être modifié en redéfinissant les fonctions membres. Cependant, l'API du
canal est légèrement plus compliquée que celle des modules simples, nous la décrirons donc dans une section ultérieure
(4.8).

Mentionnons enfinrafraîchirAffichage(),qui est lié à la mise à jour de l'apparence visuelle de la simulation


lorsqu'elle est exécutée sous une interface utilisateur graphique.rafraîchirAffichage()est couvert dans le
chapitre qui traite de la visualisation de la simulation (8.2).

REMARQUE: rafraîchirAffichage()a été ajouté dans OMNeT++ 5.0. Jusque-là, les tâches liées à la
visualisation étaient généralement mises en œuvre dans le cadre dehandleMessage().
rafraîchirAffichage()fournit une solution bien supérieure et plus efficace.

2Thread planifié en coopération, expliqué plus tard.

53
Manuel de simulation OMNeT++ – Modules simples

4.3 Définir des types de modules simples

4.3.1 Aperçu

Comme mentionné précédemment, un module simple n'est rien de plus qu'une classe C++ qui doit être sous-
classée à partir decModuleSimple,avec une ou plusieurs fonctions membres virtuelles redéfinies pour définir son
comportement.
La classe doit être enregistrée auprès d'OMNeT++ via leDefine_Module() macro. leDefine_Module() la
ligne doit toujours être insérée dans .ccou .cpp fifichiers et non le fichier d'en-tête (.h), car le
compilateur génère du code à partir de celui-ci.
Ce qui suitBonjourModule est à peu près le module simple le plus simple que l'on puisse écrire. (Nous
aurions pu omettre leinitialiser()méthode pour le rendre encore plus petit, mais comment dirait-il bonjour
alors ?) Remarque cModuleSimple comme classe de base, et la Define_Module() ligne.

// fichier : HelloModule.cc
# inclure <omnetpp.h> utilisation de
l'espace de noms omnetpp ;

classerBonjourModule : Publique cModuleSimple {

protégé:
vide virtuel initialiser();
vide virtuel handleMessage(cMessage *msg);
} ;

// enregistre la classe de module avec OMNeT++


Define_Module(HelloModule);

annuler HelloModule::initialize() {

EV << "Bonjour le monde !\n" ;


}

annuler HelloModule::handleMessage(cMessage *msg) {

effacer msg ; // juste jeter tout ce que nous recevons


}

Afin de pouvoir faire référence à ce type de module simple dans les fichiers NED, nous avons également besoin d'une déclaration
NED associée qui pourrait ressembler à ceci :

// fichier : HelloModule.ned
FacileBonjourModule
{
portes:
saisir dans;
}

54
Manuel de simulation OMNeT++ – Modules simples

4.3.2 Constructeur

Les modules simples ne sont jamais instanciés directement par l'utilisateur, mais plutôt par le noyau de
simulation. Cela implique qu'on ne peut pas écrire de constructeurs arbitraires : la signature doit être celle
attendue par le noyau de simulation. Heureusement, ce contrat est très simple : le constructeur doit être
public, et ne doit prendre aucun argument :

Publique:
BonjourModule();// le constructeur ne prend aucun argument

cModuleSimple a lui-même deux constructeurs :

1. cModuleSimple() –un sans arguments

2.cSimpleModule(size_t stacksize) –celui qui accepte la taille de la pile de coroutine

La première version doit être utilisée avechandleMessage()modules simples, et le second avecactivité()


modules. (Avec ce dernier, leactivité()La méthode de la classe de module s'exécute comme une coroutine
qui nécessite une pile CPU séparée, généralement de 16 à 32 Ko. Ceci sera discuté en détail plus tard.)
Passer une taille de pile nulle à ce dernier constructeur sélectionne égalementhandleMessage().
Ainsi, les définitions de constructeur suivantes sont toutes OK, et sélectionnez handleMessage()à utiliser
avec le module :

HelloModule::HelloModule() {...} HelloModule::HelloModule() :


cSimpleModule() {...}

Il est également acceptable d'omettre complètement le constructeur, car celui généré par le compilateur convient également.

La définition de constructeur suivante sélectionne activité()à utiliser avec le module, avec 16K de pile
coroutine :
HelloModule::HelloModule() : cSimpleModule(16384) {...}

REMARQUE: leModule_Class_Members()macro, déjà obsolète dans OMNeT++ 3.2, a été supprimée


dans la version 4.0. Lors du portage d'anciens modèles de simulation, les occurrences de cette macro
peuvent simplement être supprimées du code source.

4.3.3 Initialisation et finalisation

Utilisation de base

leinitialiser()et terminer() les méthodes sont déclarées dans le cadre de cComposant, et fournir à
l'utilisateur la possibilité d'exécuter du code au début et à la fin réussie de la simulation.

La raison initialiser()existe est que vous ne pouvez généralement pas mettre de code lié à la simulation dans le
constructeur de module simple, car le modèle de simulation est toujours en cours de configuration lorsque le
constructeur s'exécute et de nombreux objets requis ne sont pas encore disponibles. En revanche,initialiser() est
appelé juste avant le début de l'exécution de la simulation, alors que tout le reste a déjà été configuré.

terminer() sert à enregistrer des statistiques, et il n'est appelé que lorsque la simulation s'est terminée
normalement. Il n'est pas appelé lorsque les simulations s'arrêtent avec un message d'erreur. le

55
Manuel de simulation OMNeT++ – Modules simples

destructor est toujours appelé à la fin, quelle que soit la façon dont la simulation s'est arrêtée, mais à ce
moment-là, il est juste de supposer que le modèle de simulation a déjà été à moitié démoli.
Sur la base des considérations ci-dessus, les conventions d'utilisation suivantes existent pour ces quatre
méthodes :

Constructeur:
Définissez les membres de pointeur de la classe de module sur nullptr ; reporter toutes les autres tâches
d'initialisation à initialiser().

initialiser():
Effectuer toutes les tâches d'initialisation : lire les paramètres du module, initialiser les variables de classe, allouer des structures
de données dynamiques avec Nouveau; également allouer et initialiser des messages automatiques (minuteries) si nécessaire.

terminer():
Enregistrer les statistiques. Fairene pas effacer quoi que ce soit ou annuler les minuteries - tout le nettoyage doit être effectué
dans le destructeur.

Destructeur :
Supprimez tout ce qui a été alloué parNouveauet est toujours détenu par la classe de module. Avec les
messages automatiques (minuteries), utilisez lesannulerEtSupprimer(msg)une fonction! Il est presque
toujours faux de simplement supprimer un message personnel du destructeur, car il pourrait se trouver
dans la liste des événements programmés. leannulerEtSupprimer(msg)la fonction vérifie d'abord cela et
annule le message avant de le supprimer si nécessaire.

OMNeT++ imprime la liste des objets inédits à la fin de la simulation. Lorsqu'un modèle de simulation
vide"objet non disposé..."messages, cela indique que les destructeurs de modules correspondants
doivent être corrigés. De manière temporaire, ces messages peuvent être masqués en configurant
print-undisposed=falsedans la configuration.

REMARQUE: leeffectuer-gcL'option de configuration a été supprimée dans OMNeT++ 4.0. Le ramasse-


miettes automatique ne peut pas être implémenté de manière fiable, en raison des limitations du
langage C++.

Ordre d'invocation

leinitialiser()les fonctions des modules sont appelées avant de le premier événement est traité, mais après
les événements initiaux (messages de démarrage) ont été placés dans le FES par le noyau de simulation.

Les modules simples et composés ont initialiser()les fonctions. Un module composé initialiser()la
fonction s'exécute avant de celle de ses sous-modules.
leterminer() les fonctions sont appelées lorsque la boucle d'événements s'est terminée, et seulement si elle s'est
terminée normalement.

REMARQUE: terminer() n'est pas appelée si la simulation s'est terminée par une erreur d'exécution.

L'ordre d'appel pour terminer() est l'inverse de l'ordre de initialiser() : fipremiers sous-modules, puis le
module composé englobant. 3
3La façon dont vous pouvez fournir un initialiser()la fonction d'un module composé est de sous-classer cModule,et dites à
OMNeT++ d'utiliser la nouvelle classe pour le module composé. Ce dernier se fait en ajoutant le @classe(<nom de classe>)
propriété dans la déclaration NED.

56
Manuel de simulation OMNeT++ – Modules simples

Ceci est résumé dans le pseudo-code suivant :

effectuer une simulation :


construire un réseau
(c'est-à-dire le module système et ses sous-modules de manière récursive)
insérez des messages de démarrage pour tous les sous-modules en utilisant
activity() do callInitialize() sur le module système
enter event loop // (décrit précédemment)
if (boucle d'événement terminée normalement) // c'est-à-dire pas d'erreurs
faire callFinish() sur le module système
nettoyer

callInitialize()
{
appel à la fonction initialize() définie par l'utilisateur si (le
module est composé)
pour (chaque sous-module)
faire callInitialize() sur le sous-module
}

appelFin()
{
si (le module est composé)
pour (chaque sous-module)
faire callFinish() sur l'appel du sous-
module à la fonction finish() définie par l'utilisateur
}

Garde en tête queterminer() n'est pas toujours appelé, ce n'est donc pas un bon endroit pour le code de
nettoyage qui devrait s'exécuter à chaque fois que le module est supprimé. terminer() n'est qu'un bon endroit
pour écrire des statistiques, le post-traitement des résultats et d'autres opérations qui ne sont censées s'exécuter
qu'en cas de réussite. Le code de nettoyage doit aller dans le destructeur.

Initialisation en plusieurs étapes

Dans les modèles de simulation où l'initialisation en une étape fournie par initialiser()n'est pas suffisant, on
peut utiliser une initialisation en plusieurs étapes. Les modules ont deux fonctions qui peuvent être
redéfinies par l'utilisateur :

vide virtuel initialiser(entierétape); entier


virtuel numInitStages()constante;

Au début de la simulation, initialiser(0) est appelé pour tout modules, puis initialiser(1), initialiser(2),
etc. Vous pouvez y penser comme si l'initialisation se déroulait en plusieurs "vagues". Pour chaque
module,numInitStages()doit être redéfini pour renvoyer le nombre d'étapes d'initialisation requises,
par exemple pour une initialisation en deux étapes,numInitStages()devrait retourner 2, etinitialiser
(étape int)doit être mis en place pour gérerstade=0et étape=1cas.4
lecallInitialize()La fonction effectue l'initialisation complète en plusieurs étapes pour ce module et tous
ses sous-modules.
4Noter laconstantedans lenumInitStages()déclaration. Si vous l'oubliez, selon les règles C++, vous créez undifférentfonction au
lieu de redéfinir celle existante dans la classe de base, ainsi celle existante restera en vigueur et renverra 1.

57
Manuel de simulation OMNeT++ – Modules simples

Si vous ne redéfinissez pas les fonctions d'initialisation en plusieurs étapes, le comportement par défaut est
l'initialisation en une seule étape : la valeur par défautnumInitStages()renvoie 1, et la valeur par défautinitialiser
(étape int)appelle simplementinitialiser().

Événement "Fin de simulation"

La tâche determiner() est implémenté dans plusieurs autres simulateurs en introduisant un fin de
simulationun événement. Ce n'est pas une très bonne pratique car le programmeur de simulation doit
coder les modèles (souvent représentés par des FSM) afin qu'ils puissenttoujours répondre correctement
aux événements de fin de simulation, quel que soit leur état. Cela complique souvent inutilement le code
du programme. Pour cette raison, OMNeT++ n'utilise pas l'événement de fin de simulation.

Cela se voit également dans la conception du langage de simulation PARSEC (UCLA). Son prédécesseur Maisie
utilisait des événements de fin de simulation, mais - comme documenté dans le manuel PARSEC - cela a conduit à
une programmation maladroite dans de nombreux cas, donc pour PARSEC les événements de fin de simulation
ont été abandonnés au profit determiner() (appelé finaliser() en PARSEC).

4.4 Ajout de fonctionnalités au cSimpleModule

Cette section traite cModuleSimple'est mentionné précédemment handleMessage()et activité()


fonctions membres, destinées à être redéfinies par l'utilisateur.

4.4.1 handleMessage()

Fonction appelée pour chaque événement

L'idée est qu'à chaque événement (arrivée de message) nous appelons simplement une fonction définie par
l'utilisateur. Cette fonction,handleMessage(cMessage *msg)est une fonction membre virtuelle decModuleSimple
qui ne fait rien par défaut - l'utilisateur doit le redéfinir dans des sous-classes et ajouter le code de traitement du
message.
lehandleMessage()La fonction sera appelée pour chaque message qui arrive au module. La fonction
doit traiter le message et revenir immédiatement après cela. Le temps de simulation est
potentiellement différent à chaque appel. Aucun temps de simulation ne s'écoule dans un appel à
handleMessage().
La boucle d'événements à l'intérieur du simulateur gère à la foisactivité()et handleMessage()modules
simples, et il correspond au pseudocode suivant :

while (FES non vide et simulation pas encore terminée) {

récupérer le premier événement de FES t:=


horodatage de cet événement m:= module
contenant cet événement if (m fonctionne
avec handleMessage())
m->handleMessage( événement )
else // m fonctionne avec activity()
transferTo( m )
}

58
Manuel de simulation OMNeT++ – Modules simples

modules avechandleMessage()ne sont PAS démarrés automatiquement : le noyau de simulation crée des
messages de démarrage uniquement pour les modules avecactivité().Cela signifie que vous devez programmer
des messages personnels à partir duinitialiser()fonction si vous voulez unhandleMessage()module simple pour
commencer à travailler "par lui-même", sans recevoir au préalable de message des autres modules.

Programmation avec handleMessage()

Pour utiliser lehandleMessage()mécanisme dans un module simple, vous devez spécifiertaille de pile
nullepour le module. Ceci est important, car cela indique à OMNeT++ que vous souhaitez utiliser
handleMessage()et pasactivité().
Fonctions liées aux messages/événements que vous pouvez utiliser danshandleMessage() :

• envoyer()famille de fonctions - pour envoyer des messages à d'autres modules

• scheduleAt() –programmer un événement (le module "s'envoie un message à lui-même")

• annulerEvent() –pour supprimer un événement programmé avecscheduleAt()

lerecevoir()et attendre()les fonctions ne peuvent pas être utilisées dans handleMessage(), car ils sont basés
sur la coroutine par nature, comme expliqué dans la section sur activité().
Vous devez ajouter des membres de données à la classe de module pour chaque élément d'information que vous
souhaitez conserver. Ces informations ne peuvent pas être stockées dans des variables locales de
handleMessage()car ils sont détruits au retour de la fonction. De plus, ils ne peuvent pas être stockés dans des
variables statiques de la fonction (ou de la classe), car ils seraient partagés entre toutes les instances de la classe.
Les membres de données à ajouter à la classe de module incluront généralement des éléments tels que :

• état (par exemple IDLE/BUSY, CONN_DOWN/CONN_ALIVE/...)

• d'autres variables qui appartiennent à l'état du module : nombre de tentatives, files d'attente de paquets,
etc.

• valeurs récupérées/calculées une seule fois puis stockées : valeurs des paramètres du module, indices de
porte, informations de routage, etc.

• des pointeurs d'objets message créés une seule fois puis réutilisés pour les timers, timeouts, etc.

• variables/objets pour la collecte de statistiques

Ces variables sont souvent initialisées à partir duinitialiser(), car les informations nécessaires pour obtenir la
valeur initiale (par exemple, les paramètres du module) peuvent ne pas encore être disponibles au moment où le
constructeur du module s'exécute.
Une autre tâche à accomplir dansinitialiser()est de programmer le(s) événement(s) initial(aux) qui déclenche(nt) le(s)
premier(s) appel(s) àhandleMessage().Après le premier appel,handleMessage()doit prendre soin de programmer d'autres
événements pour lui-même afin que la "chaîne" ne soit pas rompue. La planification d'événements n'est pas nécessaire si
votre module ne doit réagir qu'aux messages provenant d'autres modules.

terminer() est normalement utilisé pour enregistrer les informations statistiques accumulées dans les données membres
de la classe à la fin de la simulation.

59
Manuel de simulation OMNeT++ – Modules simples

Champ d'application

handleMessage()est dans la plupart des cas un meilleur choix queactivité():

1. Lorsque vous prévoyez d'utiliser le module dans de grandes simulations, impliquant plusieurs milliers
de modules. Dans de tels cas, les piles de modules requises paractivité()consommerait simplement
trop de mémoire.

2. Pour les modules qui conservent peu ou pas d'informations d'état, comme les récepteurs de paquets,
handleMessage()est plus pratique à programmer.

3. D'autres bons candidats sont les modules avec un grand espace d'états et de nombreuses possibilités de
transition d'états arbitraires (c'est-à-dire lorsqu'il existe de nombreux états ultérieurs possibles pour tout
état). De tels algorithmes sont difficiles à programmer avecactivité(),et mieux adapté pour handleMessage()
(voir règle générale ci-dessous). C'est le cas de la plupart des protocoles de communication.

Exemple 1 : Modèles de protocole

Les modèles de couches de protocole dans un réseau de communication ont tendance à avoir une structure commune à
un niveau élevé car, fondamentalement, ils doivent tous réagir à trois types d'événements : aux messages provenant de
protocoles de couche supérieure (ou applications), aux messages provenant de protocoles de couche inférieure. (du
réseau) et à divers temporisateurs et délais d'attente (c'est-à-dire des messages automatiques).

Cela se traduit généralement par le modèle de code source suivant :

classerProtocole Foo :Publique cModuleSimple {

protégé:
// variables d'état
// ...

vide virtuel processMsgFromHigherLayer(cMessage *paquet); vide virtuel


processMsgFromLowerLayer(FooPacket *paquet); vide virtuel
processTimer(cMessage *timer);

vide virtuel initialiser();


vide virtuel handleMessage(cMessage *msg);
} ;

// ...

annuler FooProtocol::handleMessage(cMessage *msg) {

si(msg->isSelfMessage())
processTimer(msg);
sinon si(msg->arrivedOn("fromNetw"))
processMsgFromLowerLayer(check_and_cast<FooPacket *>(msg));
autre
processMsgFromHigherLayer(msg);
}

60
Manuel de simulation OMNeT++ – Modules simples

Les fonctionsprocessMsgFromHigherLayer(), processMsgFromLowerLayer()et processTimer()sont ensuite


généralement divisés davantage : il existe des méthodes distinctes pour traiter des types de paquets
distincts et des temporisateurs distincts.

Exemple 2 : Générateurs et puits de trafic simples

Le code pour les générateurs de paquets simples et les puits programmés avechandleMessage()peut être
aussi simple que le pseudo-code suivant :

PacketGenerator::handleMessage(msg) {

créeret envoyer unNouveaupaquet;


planifier à nouveau msg pour déclencher le prochain appel à handleMessage ;
}

PacketSink::handleMessage(msg) {

effacer msg ;
}

Noter queGénérateur de paquetsdevra redéfinirinitialiser()créermet planifier le premier événement.

Le module simple suivant génère des paquets avec un temps inter-arrivée exponentiel. (Certains
détails dans la source n'ont pas encore été discutés, mais le code est probablement compréhensible
néanmoins.)

classerGénérateur :Publique cModuleSimple {

Publique:
Générateur() : cSimpleModule() {} protégé:

vide virtuel initialiser();


vide virtuel handleMessage(cMessage *msg);
} ;

Define_Module(Générateur);

annuler Générateur :: initialize() {

// planifier le premier envoi


scheduleAt(simTime(),NouveaucMessage);
}

annuler Generator::handleMessage(cMessage *msg) {

// génère et envoie un paquet


cMessage *pkt =NouveaucMessage ;
envoyer(pkt, "sortir");
// planifier le prochain appel scheduleAt(simTime()
+exponentiel(1.0), msg);
}

61
Manuel de simulation OMNeT++ – Modules simples

Exemple 3 : Générateur de trafic en rafale

Un exemple un peu plus réaliste consiste à réécrire notre générateur pour créer des rafales de paquets, chacun
composé deburstLengthpaquets.
Nous ajoutons quelques membres de données à la classe :

• burstLengthstockera le paramètre qui spécifie le nombre de paquets qu'une rafale doit contenir,

• burstCountercomptera le nombre de paquets restant à envoyer dans la rafale actuelle.

Le code:

classerBurstyGenerator :Publique cModuleSimple {

protégé:
entierburstLength ;
entierburstCounter ;

vide virtuel initialiser();


vide virtuel handleMessage(cMessage *msg);
} ;

Define_Module(BurstyGenerator);

annuler BurstyGenerator::initialize() {

// paramètres d'initialisation et variables d'état


burstLength = par("burstLength"); burstCounter =
burstLength ;
// planifie le premier paquet de la première rafale
scheduleAt(simTime(),NouveaucMessage);
}

annuler BurstyGenerator::handleMessage(cMessage *msg) {

// génère et envoie un paquet


cMessage *pkt =NouveaucMessage ;
envoyer(pkt, "sortir");
// si c'était le dernier paquet de la rafale si(--burstCounter
== 0) {
// planifier la prochaine rafale burstCounter = burstLength ;
scheduleAt(simTime()+exponentiel(5.0), msg);

}
autre {
// programmer le prochain envoi dans la rafale
scheduleAt(simTime()+exponentiel(1.0), msg);
}
}

62
Manuel de simulation OMNeT++ – Modules simples

Avantages et inconvénients de l'utilisation handleMessage()

Avantages:

• consomme moins de mémoire : aucune pile séparée n'est nécessaire pour les modules simples

• rapide : l'appel de fonction est plus rapide que la commutation entre les coroutines

Les inconvénients:

• les variables locales ne peuvent pas être utilisées pour stocker des informations d'état

• besoin de redéfinir initialiser()

D'habitude, handleMessage()devrait être préféré à activité().

Autres simulateurs

De nombreux packages de simulation utilisent une approche similaire, souvent surmontée de quelque chose comme une
machine à états (FSM) qui masque les appels de fonction sous-jacents. De tels systèmes sont :

• OPNETMTqui utilise des FSM conçus à l'aide d'un éditeur graphique ;


• NetSim++ clone l'approche d'OPNET ;
• SMURPH (Université de l'Alberta) définit un langage (quelque peu éclectique) pour décrire les FSM, et utilise
un précompilateur pour le transformer en code C++ ;

• Ptolémée (UC Berkeley) utilise une méthode similaire.

Le support FSM d'OMNeT++ est décrit dans la section suivante.

4.4.2 activité()

Description du style de processus

Avecactivité(),un module simple peut être codé comme un processus ou un thread du système d'exploitation. On
peut attendre un message entrant (événement) à n'importe quel moment du code, suspendre l'exécution
pendant un certain temps (temps modèle !), etc. Lorsque leactivité()la fonction se termine, le module est terminé.
(La simulation peut continuer s'il y a d'autres modules qui peuvent fonctionner.)
Les fonctions les plus importantes qui peuvent être utilisées dans activité()sont (ils seront discutés en détail
plus tard):

• recevoir() - recevoir des messages (événements)


• attendre() - pour suspendre l'exécution pendant un certain temps (temps du modèle)

• envoyer()famille de fonctions - pour envoyer des messages à d'autres modules

• scheduleAt() –programmer un événement (le module "s'envoie un message à lui-même")


• annulerEvent() –pour supprimer un événement programmé avecscheduleAt()
• finir() - pour terminer l'exécution de ce module (identique à la sortie du activité()une fonction)

leactivité()fonction contient normalement une boucle infinie, avec au moins un attendre()ou recevoir()
appeler dans son corps.

63
Manuel de simulation OMNeT++ – Modules simples

Champ d'application

Généralement, vous devriez préférer handleMessage()àactivité().Le principal problème avec activité()est


qu'il ne s'adapte pas car chaque module a besoin d'une pile de coroutines distincte. Il a également été
observé queactivité()n'encourage pas un bon style de programmation, et la commutation de pile confond
également de nombreux débogueurs.
Il y a un scénario où activité()'La description de style processus est pratique : lorsque le processus a de nombreux
états mais que les transitions sont très limitées, c'est-à-dire de n'importe quel état, le processus ne peut passer
qu'à un ou deux autres états. C'est par exemple le cas lors de la programmation d'une application réseau, qui
utilise une seule connexion réseau. Le pseudocode de l'application qui communique avec un protocole de couche
transport pourrait ressembler à ceci :

activité()
{
tandis que (vrai)
{
ouvrir la connexion en envoyant la commande OPEN à la couche transport
recevoir la réponse de la couche transport
si (ouverture échouée) {

attendre (quelque temps)


continue // boucle vers while()
}

tandis que (il y a plus à faire) {

envoyer des données sur la connexion réseau


si (connexion interrompue)
{
continue la boucle externe // boucle vers l'extérieur tandis que ()
}
attendre (quelque temps)
recevoir des données sur la connexion réseau si
(connexion interrompue)
{
continue la boucle externe // boucle vers l'extérieur tandis que ()
}
attendre (quelque temps)
}
fermez la connexion en envoyant la commande CLOSE à la couche de transport
si (fermeture non réussie)
{
// gérer l'erreur
}
attendre (quelque temps)
}
}

S'il est nécessaire de gérer plusieurs connexions simultanément, la création dynamique de modules simples pour
gérer chacune est une option. La création de modules dynamiques sera abordée plus tard.
Il y a des situations où vous avez certainementne veut pasutiliseractivité().Si laactivité() la fonction ne
contient pasattendre()et il n'en a qu'unrecevoir()en haut d'une messagerie

64
Manuel de simulation OMNeT++ – Modules simples

boucle, il ne sert à rien d'utiliseractivité(),et le code doit être écrit avechandleMessage().Le corps de la
boucle deviendrait alors le corps dehandleMessage(), variables d'état à l'intérieuractivité()
deviendraient des membres de données dans la classe de module, et ils seraient initialisés dans
initialiser().
Exemple:
annuler Couler :: activité ()
{
tandis que(vrai) {
msg = recevoir();
effacer msg ;
}
}

devrait plutôt être programmé comme :

annuler Récepteur ::handleMessage(cMessage { * message)

effacer msg ;
}

Activity () est exécuté en tant que coroutine

activité()est exécuté dans une coroutine. Les coroutines sont similaires aux threads, mais sont
planifiées de manière non préventive (ceci est également appelé multitâche coopératif). On peut
passer d'une coroutine à une autre coroutine par untransferTo(otherCoroutine) appel, provoquant la
suspension de la première coroutine et l'exécution de la seconde. Plus tard, lorsque la deuxième
coroutine effectue unetransferTo(firstCoroutine) appel à la première, l'exécution de la première
coroutine reprendra à partir du point de transferTo(otherCoroutine) appeler. L'état complet de la
coroutine, y compris les variables locales, est préservé pendant que le fil d'exécution se trouve dans
d'autres coroutines. Cela implique que chaque coroutine a sa propre pile CPU, etTransférer à()
implique un passage d'une pile CPU à une autre.
Les coroutines sont au cœur d'OMNeT++, et le programmeur de simulation n'a jamais besoin d'appeler Transférer à() ou
d'autres fonctions dans la bibliothèque de coroutines, et il n'a pas non plus besoin de se soucier de l'implémentation de
la bibliothèque de coroutines. Il est important de comprendre, cependant, comment la boucle d'événements trouvée
dans les simulateurs d'événements discrets fonctionne avec les coroutines.

Lors de l'utilisation de coroutines, la boucle d'événements ressemble à ceci (simplifié) :

while (FES non vide et simulation pas encore terminée) {

récupérer le premier événement de FES t :=


horodatage de cet événement transferTo(module
contenant l'événement)
}

Autrement dit, lorsqu'un module a un événement, le noyau de simulation transfère le contrôle à la


coroutine du module. On s'attend à ce que lorsque le module "décide qu'il a terminé le traitement de
l'événement", il retransfère le contrôle au noyau de simulation par untransferTo(main) appeler. Au départ,
des modules simples utilisantactivité()sont "amorcés" par des événements ("messages de démarrage")
inséré dans le FES par le noyau de simulation avant le début de la simulation.

65
Manuel de simulation OMNeT++ – Modules simples

Comment la coroutine sait-elle qu'elle a "terminé le traitement de l'événement" ? La réponse:lorsqu'il


demande un autre événement. Les fonctions qui demandent des événements au noyau de simulation sont
lesrecevoir()et attendre(),donc leurs implémentations contiennent untransferTo(main)appeler quelque part.

Leur pseudocode, tel qu'implémenté dans OMNeT++ :

recevoir()
{
transferTo(main)
récupérer l'événement en cours
renvoie l'événement // rappelez-vous : événements = messages
}

attendre()
{
créer un événement e
programmez-le à (heure sim. actuelle + intervalle d'attente)
transferTo(main)
récupérer l'événement en cours
si (l'événement en cours n'est pas e) {
Erreur
}
supprimer e // note : impl réel. réutilise les événements
retour
}

Ainsi, lerecevoir()et attendre()les appels sont des points spéciaux dans leactivité()fonction, parce qu'ils
sont là où

• le temps de simulation s'écoule dans le module, et

• d'autres modules ont une chance de s'exécuter.

Messages de démarrage

Modules écrits avecactivité()besoin de messages de démarrage pour "démarrer". Ces messages de


démarrage sont automatiquement insérés dans le FES par OMNeT++ au début de la simulation, avant
mêmeinitialiser()les fonctions sont appelées.

Taille de la pile de coroutines

Le programmeur de simulation doit définir la taille de la pile CPU pour les coroutines. Cela ne peut pas être
automatisé.
16 ou 32 Ko est généralement un bon choix, mais plus d'espace peut être nécessaire si le module utilise des fonctions
récursives ou a de nombreuses/grandes variables locales. OMNeT++ possède un mécanisme intégré qui détecte
généralement si la pile de modules est trop petite et déborde. OMNeT++ peut également signaler la quantité d'espace de
pile qu'un module utilise réellement au moment de l'exécution.

66
Manuel de simulation OMNeT++ – Modules simples

initialize() et finish() avec activity()

Parce que les variables locales deactivité()sont conservés à travers les événements, vous pouvez tout y
stocker (informations d'état, tampons de paquets, etc.). Les variables locales peuvent être initialisées en
haut duactivité()fonction, donc il n'y a pas beaucoup besoin d'utiliser initialiser().
Vous avez besoin terminer(),cependant, si vous voulez écrire des statistiques à la fin de la simulation. Parce
queterminer() ne peut pas accéder aux variables locales de activité(),vous devez mettre les variables et les
objets contenant les statistiques dans la classe du module. Tu n'as toujours pas besoin initialiser()parce que
les membres de la classe peuvent également être initialisés en haut de activité().
Ainsi, une configuration typique ressemble à ceci en pseudocode :

classe MonModuleSimple... {

...
variables pour l'activité de collecte de
statistiques();
terminer();
} ;

MonModuleSimple::activité() {

déclarer des variables locales et les initialiser initialiser


les variables de collecte de statistiques

tandis que (vrai)


{
...
}
}

MonModuleSimple::finish() {

enregistrer les statistiques dans le fichier


}

Avantages et inconvénients de l'utilisation activité()

Avantages:

• initialiser()pas nécessaire, l'état peut être stocké dans des variables locales de activité()
• la description de style processus est un modèle de programmation naturel dans certains cas

Les inconvénients:

• évolutivité limitée : les piles de coroutines peuvent augmenter de manière inacceptable les besoins en
mémoire du programme de simulation s'il y en a beaucoup activité()-modules simples basés;
• surcharge d'exécution : la commutation entre les coroutines est plus lente qu'un simple appel de fonction

• n'encourage pas un bon style de programmation : à mesure que la complexité des modules augmente, activité()
tend à devenir une grande fonction monolythique.

Dans la plupart des cas, les inconvénients l'emportent sur les avantages et il est préférable d'utiliser handleMessage()plutôt.

67
Manuel de simulation OMNeT++ – Modules simples

Autres simulateurs

Les coroutines sont utilisées par un certain nombre d'autres packages de simulation :

• Tous les logiciels de simulation qui héritent de SIMULA (par exemple C++SIM) sont basés sur des
coroutines, bien que dans l'ensemble le modèle de programmation soit assez différent.

• Le langage de simulation/programmation parallèle Maisie et son successeur PARSEC (de UCLA) utilisent
également des coroutines (bien qu'implémentées avec des threads préemptifs « normaux »). La philosophie
est assez similaire à OMNeT++. PARSEC, étant "juste" un langage de programmation, il a une syntaxe plus
élégante mais beaucoup moins de fonctionnalités que OMNeT++.

• De nombreuses bibliothèques de simulation basées sur Java sont basées sur des threads Java.

4.4.3 Comment éviter les variables globales

Si possible, évitez d'utiliser des variables globales, y compris des membres de classe statiques. Ils sont susceptibles de
causer plusieurs problèmes. Premièrement, ils ne sont pas réinitialisés à leurs valeurs initiales (à zéro) lorsque vous
reconstruisez la simulation dans Tkenv/Qtenv, ou démarrez une autre exécution dans Cmdenv. Cela peut produire des
résultats surprenants. Deuxièmement, ils vous empêchent de paralléliser la simulation. Lors de l'utilisation de la
simulation parallèle, chaque partition du modèle s'exécute dans un processus séparé, ayant ses propres copies de
variables globales. Ce n'est généralement pas ce que vous voulez.

La solution consiste à encapsuler les variables dans des modules simples en tant que membres de données privés
ou protégés, et à les exposer via des méthodes publiques. D'autres modules peuvent ensuite appeler ces
méthodes publiques pour obtenir ou définir les valeurs. Les méthodes d'appel d'autres modules seront abordées
dans la section 4.12. Des exemples de tels modules sont lesTableau noirdans leCadre de mobilité, et Table
d'interface et Table de routage dans leCadre INET.

4.4.4 Réutilisation du code de module via le sous-classement

Le code des modules simples peut être réutilisé via le sous-classement et la redéfinition des fonctions membres
virtuelles. Un exemple:

classerTransportProtocolExt : Publique Protocole de transport {

protégé:
vide virtuel recalculateTimeout();
} ;

Define_Module(TransportProtocolExt);

annuler TransportProtocolExt::recalculateTimeout() {

//...
}

La déclaration NED correspondante :


FacileTransportProtocolExt s'étendProtocole de transport {

@class(TransportProtocolExt); // Important!
}

68
Manuel de simulation OMNeT++ – Modules simples

REMARQUE: Noter la @classer() propriété, qui indique à OMNeT++ d'utiliser la propriété


TransportProtocolExt Classe C++ pour le type de module ! Il est nécessaire car l'héritage NED est
l'héritage NEDseul, donc sans @classer() la TransportProtocolExt Le type NED hériterait de la
classe C++ de son type NED de base.

4.5 Accès aux paramètres du module

Les paramètres de module déclarés dans les fichiers NED sont représentés par le cPar classe au moment de
l'exécution, et accessible en appelant la par() fonction membre de cComposant :

cPar& delayPar = par("delay");

cPar'La valeur de s peut être lue avec des méthodes correspondant au type NED du paramètre : boolValue(),
longValue(), doubleValue(), stringValue(), stdstringValue(), xmlValue(). Il existe également des opérateurs de
conversion de type surchargés pour les types correspondants (boo; types entiers, y compris entier, long, etc;
double; caractère const * ; cXMLElement *).
long numJobs = par("numJobs").longValue();
doubletraitementDelay = par("processingDelay"); // en utilisant l'opérateur double()

Noter quecPar a deux méthodes pour renvoyer une valeur de chaîne : valeur de chaîne(), qui revient
caractère const *, et stdstringValue(), qui revient std :: chaîne. Pour les paramètres volatils,
uniquement stdstringValue() peuvent être utilisés, mais sinon les deux sont interchangeables.
Si vous utilisez le par("foo") paramètre dans les expressions (telles que 4*par("foo")+2), le compilateur
C++ peut être incapable de décider entre les opérateurs surchargés et signaler l'ambiguïté. Ce
problème peut être résolu en ajoutant un cast explicite tel que (double)par("foo"), ou en utilisant le
doubleValeur() ou valeurlong() méthodes.

4.5.1 Paramètres volatils et non volatils

Un paramètre peut être déclaré volatildans le fichier NED. levolatilmodificateur indique qu'un paramètre est relu
chaque fois qu'une valeur est nécessaire pendant la simulation. Les paramètres volatils sont généralement
utilisés pour des choses comme l'intervalle de génération de paquets aléatoires, et se voient attribuer des valeurs
telles queexponentiel(1.0) (nombres tirés de la distribution exponentielle avec une moyenne de 1,0).
En revanche, les paramètres NED non volatils sont des constantes et la lecture de leurs valeurs plusieurs
fois garantit la même valeur. Lorsqu'un paramètre non volatil se voit attribuer une valeur aléatoire comme
exponentiel(1.0), il est évalué une fois au début de la simulation et remplacé par le résultat, de sorte que
toutes les lectures obtiendront la même valeur (générée de manière aléatoire).
L'utilisation typique des paramètres non volatils est de les lire dans le initialiser()de la classe du module et
stockez les valeurs dans des variables de classe pour un accès facile ultérieurement :
classerLa source : Publique cModuleSimple {

protégé:
long nombre d'emplois ;
vide virtuel initialiser(); . . .

} ;

annuler Source :: initialize () {

69
Manuel de simulation OMNeT++ – Modules simples

nombreJobs =
par("nombreJobs"); . . .
}

volatilles paramètres doivent être relus chaque fois que la valeur est nécessaire. Par exemple, un
paramètre qui représente un intervalle de génération de paquet aléatoire peut être utilisé comme ceci :
annuler Source::handleMessage(cMessage *msg) {

...
scheduleAt(simTime() + par("interval").doubleValue(), timerMsg); . . .

Ce code recherche le paramètre par son nom à chaque fois. Cette recherche peut être évitée en stockant le
pointeur de l'objet paramètre dans une variable de classe, ce qui donne le code suivant :
classerLa source : Publique cModuleSimple {

protégé:
cPar *intervalle ;
vide virtuel initialiser();
vide virtuel handleMessage(cMessage *msg); . . .

} ;

annuler Source :: initialize () {

intervallep = &par("intervalle"); . . .

annuler Source::handleMessage(cMessage *msg) {

...
scheduleAt(simTime() + intervalp->doubleValue(), timerMsg); . . .

4.5.2 Modification de la valeur d'un paramètre

Les valeurs des paramètres peuvent être modifiées à partir du programme, pendant l'exécution. Ceci est rarement nécessaire,
mais peut être utile pour certains scénarios.

REMARQUE: Le type du paramètre ne peut pas être modifié lors de l'exécution – il doit rester le type déclaré dans
le fichier NED. Il n'est pas non plus possible d'ajouter ou de supprimer des paramètres de module lors de
l'exécution.

Les méthodes pour définir la valeur du paramètre sont setBoolValue(), setLongValue(), setString-
Value(), setDoubleValue(), setXMLValue(). Il existe également des opérateurs d'affectation surchargés
pour différents types, notamment booléen, int, long, double, const char *, et cXMLElement
*.

70
Manuel de simulation OMNeT++ – Modules simples

Pour autoriser un module à être notifié des changements de paramètres, remplacez son handleParameter-
Change() méthode, voir 4.5.5.

4.5.3 Autres méthodes cPar

Le nom et le type du paramètre sont retournés par le obtenirNom() et getType() méthodes. Ce dernier
renvoie une valeur à partir d'une énumération, qui peut être convertie en une chaîne lisible avec le
getTypeName() méthode statique. Les valeurs d'énumération sontBOOL, DOUBLE, LONG, CHAÎNE et XML ;
et puisque l'énumération est un type interne, ils doivent généralement être qualifiés avec cPar ::.
estvolatile() renvoie si le paramètre a été déclaré volatile dans le fichier NED. estNumérique() renvoie
true si le type de paramètre est double ou long.
lechaîne() La méthode renvoie la valeur du paramètre sous forme de chaîne. Si le paramètre contient une
expression, la représentation sous forme de chaîne de l'expression est renvoyée.
Un exemple d'utilisation des méthodes ci-dessus :

entiern = getNumParams(); pour(


entierje = 0 ; je < n ; je++) {

cPar& p = par(i);
EV << "paramètre : " << p.getName() << "\n" ; EV << "
type :" << cPar::getTypeName(p.getType()) << "\n" ; contient :" <<
EV << " p.str() << "\n" ;
}

Les propriétés NED d'un paramètre sont accessibles avec la getProperties() méthode qui renvoie un
pointeur vers cPropriétés objet qui stocke les propriétés de ce paramètre. Spécifiquement,getUnit()
renvoie l'unité de mesure associée au paramètre (@unité propriété dans NED).

Davantage cPar méthodes et classes associées comme cExpression et cDynamicExpressionsont utilisés par
l'infrastructure NED pour configurer et attribuer des paramètres. Ils sont documentés dans le Référence API,
mais ils présentent normalement peu d'intérêt pour les utilisateurs.

4.5.4 Émulation de tableaux de paramètres

Depuis la version 4.2, OMNeT++ ne prend pas en charge les tableaux de paramètres, mais en pratique, ils peuvent
être émulés à l'aide de paramètres de chaîne. On peut assigner au paramètre une chaîne qui contient toutes les
valeurs sous forme textuelle (par exemple, "0 1,234 3,95 5,467"),puis analysez cette chaîne dans le module simple.

lecStringTokenizerclass peut être très utile à cette fin. Le constructeur accepte une chaîne, qu'il
considère comme une séquence de jetons (mots) séparés par des caractères délimiteurs (par défaut,
des espaces). Ensuite, vous pouvez soit énumérer les jetons et les traiter un par un (hasMoreTokens(),
nextToken()),ou utilisez l'un descStringTokenizerdes méthodes pratiques pour les convertir en un
vecteur de chaînes (asVector()),entiers (asIntVector()),ou doubles (asDoubleVector()).

Ces dernières méthodes peuvent être utilisées comme ceci :

caractère constant*vstr = par("v").stringValue();// par exemple "aa bb cc" ;


std::vector<std::string> v = cStringTokenizer(vstr).asVector();

et

71
Manuel de simulation OMNeT++ – Modules simples

caractère constant*chaîne = "34 42 13 46 72 41" ; std :: vecteur <entier


> v = cStringTokenizer().asIntVector();

caractère constant*chaîne = "0,4311 0,7402 0,7134" ;


std :: vecteur <double> v = cStringTokenizer().asDoubleVector();

L'exemple suivant traite la chaîne en énumérant les jetons :


caractère constant*chaîne = "3,25 1,83 34 X 19,8" ;// saisir

std :: vecteur <double> résultat ; jetoniseur


cStringTokenizer(str); tandis que(
tokenizer.hasMoreTokens()) {

caractère constant*jeton = tokenizer.nextToken(); si(


strcmp(token, "X")==0)
result.push_back(DEFAULT_VALUE);
autre
result.push_back(atof(token));
}

4.5.5 handleParameterChange()

Il est possible que les modules soient avertis lorsque la valeur d'un paramètre change au moment de l'exécution,
éventuellement en raison d'un autre module qui le modifie dynamiquement. Une utilisation typique consiste à relire le
paramètre modifié et à mettre à jour l'état du module si nécessaire.

Pour activer la notification, redéfinissez le handleParameterChange() méthode de la classe du module.


Cette méthode sera rappelée par le noyau de simulation lors d'un changement de paramètre d'un module,
sauf lors de l'initialisation du module donné.

REMARQUE: Les notifications sont désactivées lors de l'initialisation du composant, car elles
rendraient très difficile l'écriture de composants qui fonctionnent de manière fiable dans toutes les
conditions. handleParameterChange() est généralement déclenché à partir d'un autre module (il n'est
pas très logique qu'un module modifie ses propres paramètres), donc l'ordre relatif de initialiser()et
handleParameterChange() serait effectivement déterminé par l'ordre d'initialisation des modules, sur
lequel on ne peut généralement pas compter. Une fois la dernière étape de l'initialisation du
composant terminée,handleParameterChange() est appelé par le noyau de simulation avec nullptr
comme nom de paramètre. Cela permet au composant de réagir aux changements de paramètres qui
se sont produits pendant la phase d'initialisation.

La signature de la méthode est la suivante :

annuler handleParameterChange(caractère constant*le nom du paramètre);

L'exemple suivant montre un module qui relit son temps de serviceparamètre lorsque sa valeur
change :
annuler File d'attente ::handleParameterChange(caractère constant*nom de famille) {

si(strcmp(parname, "serviceTime")==0)
serviceTime = par("serviceTime"); // actualiser le membre de données
}

72
Manuel de simulation OMNeT++ – Modules simples

Si votre code dépend fortement des notifications et que vous souhaitez également recevoir des
notifications lors de l'initialisation ou de la finalisation, une solution consiste à appeler explicitement
handleParameter-Change() du initialiser()ou terminer() une fonction:
pour(entierje = 0 ; je < getNumParams(); je++)
handleParameterChange(par(i).getName());

REMARQUE: Soyez extrêmement prudent lorsque vous modifiez les paramètres de l'intérieur
handleParameter-Change(), car il est facile de créer accidentellement une boucle de notification infinie.

4.6 Accès aux portails et aux connexions

4.6.1 Objets porte

Les portes des modules sont représentées par cGateobjets. Les objets porte savent à quelles autres
portes ils sont connectés et quels sont les objets canal associés aux liens.

Accéder aux portails par nom

lecModuleLa classe a un certain nombre de fonctions membres qui traitent des portes. Vous pouvez rechercher une
porte par son nom en utilisant leportail()méthode:

cGate *outGate = gate("out");

Cela fonctionne pour les portes d'entrée et de sortie. Cependant, lorsqu'une porte a été déclaréeinoutdans
NED, il est en fait représenté par le noyau de simulation avec deux portes, donc l'appel ci-dessus se
traduirait par un portail introuvable Erreur. leportail()La méthode doit savoir si l'entrée ou la sortie de la
moitié de la porte dont vous avez besoin. Cela peut être fait en ajoutant le "$je" ou "$o" au nom de la porte.
L'exemple suivant récupère les deux portes pour la porte inout "g":
cGate *gIn = gate("g$i"); cGate
*gOut = gate("g$o");

Une autre façon consiste à utiliser le gateHalf() fonction, qui prend le nom de la porte inout plus soit
cGate::INPUT ou cGate::SORTIE :
cGate *gIn = gateHalf("g", cGate::INPUT); cGate *gOut =
gateHalf("g", cGate::OUTPUT);

Ces méthodes génèrent une erreur si la porte n'existe pas, elles ne peuvent donc pas être utilisées
pour déterminer si le module a une porte particulière. A cet effet il existe unaPorte()méthode. Un
exemple:
si (hasGate("optOut"))
envoyer(NouveaucMessage(), "optOut");

Une porte peut également être identifiée et recherchée par un ID de porte numérique. Vous pouvez obtenir l'ID
de la porte elle-même (getId()méthode), ou depuis le module par nom de porte (findGate()méthode). le portail()La
méthode a également une variante surchargée qui renvoie la porte à partir de l'ID de porte.

entiergateId = gate("in")->getId();// ou: entiergateId =


findGate("dans");

Comme les identifiants de porte sont plus utiles avec les vecteurs de porte, nous les aborderons en détail dans une section ultérieure.

73
Manuel de simulation OMNeT++ – Modules simples

Vecteurs de porte

Les vecteurs de porte en possèdent uncGateobjet par élément. Pour accéder à des portes individuelles dans
le vecteur, vous devez appeler leportail()fonction avec un supplémentindiceparamètre. L'indice doit être
compris entre zéro etTaille-1. La taille du vecteur porte peut être lue avec lagateSize() méthode. L'exemple
suivant parcourt tous les éléments du vecteur de porte :

pour(entierje = 0 ; je < gateSize("out"); je++) {


cGate *gate = gate("out", je); //...

Un vecteur de porte ne peut pas avoir de "trous" ; C'est,portail()ne revient jamais nullptrou génère une
erreur si le vecteur de porte existe et que l'index est dans les limites.
Pour les portails d'entrée, gateSize() peut être appelé avec ou sans le "$je"/"$o" suffixe et renvoie le
même nombre.
leaPorte()peut être utilisée à la fois avec et sans index, et elles signifient deux choses différentes : sans
index, elle indique l'existence d'un vecteur de porte avec le nom donné, quelle que soit sa taille (elle
renvoie vrai pour un vecteur existant même si sa taille est actuellement nulle !); avec un index, il
examine également si l'index est dans les limites.

ID de porte

Une porte est également accessible par son ID. Une propriété très importante des identifiants de porte est qu'ils
sont contigu dans un vecteur de porte, c'est-à-dire l'ID d'une porte g[k] peut être calculé comme l'ID de g[0] plus k
. Cela vous permet d'accéder efficacement à n'importe quelle porte dans un vecteur de porte, car la récupération
d'une porte par ID est plus efficace que par nom et index. L'indice de la première porte peut être obtenu avec
gate("out",0)->getId(), mais il vaut mieux utiliser une méthode dédiée, gateBaseId(), car cela fonctionne également
lorsque la taille du vecteur de porte est nulle.
Deux autres propriétés importantes des identifiants de porte : ils sont stable et unique (dans le module). Par stable, nous
entendons que l'ID d'une porte ne change jamais ; et par unique, nous entendons non seulement qu'à un moment donné, deux
portes n'ont pas les mêmes identifiants, mais également que les identifiants des portes supprimées ne sont pas réutilisés
ultérieurement, de sorte que les identifiants de porte sont uniques dans la durée de vie d'une simulation.

REMARQUE: La version OMNeT++ antérieure à 4.0 n'avait pas ces garanties - le redimensionnement d'un vecteur
de porte pouvait entraîner le déplacement de sa plage d'ID, s'il avait chevauché la plage d'ID d'autres vecteurs de
porte. OMNeT++ 4.x résout le même problème en interprétant l'ID de porte comme un champ de bits, contenant
essentiellement des bits qui identifient le nom de la porte et d'autres bits qui contiennent l'index. Cela signifie
également que la limite supérieure théorique pour une taille de porte est maintenant plus petite, bien qu'elle soit
encore suffisamment grande pour qu'elle puisse être ignorée en toute sécurité à des fins pratiques.

L'exemple suivant parcourt un vecteur de porte à l'aide d'ID :

entierbaseId = gateBaseId("out"); entier


taille = gateSize("out"); pour(entierje = 0 ;
je < taille ; je++) {
cGate *gate = gate(baseId + i); //...

74
Manuel de simulation OMNeT++ – Modules simples

Énumération de toutes les portes

Si vous devez passer par toutes les portes d'un module, il y a deux possibilités. L'un invoque le
getGateNames() méthode qui renvoie les noms de toutes les portes et vecteurs de porte du module ;
alors tu peux appelerisGateVector(nom) pour déterminer si des noms individuels identifient une porte
scalaire ou un vecteur de porte ; alors les vecteurs de porte peuvent être énumérés par index. Aussi,
pour les portes d'entréegetGateNames() renvoie le nom de base sans le "$je"/"$o" suffixe, les deux
directions doivent donc être traitées séparément. legateType(nom) peut être utilisée pour tester si
une porte est entrée, entrée ou sortie (elle renvoie cGate::INOUT, cGate::INPUT, ou cGate::OUTPUT).

De toute évidence, la solution ci-dessus peut être assez difficile. Une alternative consiste à utiliser leGateIterator
classe assurée par cModule. Ça va comme ça:

pour(cModule::GateIterator i(cette); !i.end(); je++) {


cGate *gate = *i; . . .

Où cette désigne le module dont les portes sont énumérées (il peut être remplacé par n'importe quel
cModule * variable).

REMARQUE: Dans les versions antérieures d'OMNeT++, les ID de porte étaient de petits nombres entiers, il
était donc logique d'itérer sur toutes les portes d'un module en énumérant tous les ID de zéro à un
maximum, en sautant les trous (nullptrs). Ce n'est plus le cas avec OMNeT++ 4.0 et les versions ultérieures.
De plus, leportail()la méthode génère maintenant une erreur lorsqu'elle est appelée avec un ID invalide, et
ne se contente pas de renvoyer nullptr.

Ajouter et supprimer des portes

Bien que rarement nécessaire, il est possible d'ajouter et de supprimer des portes pendant la simulation. Vous pouvez ajouter
des portes scalaires et des vecteurs de porte, modifier la taille des vecteurs de porte et supprimer des portes scalaires et des
vecteurs de porte entiers. Il n'est pas possible de supprimer des portes aléatoires individuelles d'un vecteur de porte, de
supprimer la moitié d'une porte inout (par exemple "porte$o"),ou pour définir différentes tailles de vecteur de porte sur les deux
moitiés d'un vecteur de porte inout.

lecModuleles méthodes d'ajout et de suppression de portes sontaddGate(nom,type,isvector=false) et


deleteGate(nom).La taille du vecteur de porte peut être modifiée en utilisantsetGateSize(nom,taille). Aucune de
ces méthodes n'accepte "$je" / "$o"suffixe dans les noms de porte.

REMARQUE: Lorsque l'efficacité de la mémoire est préoccupante, il est utile de savoir que dans OMNeT++ 4.0 et
versions ultérieures, un vecteur de porte consommera beaucoup moins de mémoire que le même nombre de
portes scalaires individuelles.

Méthodes cGate

leobtenirNom() méthode de cGaterenvoie le nom de la porte ou du vecteur de porte sans l'index. Si vous avez
besoin d'une chaîne contenant également l'index de la porte,obtenirNomComplet() est ce que vous voulez. Si vous
souhaitez également inclure le nom hiérarchique du module propriétaire, appelezgetFullPath().
legetType() méthode de cGaterenvoie le type de porte, soit cGate::INPUT ou cGate::SORTIE. (Il ne peut
pas revenir cGate::INOUT, parce qu'une porte inout est représentée par une paire de cGates.)

75
Manuel de simulation OMNeT++ – Modules simples

Si vous avez une porte qui représente la moitié d'une porte inout (c'est-à-dire, obtenirNom() renvoie
quelque chose comme "g$i" ou "g$o"), vous pouvez diviser le nom avec le getBaseName() et
getNameSuffix() méthodes. getBaseName() la méthode renvoie le nom sans le $je/$o suffixe; et
getName-Suffix() renvoie uniquement le suffixe (y compris le signe dollar). Pour les portes normales,
getBaseName() est le même que obtenirNom(), et getNameSuffix() renvoie la chaîne vide.
leestVector(), getIndex(), getVectorSize() parler pour eux-mêmes; Taille() est un alias pour
getVectorSize(). Pour les portes non vectorielles, obtenirIndex() renvoie 0 et getVectorSize() renvoie 1.

legetId()renvoie l'ID de la porte (à ne pas confondre avec l'index de la porte). le


getOwnerModule() renvoie le module auquel appartient l'objet gate.
Pour illustrer ces méthodes, nous développons l'exemple d'itérateur de porte pour imprimer des informations sur
chaque porte :

pour(cModule::GateIterator i(cette); !i.end(); je++) {


cGate *gate = *i;
EV << gate->getFullName() << ": " ; EV << "id=" <<
gate->getId() << ", "; si(!gate->estVector())

EV << "porte scalaire, " ;


autre
EV << "porte" << porte->getIndex()
<< " dans le vecteur " << gate->getName()
<< " de taille " << gate->getVectorSize() << ", "; EV << "type :" <<
cGate::getTypeName(gate->getType()); EV << "\n" ;

Il y a plus loincGatedes méthodes pour accéder et manipuler la ou les connexion(s) attachée(s) à la


porte ; ils seront traités dans les sections suivantes.

4.6.2 Connexions

Les portes de module simples ont normalement une connexion attachée. Les portes des modules composés,
cependant, doivent être connectées à la fois à l'intérieur et à l'extérieur du module pour être utiles. Une série de
connexions (jointes avec des portes de modules composés) est appelée unchemin de connexionou simplement
chemin. Un chemin est dirigé et commence normalement à une porte de sortie d'un module simple, se termine à
une porte d'entrée d'un module simple et passe par plusieurs portes de module composé.
TouscGateL'objet contient des pointeurs vers la porte précédente et la porte suivante dans le chemin
(renvoyé par legetPreviousGate()et getNextGate()méthodes), de sorte qu'un chemin peut être considéré
comme une liste à double lien.
L'utilisation de laporte précédenteet prochaine portepointeurs avec différents types de portes est illustré sur la
figure 4.2.
Les portes de début et de fin du chemin peuvent être trouvées avec legetPathStartGate()et
getPathEndGate()méthodes, qui suivent simplement lesporte précédenteet prochaine portepointeurs,
respectivement, jusqu'à ce qu'ils soientnullptr.
leisConnectedOutside()et isConnectedInside()Les méthodes renvoient si une porte est connectée à
l'extérieur ou à l'intérieur. Ils examinent soit leprécédentou lasuivant pointeur, selon le type de porte
(entrée ou sortie). Par exemple, une porte de sortie estconnecté à l'extérieursi lasuivantle pointeur
n'est pasnullptr ; la même fonction pour une porte d'entrée vérifie la

76
Manuel de simulation OMNeT++ – Modules simples

"suivant" "préc" "suivant"

(une) (b)

"préc" "suivant" "préc"

(c) (ré)

Figure 4.2 : (a) porte de sortie du module simple, (b) porte de sortie du module composé, (c) porte d'entrée du
module simple, (d) porte d'entrée du module composé

précédentaiguille. Encore une fois, voir la figure 4.2 pour une illustration.

leest connecté()est un peu différente : elle renvoie true si la porte estpleinementconnecté, c'est-à-dire pour une
porte de module composé à la fois à l'intérieur et à l'extérieur, et pour une porte de module simple, à l'extérieur.

Le code suivant imprime le nom de la porte à laquelle une simple porte de module est connectée :

cGate *gate = gate("somegate");


cGate *otherGate = gate->getType()==cGate::OUTPUT ? gate->getNextGate() :
gate->getPreviousGate();
si(autre porte)
EV << "la porte est connectée à : " << otherGate->getFullPath() << endl; autre

EV << "porte non connectée" << endl;

4.6.3 Le canal de connexion

L'objet canal associé à une connexion est accessible par un pointeur stocké à la porte source de la
connexion. Le pointeur est renvoyé par legetChannel()méthode de la porte:
cChannel *channel = gate->getChannel();

Le résultat peut êtrenullptr,c'est-à-dire qu'une connexion peut ne pas avoir d'objet canal associé.
Si vous avez un pointeur de canal, vous pouvez récupérer sa porte source avec legetSourceGate()
méthode:
cGate *gate = canal->getSourceGate();

cCanal est juste une classe de base abstraite pour les canaux, donc pour accéder aux détails du canal, vous
devrez peut-être convertir le pointeur résultant dans une classe de canal spécifique, par exemplecDelay-
Canalou cDatarateChannel.
Un autre type de canal spécifique estcIdealChannel,qui ne fait fondamentalement rien : il agit comme
s'il n'y avait aucun objet de canal affecté à la connexion. OMNeT++ insère parfois de manière
transparente uncIdealChanneldans une connexion sans canal, par exemple pour contenir la chaîne
d'affichage associée à la connexion.
Souvent, vous n'êtes pas vraiment intéressé par le canal d'une connexion spécifique, mais plutôt par le canal de
transmission (voir 4.7.6) du chemin de connexion qui commence à une porte de sortie spécifique. le

77
Manuel de simulation OMNeT++ – Modules simples

canal de transmission peut être trouvé en suivant le chemin de connexion jusqu'à ce que vous
trouviez un canal dont isTransmissionChannel() la méthode renvoie vrai,mais cGatea une méthode
pratique pour cela, nommée getTransmissionChannel(). Un exemple d'utilisation :
cChannel *txChan = gate("ppp$o")->getTransmissionChannel();

Une méthode complémentaire pour getTransmissionChannel() estgetIncomingTransmissionChannel(); il est


généralement invoqué sur les portes d'entrée et recherche le chemin de connexion dans le sens inverse.

cChannel *incomingTxChan = gate("ppp$i")->getIncomingTransmissionChannel();

Les deux méthodes génèrent une erreur si aucun canal de transmission n'est trouvé. Si cela ne
convient pas, utilisez le mêmefindTransmissionChannel()et findIncomingTransmissionChannel()
méthodes qui retournent simplementnullptrdans ce cas.
Les canaux sont traités plus en détail dans la section 4.8.

4.7 Envoi et réception de messages

À un niveau abstrait, un modèle de simulation OMNeT++ est un ensemble de modules simples qui
communiquent entre eux via la transmission de messages. L'essence des modules simples est qu'ils créent,
envoient, reçoivent, stockent, modifient, planifient et détruisent des messages - le reste d'OMNeT++ existe
pour faciliter cette tâche et collecter des statistiques sur ce qui se passait.
Les messages dans OMNeT++ sont des instances decMessage classe ou l'une de ses sous-classes. Les
paquets réseau sont représentés parcPaquet,qui est également sous-classé decMessage.Les objets de
message sont créés à l'aide de C++Nouveauopérateur, et détruit à l'aide dueffacer l'opérateur lorsqu'ils ne
sont plus nécessaires.
Les messages sont décrits en détail au chapitre 5. À ce stade, tout ce que nous devons savoir à leur
sujet est qu'ils sont appeléscMessage *pointeurs. Dans les exemples ci-dessous, les messages seront
créés avecnouveau cMessage("foo")où "fou"est un nom de message descriptif, utilisé à des fins de
visualisation et de débogage.

4.7.1 Messages personnels

Presque tous les modèles de simulation doivent planifier des événements futurs afin d'implémenter des temporisateurs, des
délais d'attente, des retards, etc. Quelques exemples typiques :

• Un module source qui crée et envoie périodiquement des messages doit programmer le prochain
envoi après chaque opération d'envoi ;

• Un serveur qui traite des travaux à partir d'une file d'attente doit démarrer un temporisateur chaque fois qu'il commence
à traiter un travail. Lorsque la minuterie expire, le travail terminé peut être envoyé et un nouveau travail peut commencer
à être traité ;

• Lorsqu'un paquet est envoyé par un protocole de communication qui utilise la retransmission, il doit
planifier un délai d'attente afin que le paquet puisse être retransmis si aucun accusé de réception
n'arrive dans un certain laps de temps.

Dans OMNeT++, vous résolvez de telles tâches en laissant le module simple s'envoyer un message à lui-
même ; le message serait délivré au module simple ultérieurement. Les messages utilisés de cette façon
sont appelésauto-messages, et la classe module a pour eux des méthodes spéciales qui permettent
d'implémenter des messages automatiques sans portes ni connexions.

78

Vous aimerez peut-être aussi