Il s'agit de la commande ns-3-manual qui peut être exécutée dans le fournisseur d'hébergement gratuit OnWorks en utilisant l'un de nos multiples postes de travail en ligne gratuits tels que Ubuntu Online, Fedora Online, l'émulateur en ligne Windows ou l'émulateur en ligne MAC OS
PROGRAMME:
Nom
ns-3-manuel - ns-3 manuel
Il s'agit de la ns-3 Manuel. La documentation principale du projet ns-3 est disponible en cinq
formes:
· ns-3 doxygen: Documentation des API publiques du simulateur
· Tutoriel, Manuel (ce document), et la bibliothèque de modèles pour le derniers libérer et
développant arbre
· ns-3 wiki
CONTENU
Nom de l'entreprise
Ce chapitre décrit l'ensemble ns-3 l'organisation du logiciel et le correspondant
organisation de ce manuel.
ns-3 est un simulateur de réseau à événements discrets dans lequel le noyau de simulation et les modèles sont
implémenté en C++. ns-3 est construit comme une bibliothèque qui peut être statique ou dynamique
lié à un programme principal C++ qui définit la topologie de simulation et lance le
simulateur. ns-3 exporte également la quasi-totalité de son API vers Python, permettant aux programmes Python de
importer un module "ns3" de la même manière que le ns-3 la bibliothèque est liée par des exécutables
en C ++.
[image] Organisation logicielle de ns-3.UNINDENT
Le code source de ns-3 est principalement organisé dans le src répertoire et peut être décrit
par le diagramme dans Software organisation of ns-3. Nous allons travailler notre chemin à partir du bas
en haut; en général, les modules n'ont de dépendances que sur les modules en dessous d'eux dans la figure.
Nous décrivons d'abord le cœur du simulateur ; ces composants qui sont communs à tous
modèles de protocole, de matériel et d'environnement. Le noyau de simulation est implémenté dans
src/noyau. Les paquets sont des objets fondamentaux dans un simulateur de réseau et sont implémentés dans
src/réseau. Ces deux modules de simulation sont à eux seuls destinés à comprendre un
noyau de simulation générique qui peut être utilisé par différents types de réseaux, pas seulement
Réseaux basés sur Internet. Les modules ci-dessus de ns-3 sont indépendants d'un réseau spécifique
et les modèles d'appareils, qui sont traités dans les parties suivantes de ce manuel.
En plus de ce qui précède ns-3 noyau, nous introduisons, également dans la partie initiale du
manual, deux autres modules qui complètent l'API de base basée sur C++. ns-3 les programmes peuvent
accéder directement à l'ensemble de l'API ou utiliser un assistant API qui procure
wrappers pratiques ou encapsulation d'appels d'API de bas niveau. Le fait que ns-3 tout proche.
peut être écrit sur deux API (ou une combinaison de celles-ci) est un aspect fondamental de la
simulateur. Nous décrivons également comment Python est pris en charge dans ns-3 avant de passer à certains
modèles pertinents pour la simulation de réseau.
Le reste du manuel se concentre sur la documentation des modèles et la prise en charge
capacités. La partie suivante se concentre sur deux objets fondamentaux dans ns-3: la Nœud et
NetDevice. Deux types de NetDevice spéciaux sont conçus pour prendre en charge l'utilisation de l'émulation réseau
cas, et l'émulation est décrite ci-après. Le chapitre suivant est consacré à
Modèles liés à Internet, y compris l'API de sockets utilisée par les applications Internet. le
le chapitre suivant traite des applications, et le chapitre suivant décrit une assistance supplémentaire
pour la simulation, comme les animateurs et les statistiques.
Le projet maintient un manuel séparé consacré aux tests et à la validation des ns-3 code
(voir le ns-3 Contrôle de qualité et Validation Manuel).
aléatoire Variables
ns-3 contient un générateur de nombres pseudo-aléatoires intégré (PRNG). Il est important pour
utilisateurs sérieux du simulateur pour comprendre la fonctionnalité, la configuration et l'utilisation
de ce PRNG, et de décider s'il est suffisant pour son utilisation de recherche.
Rapide Aperçu
ns-3 les nombres aléatoires sont fournis via des instances de ns3 ::RandomVariableStream.
· par défaut, ns-3 les simulations utilisent une graine fixe ; s'il y a du hasard dans le
simulation, chaque exécution du programme donnera des résultats identiques à moins que la semence et/ou
le numéro d'exécution est modifié.
· dans ns-3.3 et plus tôt, ns-3 les simulations utilisaient une graine aléatoire par défaut ; cela marque un
changement de politique en commençant par ns-3.4.
· dans ns-3.14 et plus tôt, ns-3 les simulations utilisaient une classe wrapper différente appelée
ns3 :: Variable aléatoire. Au ns-3.15, cette classe a été remplacée par
ns3 ::RandomVariableStream; le générateur de nombres pseudo-aléatoires sous-jacent n'a pas
changé.
· pour obtenir un caractère aléatoire sur plusieurs exécutions de simulation, vous devez soit définir la valeur de départ
différemment ou définissez le numéro de série différemment. Pour définir une graine, appelez
ns3 :: RngSeedManager :: SetSeed () au début du programme; pour définir un numéro d'exécution avec
la même graine, appelle ns3 :: RngSeedManager :: SetRun () au début du programme; voir
La création aléatoire les variables.
· chaque RandomVariableStream utilisé dans ns-3 a un générateur de nombres aléatoires virtuel associé
avec ça; toutes les variables aléatoires utilisent une graine fixe ou aléatoire basée sur l'utilisation de la
semences mondiales (puce précédente) ;
· si vous avez l'intention d'effectuer plusieurs exécutions du même scénario, avec différents
numéros, assurez-vous de lire la section sur la façon d'effectuer des réplications indépendantes :
La création aléatoire les variables.
Lisez la suite pour plus d'explications sur la fonction de nombre aléatoire pour ns-3.
Biographie
Les simulations utilisent beaucoup de nombres aléatoires ; une étude a révélé que la plupart des simulations de réseau
dépenser jusqu'à 50 % du processeur pour générer des nombres aléatoires. Les utilisateurs de simulation doivent être
soucieux de la qualité des nombres (pseudo) aléatoires et de l'indépendance entre
différents flux de nombres aléatoires.
Les utilisateurs doivent se préoccuper de quelques problèmes, tels que :
· l'ensemencement du générateur de nombres aléatoires et si un résultat de simulation est
déterministe ou non,
· comment acquérir différents flux de nombres aléatoires indépendants les uns des autres
un autre, et
· combien de temps faut-il aux flux pour cycler
Nous allons introduire ici quelques termes : un RNG fournit une longue séquence de (pseudo) aléatoires
Nombres. La longueur de cette séquence est appelée la cycle longueur or période, après quoi
le RNG se répétera. Cette séquence peut être partitionnée en disjoint flux. A
flux d'un RNG est un sous-ensemble ou un bloc contigu de la séquence RNG. Par exemple, si le
La période RNG est de longueur N, et deux flux sont fournis à partir de ce RNG, puis le premier
le flux peut utiliser les premières valeurs N/2 et le deuxième flux peut produire le deuxième N/2
valeurs. Une propriété importante ici est que les deux flux ne sont pas corrélés. Également,
chaque flux peut être partitionné de manière disjointe en un certain nombre de sous-fluxL’
Le RNG sous-jacent produit, espérons-le, une séquence pseudo-aléatoire de nombres avec un très long
durée du cycle, et le divise en flux et sous-flux de manière efficace.
ns-3 utilise le même générateur de nombres aléatoires sous-jacent que ns-2: le MRG32k3a
générateur de Pierre L'Ecuyer. Vous trouverez une description détaillée dans
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. Le générateur MRG32k3a
fournit 1.8x10^{19} flux indépendants de nombres aléatoires, chacun composé de
2.3x10^{15} sous-flux. Chaque sous-flux a une période (à, le nombre de nombres aléatoires
avant chevauchement) de 7.6x10^{22}. La période de l'ensemble du générateur est de 3.1x10^{57}.
Classe ns3 ::RandomVariableStream est l'interface publique de ce nombre aléatoire sous-jacent
Générateur. Lorsque les utilisateurs créent de nouvelles variables aléatoires (telles que ns3::UniformeRandomVariable,
ns3::ExponentielleRandomVariable, etc.), ils créent un objet qui utilise l'un des
flux distincts et indépendants du générateur de nombres aléatoires. Par conséquent, chaque objet de
type ns3 ::RandomVariableStream a, conceptuellement, son propre RNG « virtuel ». Par ailleurs,
chacun ns3 ::RandomVariableStream peut être configuré pour utiliser l'un des ensembles de sous-flux tirés
du courant principal.
Une autre implémentation serait de permettre à chaque RandomVariable d'avoir son propre
(différemment ensemencé) RNG. Cependant, nous ne pouvons garantir aussi fortement que les différents
les séquences seraient décorrélées dans un tel cas ; par conséquent, nous préférons utiliser un seul RNG et
flux et sous-flux de celui-ci.
La création aléatoire les variables
ns-3 prend en charge un certain nombre d'objets variables aléatoires de la classe de base
RandomVariableStream. Ces objets proviennent de ns3::Objet et sont gérés par smart
pointeurs.
La bonne façon de créer ces objets est d'utiliser le modèle CréerObjet<> Procédé,
telles que:
Ptr x = CréerObjet ();
alors vous pouvez accéder aux valeurs en appelant des méthodes sur l'objet telles que :
myRandomNo = x->GetInteger ();
Si vous essayez plutôt de faire quelque chose comme ceci :
monRandomNo = UniformRandomVariable().GetInteger ();
votre programme rencontrera un défaut de segmentation, car l'implémentation repose sur
une construction d'attributs qui se produit uniquement lorsque CreateObject est appelé.
Une grande partie du reste de ce chapitre traite maintenant des propriétés du flux de
nombres pseudo-aléatoires générés à partir de tels objets, et comment contrôler l'ensemencement de tels
objets.
Ensemencement et indépendant réplications
ns-3 les simulations peuvent être configurées pour produire des résultats déterministes ou aléatoires. Si la
ns-3 la simulation est configurée pour utiliser une graine fixe et déterministe avec le même numéro d'exécution,
il devrait donner la même sortie à chaque fois qu'il est exécuté.
Par défaut, ns-3 les simulations utilisent un numéro de départ et d'exécution fixe. Ces valeurs sont stockées dans
deux ns3 :: Valeur globale les instances: g_rngSeed et g_rngExécuter.
Un cas d'utilisation typique consiste à exécuter une simulation sous la forme d'une séquence d'essais indépendants, de manière à
calculer des statistiques sur un grand nombre d'exécutions indépendantes. L'utilisateur peut soit modifier le
amorçage global et réexécute la simulation, ou peut faire avancer l'état du sous-flux du RNG, ce qui
est appelé incrémentation du numéro d'exécution.
Une classe ns3 :: RngSeedManager fournit une API pour contrôler l'ensemencement et le nombre d'exécutions
comportement. Ce paramètre d'état d'amorçage et de sous-flux doit être appelé avant tout
des variables sont créées ; par exemple:
RngSeedManager::SetSeed (3) ; // Change la valeur par défaut de 1 à 3
RngSeedManager::SetRun (7) ; // Change le numéro d'exécution de 1 par défaut à 7
// Maintenant, créez des variables aléatoires
Ptr x = CréerObjet ();
Ptr y = CréerObjet ();
Quel est le meilleur, définir une nouvelle graine ou faire avancer l'état du sous-flux ? Il n'y a pas
garantir que les flux produits par deux graines aléatoires ne se chevaucheront pas. La seule façon de
garantir que deux flux ne se chevauchent pas consiste à utiliser la capacité de sous-flux fournie par
la mise en œuvre du RNG. Par conséquent, utilisé le sous-flux aptitude à produire plusieurs
indépendant fonctionne of le même simulation. En d'autres termes, le plus statistiquement rigoureux
moyen de configurer plusieurs réplications indépendantes est d'utiliser une graine fixe et d'avancer
le numéro de course. Cette implémentation permet un maximum de 2.3x10^{15} indépendant
réplications utilisant les sous-flux.
Pour faciliter l'utilisation, il n'est pas nécessaire de contrôler la semence et le numéro de série à partir du
programme; l'utilisateur peut définir le NS_GLOBAL_VALUE variable d'environnement comme suit :
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run nom-programme
Une autre façon de contrôler cela est de passer un argument de ligne de commande ; puisqu'il s'agit d'un ns-3
Instance GlobalValue, cela se fait de manière équivalente comme suit :
$ ./waf --command-template="%s --RngRun=3" --run program-name
ou, si vous exécutez des programmes directement en dehors de waf :
$ ./build/optimized/scratch/nom-programme --RngRun=3
Les variantes de ligne de commande ci-dessus facilitent l'exécution de nombreuses exécutions différentes à partir d'un shell
script en passant simplement un index RngRun différent.
Classe RandomVariableStream
Toutes les variables aléatoires doivent dériver de la classe Variable aléatoire. Cette classe de base fournit un
quelques méthodes pour configurer globalement le comportement du générateur de nombres aléatoires. Dérivé
les classes fournissent une API pour dessiner des variables aléatoires à partir de la distribution particulière étant
prise en charge.
Chaque RandomVariableStream créé dans la simulation reçoit un générateur qui est un nouveau
RNGStream du PRNG sous-jacent. Utilisée de cette manière, l'implémentation L'Ecuyer
autorise un maximum de 1.8x10^19 variables aléatoires. Chaque variable aléatoire dans un seul
la réplication peut produire jusqu'à 7.6x10^22 nombres aléatoires avant de se chevaucher.
Base classe public API
Ci-dessous sont extraites quelques méthodes publiques de classe RandomVariableStream qui accèdent au
valeur suivante dans le sous-flux.
/ **
* \brief Renvoie un double aléatoire de la distribution sous-jacente
* \return Une valeur aléatoire à virgule flottante
*/
double GetValue (void) const ;
/ **
* \brief Renvoie un entier aléatoire de la distribution sous-jacente
* \return Integer cast de ::GetValue()
*/
uint32_t GetInteger (void) const ;
Nous avons déjà décrit la configuration d'ensemencement ci-dessus. Variable aléatoire différente
les sous-classes peuvent avoir une API supplémentaire.
Types of Variables aléatoires
Les types de variables aléatoires suivants sont fournis et sont documentés dans le ns-3
Doxygen ou en lisant src/core/model/random-variable-stream.h. Les utilisateurs peuvent également créer
leurs propres variables aléatoires personnalisées en dérivant de la classe RandomVariableStream.
· classer UniformeAléatoireVariable
· classer ConstanteRandomVariable
· classer SéquentielleRandomVariable
· classer ExponentielRandomVariable
· classer Variable aléatoire de Pareto
· classer WeibullVariableAléatoire
· classer NormalAléatoireVariable
· classer Lognormalrandomvariable
· classer GammaAléatoireVariable
· classer ErlangAléatoireVariable
· classer TriangulaireRandomVariable
· classer ZipfRandomVariable
· classer ZêtaRandomVariable
· classer DéterministeRandomVariable
· classer EmpiriqueAléatoireVariable
Sémantique of RandomVariableStream objets
Les objets RandomVariableStream dérivent de ns3::Objet et sont gérés par des pointeurs intelligents.
Les instances RandomVariableStream peuvent également être utilisées dans ns-3 attributs, ce qui signifie que
des valeurs peuvent être définies pour eux via le ns-3 système d'attributs. Un exemple est dans le
modèles de propagation pour WifiNetDevice :
ID de type
RandomPropagationDelayModel :: GetTypeId (vide)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent ()
.AddConstructor ()
.AddAttribute ("Variable",
"La variable aléatoire qui génère des retards aléatoires (s).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker ())
;
retour tid;
}
Ici le ns-3 l'utilisateur peut modifier la variable aléatoire par défaut pour ce modèle de délai (qui est
une UniformRandomVariable allant de 0 à 1) via le système d'attributs.
En utilisant autre PRNG
Il n'y a actuellement aucun support pour substituer un nombre aléatoire sous-jacent différent
générateur (par exemple, la bibliothèque scientifique GNU ou le paquet Akaroa). Les patchs sont les bienvenus.
Paramètres le courant nombre
Le générateur MRG32k3a sous-jacent fournit 2^64 flux indépendants. Dans ns-3, ce sont
attribué séquentiellement à partir du premier flux en tant que nouvelles instances RandomVariableStream
font leur premier appel à GetValue().
En raison de la façon dont ces objets RandomVariableStream sont affectés aux flux sous-jacents,
l'affectation est sensible aux perturbations de la configuration de simulation. le
conséquence est que si un aspect de la configuration de simulation est modifié, le mappage
de RandomVariables aux flux peuvent (ou ne peuvent pas) changer.
A titre d'exemple concret, un utilisateur réalisant une étude comparative entre protocoles de routage peut
constater que le fait de changer un protocole de routage pour un autre remarquera que le
le modèle de mobilité sous-jacent a également changé.
À partir de ns-3.15, un certain contrôle a été fourni aux utilisateurs pour leur permettre de
éventuellement corriger l'affectation des objets RandomVariableStream sélectionnés aux sous-jacents
ruisseaux. C'est le Discussions attribut, faisant partie de la classe de base RandomVariableStream.
En partitionnant la séquence de flux existante d'avant :
<------------------------------------------------- ------------------------->
flux 0 flux (2^64 - 1)
en deux ensembles de taille égale :
<------------------------------------------------- ------------------------->
^ ^^ ^
| || |
flux 0 flux (2^63 - 1) flux 2^63 flux (2^64 - 1)
<- attribué automatiquement -----------><- attribué par l'utilisateur ------------------>
Les 2^63 premiers flux continuent d'être attribués automatiquement, tandis que les 2^63 derniers sont
indices de flux donnés commençant par zéro jusqu'à 2^63-1.
L'attribution de flux à un numéro de flux fixe est facultative ; cas de
RandomVariableStream qui n'a pas de valeur de flux attribuée sera attribué le prochain
un du pool de flux automatiques.
Pour fixer un RandomVariableStream à un flux sous-jacent particulier, attribuez son Discussions
attribuer à un entier non négatif (la valeur par défaut de -1 signifie qu'une valeur sera
attribué automatiquement).
Édition votre résultats
Lorsque vous publiez des résultats de simulation, une information de configuration clé que vous
devrait toujours indiquer comment vous avez utilisé le générateur de nombres aléatoires.
· quelles graines vous avez utilisées,
· quel RNG vous avez utilisé sinon celui par défaut,
· comment les courses indépendantes ont-elles été effectuées,
· pour les grosses simulations, comment as-tu vérifié que tu n'avais pas fait de vélo.
Il incombe au chercheur qui publie les résultats d'inclure suffisamment d'informations pour
permettre aux autres de reproduire ses résultats. Il incombe également au chercheur de
se convaincre que les nombres aléatoires utilisés étaient statistiquement valables, et d'indiquer en
le papier pourquoi une telle confiance est supposée.
Résumé
Voyons ce que vous devez faire lors de la création d'une simulation.
· Décidez si vous courez avec une graine fixe ou aléatoire ; une graine fixe est la
défaut
· Décidez comment vous allez gérer les réplications indépendantes, le cas échéant,
· Convainquez-vous que vous ne tirez pas plus de valeurs aléatoires que la durée du cycle, si
vous exécutez une très longue simulation, et
· Lorsque vous publiez, suivez les directives ci-dessus pour documenter votre utilisation de l'aléatoire
générateur de nombres.
Hash Les fonctions
ns-3 fournit une interface générique aux fonctions de hachage à usage général. Dans le plus simple
utilisation, la fonction de hachage renvoie le hachage 32 bits ou 64 bits d'un tampon de données ou d'une chaîne.
La fonction de hachage sous-jacente par défaut est murmure3, choisi car il a une bonne fonction de hachage
properties et propose une version 64 bits. le vénérable FNV1a le hachage est également disponible.
Il existe un mécanisme simple pour ajouter (ou fournir au moment de l'exécution) un hachage alternatif
implémentations de fonctions.
Basic Utilisation
Le moyen le plus simple d'obtenir une valeur de hachage d'un tampon de données ou d'une chaîne est simplement :
#include "ns3/hash.h"
en utilisant l'espace de noms ns3 ;
char * tampon = ...
size_t taille_tampon = ...
uint32_t buffer_hash = Hash32 (tampon, buffer_size);
std ::chaîne s;
uint32_t string_hash = Hash32 (s);
Des fonctions équivalentes sont définies pour les valeurs de hachage 64 bits.
Incrémental Hachage
Dans certaines situations, il est utile de calculer le hachage de plusieurs tampons, comme s'ils avaient
été réunis. (Par exemple, vous pouvez vouloir le hachage d'un flux de paquets, mais pas
voulez assembler un seul tampon avec le contenu combiné de tous les paquets.)
C'est presque aussi simple que le premier exemple :
#include "ns3/hash.h"
en utilisant l'espace de noms ns3 ;
char * tampon ;
size_t buffer_size ;
hasher hasher ; // Utiliser la fonction de hachage par défaut
pour ( )
{
tampon = get_next_buffer ();
hacheur (tampon, taille_tampon);
}
uint32_t combine_hash = hasher.GetHash32 ();
Par défaut Hacheur préserve l'état interne pour permettre le hachage incrémentiel. Si tu veux
réutiliser un Hacheur objet (par exemple parce qu'il est configuré avec un hachage différent de celui par défaut
fonction), mais ne voulez pas ajouter au hachage précédemment calculé, vous devez clair()
premier:
hasher.clear ().GetHash32 (tampon, taille_tampon);
Cela réinitialise l'état interne avant de hacher le tampon.
En utilisant an Alternative Hash Fonction
La fonction de hachage par défaut est murmure3. FNV1a est également disponible. Pour spécifier le hachage
fonction explicitement, utilisez ce constructeur :
Hasher hasher = Hasher ( Créer () );
L'ajout de Équipement Hash Fonction Implémentations
Pour ajouter la fonction de hachage foo, suivre la hachage-murmur3.h/. Cc modèle:
· Créer une déclaration de classe (.h) et définition (. Cc) héritant de
Hash::Implémentation.
· de Swanson comprennent la déclaration en hachage.h (au point où hachage-murmur3.h est inclus.
· Dans votre propre code, instanciez un Hacheur objet via le constructeur Hacheur
(Ptr ())
Si votre fonction de hachage est une fonction unique, par exemple hachage, vous n'avez même pas besoin de créer un
nouvelle classe dérivée de HashImplementation :
hacher hacher =
Hachage (Créer (&hashf) );
Pour que cela compile, votre hachage doit correspondre à l'une des signatures de pointeur de fonction :
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
Sources pour Hash Les fonctions
Les sources d'autres implémentations de fonction de hachage incluent :
· Peter Kankowski : http://www.strchr.com
· Arash Partow : http://www.partow.net/programming/hashfunctions/index.html
· SMHasher : http://code.google.com/p/smhasher/
· Sanmayce : http://www.sanmayce.com/Fastest_Hash/index.html
MICE et Simulateur
ns-3 est un simulateur de réseau à événements discrets. Conceptuellement, le simulateur garde une trace d'un
nombre d'événements planifiés pour s'exécuter à une heure de simulation spécifiée. Le travail de
le simulateur doit exécuter les événements dans un ordre chronologique séquentiel. Une fois l'achèvement de
un événement se produit, le simulateur passera à l'événement suivant (ou quittera s'il n'y a pas
plus d'événements dans la file d'attente des événements). Si, par exemple, un événement programmé pour le temps de simulation
"100 secondes" est exécuté, et le prochain événement n'est pas programmé avant "200 secondes", le
le simulateur passera immédiatement de 100 secondes à 200 secondes (du temps de simulation) à
exécuter l'événement suivant. C'est ce que l'on entend par simulateur "à événements discrets".
Pour que tout cela se produise, le simulateur a besoin de quelques éléments :
1. un objet simulateur qui peut accéder à une file d'attente d'événements où les événements sont stockés et qui peut
gérer l'exécution des événements
2. un planificateur chargé d'insérer et de supprimer des événements de la file d'attente
3. une façon de représenter le temps de simulation
4. les événements eux-mêmes
Ce chapitre du manuel décrit ces objets fondamentaux (simulateur, ordonnanceur,
heure, événement) et comment ils sont utilisés.
Événement
À be terminé
Simulateur
La classe Simulator est le point d'entrée public pour accéder aux fonctionnalités de planification d'événements. Une fois
quelques événements ont été programmés pour démarrer la simulation, l'utilisateur peut commencer à
les exécuter en entrant dans la boucle principale du simulateur (appel Simulateur :: Exécuter). Une fois la boucle principale
commence à s'exécuter, il exécutera séquentiellement tous les événements programmés dans l'ordre du plus ancien au
le plus récent jusqu'à ce qu'il n'y ait plus d'événements dans la file d'attente d'événements ou
Simulator::Stop a été appelé.
Pour planifier des événements à exécuter par la boucle principale du simulateur, la classe Simulator fournit
la famille de fonctions Simulator::Schedule*.
1. Gestion des gestionnaires d'événements avec différentes signatures
Ces fonctions sont déclarées et implémentées en tant que modèles C++ pour gérer automatiquement le
grande variété de signatures de gestionnaires d'événements C++ utilisées à l'état sauvage. Par exemple, pour planifier une
événement pour exécuter 10 secondes dans le futur et invoquer une méthode ou une fonction C++ avec
arguments spécifiques, vous pouvez écrire ceci :
gestionnaire void (int arg0, int arg1)
{
std::cout << "gestionnaire appelé avec l'argument arg0=" << arg0 << " et
arg1=" << arg1 << std::endl;
}
Simulateur::Calendrier(Sec(10), &handler, 10, 5);
Ce qui affichera :
gestionnaire appelé avec l'argument arg0=10 et arg1=5
Bien sûr, ces modèles C++ peuvent également gérer de manière transparente les méthodes membres sur C++
objets:
À be complété: membre méthode (ici)
Notes:
· les méthodes ns-3 Schedule reconnaissent automatiquement les fonctions et méthodes uniquement si elles
prendre moins de 5 arguments. Si vous en avez besoin pour soutenir plus d'arguments, veuillez déposer un
rapport d'erreur.
· Les lecteurs familiers avec le terme « foncteurs pleinement liés » reconnaîtront le
Simulator::Schedule méthodes comme moyen de construire automatiquement de tels objets.
2. Opérations d'ordonnancement communes
L'API Simulator a été conçue pour simplifier la planification de la plupart des événements. Ce
propose trois variantes pour le faire (classées du plus couramment utilisé au moins couramment utilisé) :
· Méthodes de planification qui vous permettent de planifier un événement à l'avenir en fournissant les
délai entre l'heure de simulation actuelle et la date d'expiration de l'événement cible.
· Méthodes ScheduleNow qui vous permettent de programmer un événement pour la simulation en cours
heure : ils s'exécuteront _après_ l'exécution de l'événement en cours, mais _avant_ le
l'heure de la simulation est modifiée pour l'événement suivant.
· Méthodes ScheduleDestroy qui vous permettent de vous connecter au processus d'arrêt du simulateur
pour nettoyer les ressources de simulation : chaque événement 'destroy' est exécuté lorsque l'utilisateur appelle
la méthode Simulator::Destroy.
3. Maintien du contexte de simulation
Il existe deux manières de programmer des événements, avec et sans aux contextes. Qu'est-ce que cela
signifier?
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
vs.
Simulator::ScheduleWithContext (contexte uint32_t, Time const &time, MEM mem_ptr, OBJ obj);
Les lecteurs qui investissent du temps et des efforts dans le développement ou l'utilisation d'un modèle de simulation non trivial
connaîtra la valeur du framework de journalisation ns-3 pour déboguer des simulations simples et complexes
ressemblent. L'une des fonctionnalités importantes fournies par ce cadre de journalisation est la
affichage automatique de l'identifiant du nœud de réseau associé à l'événement en cours d'exécution.
L'identifiant de nœud du nœud de réseau en cours d'exécution est en fait suivi par le simulateur
classer. Il est accessible avec la méthode Simulator::GetContext qui renvoie le
'context' (un entier de 32 bits) associé et stocké dans l'événement en cours d'exécution. Dans
quelques rares cas, lorsqu'un événement n'est pas associé à un nœud de réseau spécifique, son
'context' est défini sur 0xffffffff.
Pour associer un contexte à chaque événement, les méthodes Schedule et ScheduleNow automatiquement
réutiliser le contexte de l'événement en cours d'exécution comme contexte de l'événement programmé
pour exécution plus tard.
Dans certains cas, notamment lors de la simulation de la transmission d'un paquet d'un nœud à
autre, ce comportement est indésirable puisque le contexte attendu de l'événement de réception est
celui du nœud récepteur, et non celui du nœud expéditeur. Pour éviter ce problème, le simulateur
classe fournit une méthode de planification spécifique : ScheduleWithContext qui permet de fournir
explicitement l'identifiant du nœud de réception associé à l'événement de réception.
XXX : code (ici)
Dans de très rares cas, les développeurs peuvent avoir besoin de modifier ou de comprendre comment le contexte
(node id) du premier événement est défini sur celui de son nœud associé. Ceci est accompli
par la classe NodeList : chaque fois qu'un nouveau nœud est créé, la classe NodeList utilise
ScheduleWithContext pour planifier un événement « initialiser » pour ce nœud. L'événement « initialiser »
s'exécute donc avec un contexte défini sur celui de l'ID de nœud et peut utiliser la variété normale de
Méthodes de planification. Il invoque la méthode Node::Initialize qui propage le 'initialiser'
événement en appelant la méthode DoInitialize pour chaque objet associé au nœud. le
La méthode DoInitialize est remplacée dans certains de ces objets (notamment dans l'Application
classe de base) programmera certains événements (notamment Application::StartApplication) qui
programmera à son tour des événements de génération de trafic qui programmeront à leur tour
événements au niveau du réseau.
Notes:
· Les utilisateurs doivent faire attention à propager les méthodes DoInitialize à travers les objets en appelant
Initialiser explicitement sur leurs objets membres
· L'identifiant de contexte associé à chaque méthode ScheduleWithContext a d'autres utilisations au-delà
logging : il est utilisé par une branche expérimentale de ns-3 pour effectuer une simulation parallèle sur
systèmes multicœurs utilisant le multithreading.
Les fonctions Simulator::* ne connaissent pas le contexte : elles s'assurent simplement que
quel que soit le contexte que vous spécifiez avec ScheduleWithContext est disponible lorsque le correspondant
l'événement s'exécute avec ::GetContext.
Il appartient aux modèles implémentés au-dessus de Simulator ::* d'interpréter la valeur de contexte.
Dans ns-3, les modèles de réseau interprètent le contexte comme l'identifiant du nœud qui
généré un événement. C'est pourquoi il est important d'appeler ScheduleWithContext dans
ns3::Channel sous-classes car nous générons un événement du nœud i au nœud j et nous
veulent s'assurer que l'événement qui s'exécutera sur le nœud j a le bon contexte.
Heure
À be terminé
Planificateur
À be terminé
Rappels
Quelques nouveaux utilisateurs à ns-3 ne sont pas familiers avec un idiome de programmation largement utilisé utilisé
dans tout le code : le ns-3 rappeler. Ce chapitre fournit une certaine motivation sur la
rappel, des conseils sur la façon de l'utiliser et des détails sur sa mise en œuvre.
Rappels motivation
Considérez que vous disposez de deux modèles de simulation A et B, et que vous souhaitez les faire passer
informations entre eux au cours de la simulation. Une façon de le faire est que vous
peut rendre A et B chacun explicitement informé de l'autre, afin qu'ils puissent invoquer
méthodes les unes sur les autres :
classe A {
publique:
void ReceiveInput ( // paramètres );
}
(dans un autre fichier source :)
classe B {
publique:
void DoSomething (vide);
privé:
A* une_instance ; // pointeur vers un A
}
annuler
B::Faire Quelque Chose()
{
// Indique à une_instance qu'il s'est passé quelque chose
a_instance->ReceiveInput ( // paramètres);
}
Cela fonctionne certes, mais cela présente l'inconvénient d'introduire une dépendance sur A et B
connaître l'autre au moment de la compilation (cela rend plus difficile d'avoir des
unités de compilation dans le simulateur) et n'est pas généralisé ; si dans un scénario d'utilisation ultérieur,
B doit parler à un objet C complètement différent, le code source de B doit être
modifié pour ajouter un c_instance et ainsi de suite. Il est facile de voir que c'est une force brute
mécanisme de communication qui peut conduire à des erreurs de programmation dans les modèles.
Cela ne veut pas dire que les objets ne doivent pas se connaître s'il y a un
dépendance entre eux, mais que souvent le modèle peut être rendu plus flexible si son
les interactions sont moins contraintes au moment de la compilation.
Il ne s'agit pas d'un problème abstrait pour la recherche en simulation de réseau, mais il s'agit plutôt d'un
source de problèmes dans les simulateurs précédents, lorsque les chercheurs souhaitent étendre ou modifier le
système pour faire des choses différentes (comme ils sont susceptibles de le faire dans la recherche). Considérez, par exemple,
un utilisateur qui souhaite ajouter une sous-couche de protocole de sécurité IPsec entre TCP et IP :
------------ -----------
| TCP | | TCP |
------------ -----------
| devient -> |
----------- -----------
| PI | | IPsec |
----------- -----------
|
-----------
| PI |
-----------
Si le simulateur a fait des hypothèses et codé en dur dans le code, cette IP parle toujours
à un protocole de transport ci-dessus, l'utilisateur peut être contraint de pirater le système pour obtenir le
interconnexions souhaitées. Ce n'est clairement pas une manière optimale de concevoir un
simulateur.
Rappels Biographie
NOTE:
Les lecteurs familiarisés avec la programmation des rappels peuvent ignorer cette section du didacticiel.
Le mécanisme de base qui permet de résoudre le problème ci-dessus est connu sous le nom de rappeler.
Le but ultime est de permettre à un morceau de code d'appeler une fonction (ou une méthode en C++)
sans aucune dépendance inter-module spécifique.
Cela signifie en fin de compte que vous avez besoin d'une sorte d'indirection - vous traitez l'adresse du
fonction appelée en tant que variable. Cette variable est appelée variable pointeur vers fonction.
La relation entre la fonction et le pointeur vers la fonction n'est vraiment pas différente
celui de l'objet et du pointeur vers objet.
En C, l'exemple canonique d'un pointeur vers une fonction est un
pointeur-à-fonction-retournant-entier (PFI). Pour un PFI prenant un paramètre int, ce
pourrait être déclaré comme :
int (*pfi)(int arg) = 0 ;
Ce que vous obtenez de ceci est une variable nommée simplement Pfi qui est initialisé à la valeur 0.
Si vous voulez initialiser ce pointeur sur quelque chose de significatif, vous devez avoir un
fonction avec une signature correspondante. Dans ce cas:
int MaFonction (int arg) {}
Si vous avez cette cible, vous pouvez initialiser la variable pour pointer vers votre fonction comme :
pfi = MaFonction;
Vous pouvez alors appeler MyFunction indirectement en utilisant la forme plus suggestive de l'appel :
résultat int = (*pfi) (1234);
Ceci est suggestif car il semble que vous déréférencez simplement le pointeur de fonction
comme si vous déréférencez n'importe quel pointeur. En règle générale, cependant, les gens profitent de la
le fait que le compilateur sait ce qui se passe et utilisera simplement une forme plus courte :
résultat int = pfi (1234);
Notez que le pointeur de fonction obéit à la sémantique des valeurs, vous pouvez donc le faire circuler comme n'importe quel autre
autre valeur. En règle générale, lorsque vous utilisez une interface asynchrone, vous passez une entité
comme ceci à une fonction qui effectuera une action et Appelez-nous RETOUR pour vous le faire savoir
complété. Il rappelle en suivant l'indirection et en exécutant la fonction fournie.
En C++, vous avez la complexité supplémentaire des objets. L'analogie avec le PFI ci-dessus signifie que vous
avoir un pointeur vers une fonction membre renvoyant un int (PMI) au lieu du pointeur vers
fonction renvoyant un int (PFI).
La déclaration de la variable fournissant l'indirection n'est que légèrement différente :
int (MaClasse::*pmi) (int arg) = 0;
Cela déclare une variable nommée pmi tout comme l'exemple précédent a déclaré une variable nommée
Pfi. Puisque le sera d'appeler une méthode d'une instance d'une classe particulière, il faut
déclarer cette méthode dans une classe :
classe MaClasse {
publique:
int MaMéthode (int arg);
};
Étant donné cette déclaration de classe, on initialiserait alors cette variable comme ceci :
pmi = &MaClasse::MaMéthode;
Ceci affecte l'adresse du code implémentant la méthode à la variable, complétant
l'indirection. Pour appeler une méthode, le code a besoin d'un ceci. aiguille. Celui-ci, à son tour,
signifie qu'il doit y avoir un objet de MyClass auquel se référer. Un exemple simpliste de ceci est juste
appeler une méthode indirectement (pensez à une fonction virtuelle) :
int (MaClasse::*pmi) (int arg) = 0; // Déclarer un PMI
pmi = &MaClasse::MaMéthode; // Point sur le code d'implémentation
MaClasse maClasse; // Besoin d'une instance de la classe
(maClasse.*pmi) (1234); // Appel de la méthode avec un objet ptr
Tout comme dans l'exemple C, vous pouvez l'utiliser dans un appel asynchrone à un autre module
Qui va Appelez-nous RETOUR en utilisant une méthode et un pointeur d'objet. L'extension simple
on pourrait envisager de passer un pointeur vers l'objet et la variable PMI. Le module
ferait juste :
(*objetPtr.*pmi) (1234);
pour exécuter le rappel sur l'objet souhaité.
On pourrait se demander en ce moment, qu'est-ce que le point? Le module appelé devra comprendre
le type concret de l'objet appelant afin d'effectuer correctement le rappel. Pourquoi pas
acceptez simplement ceci, passez le pointeur d'objet correctement tapé et faites objet->Méthode(1234) in
le code au lieu du rappel ? C'est précisément le problème décrit ci-dessus. Quel est
nécessaire est un moyen de découpler complètement la fonction appelante de la classe appelée. Cette
l'exigence a conduit au développement de la foncteur.
Un foncteur est l'excroissance de quelque chose inventé dans les années 1960 appelé fermeture. Il est
fondamentalement juste un appel de fonction packagé, éventuellement avec un état.
Un foncteur a deux parties, une partie spécifique et une partie générique, liées par héritage.
Le code appelant (le code qui exécute le rappel) exécutera un générique surchargé
opérateur () d'un foncteur générique pour provoquer l'appel du callback. Le code appelé (le
qui veut être rappelé) devra fournir une implémentation spécialisée de
le opérateur () qui effectue le travail spécifique à la classe qui a causé le couplage étroit
problème ci-dessus.
Avec le foncteur spécifique et son surchargé opérateur () créé, le code appelé puis
donne le code spécialisé au module qui exécutera le rappel (le
code).
Le code appelant prendra un foncteur générique en paramètre, donc un transtypage implicite est effectué
dans l'appel de fonction pour convertir le foncteur spécifique en un foncteur générique. Ça signifie
que le module appelant a juste besoin de comprendre le type de foncteur générique. il est découplé
du code d'appel complètement.
L'information dont on a besoin pour faire un foncteur spécifique est le pointeur d'objet et le
adresse pointeur vers méthode.
L'essence de ce qui doit arriver est que le système déclare une partie générique du
foncteur :
modèle
foncteur de classe
{
publique:
opérateur int virtuel() (T arg) = 0 ;
};
L'appelant définit une partie spécifique du foncteur qui n'est vraiment là que pour implémenter
le spécifique opérateur() méthode:
modèle
classe SpecificFunctor : public Functor
{
publique:
SpecificFunctor(T* p, int (T ::*_pmi)(ARG arg))
{
m_p = p ;
m_pmi = _pmi ;
}
opérateur int virtuel () (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
privé:
int (T ::*m_pmi)(ARG arg);
T* m_p;
};
Voici un exemple d'utilisation :
Classe A
{
publique:
A (entier a0) : a (a0) {}
int Bonjour (int b0)
{
std::cout << "Bonjour de A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};
int main ()
{
A a(10);
SpécifiqueFunctor sf(&a, &A::Bonjour);
sf(5);
}
NOTE:
Le code précédent n'est pas du vrai code ns-3. C'est un exemple de code simpliste utilisé uniquement pour
illustrer les concepts impliqués et vous aider à mieux comprendre le système. Ne pas
attendez-vous à trouver ce code n'importe où dans l'arborescence ns-3.
Notez qu'il y a deux variables définies dans la classe ci-dessus. La variable m_p est la
pointeur d'objet et m_pmi est la variable contenant l'adresse de la fonction à
exécuter.
Remarquez que quand opérateur() est appelé, il appelle à son tour la méthode fournie avec le
pointeur d'objet utilisant la syntaxe C++ PMI.
Pour l'utiliser, on pourrait alors déclarer un code modèle qui prend un foncteur générique comme un
paramètre:
void LibraryFunction (foncteur Functor);
Le code qui parlera au modèle construira un foncteur spécifique et le passera à
Fonction Bibliothèque:
MaClasse maClasse;
SpécifiqueFunctor foncteur (&maclasse, MaClasse::MaMéthode);
Lorsque Fonction Bibliothèque est terminé, il exécute le rappel en utilisant le opérateur() sur le générique
foncteur il a été passé, et dans ce cas particulier, fournit l'argument entier :
annuler
LibraryFunction (Functor foncteur)
{
// Exécute la fonction de bibliothèque
foncteur(1234);
}
Remarquerez que Fonction Bibliothèque est complètement découplée du type spécifique du client.
La connexion se fait par le polymorphisme Functor.
L'API de rappel dans ns-3 implémente des rappels orientés objet en utilisant le mécanisme foncteur.
Cette API de rappel, basée sur des modèles C++, est de type sécurisé ; c'est-à-dire qu'il exécute statique
des contrôles de type pour imposer une compatibilité de signature appropriée entre les appelants et les appelés. Il est
donc plus sûr à utiliser que les pointeurs de fonction traditionnels, mais la syntaxe peut
sembler imposant au premier abord. Cette section est conçue pour vous guider à travers le système de rappel
afin que vous puissiez être à l'aise de l'utiliser dans ns-3.
En utilisant le Rappel API
L'API de rappel est assez minime, ne fournissant que deux services :
1. déclaration de type callback : une manière de déclarer un type de callback avec une signature donnée,
et,
2. instanciation de rappel : un moyen d'instancier un rappel de transfert généré par un modèle
qui peut transférer tous les appels à une autre méthode membre de classe C++ ou à une fonction C++.
Ceci est mieux observé en parcourant un exemple, basé sur exemples/main-callback.cc.
En utilisant le Rappel API avec statique fonctions
Considérons une fonction :
double statique
CbOne (double a, double b)
{
std::cout << "invoquer cbOne a=" << a << ", b=" << b << std::endl;
retourne un;
}
Considérez également l'extrait de programme principal suivant :
int main (int argc, char * argv [])
{
// type de retour : double
// premier type d'argument : double
// deuxième type d'argument : double
Rappeler un;
}
Ceci est un exemple de rappel de style C - qui n'inclut pas ou n'a pas besoin d'un ceci.
aiguille. Le modèle de fonction Rappel est essentiellement la déclaration de la variable
contenant le pointeur vers la fonction. Dans l'exemple ci-dessus, nous avons explicitement montré un pointeur
à une fonction qui a renvoyé un entier et a pris un seul entier comme paramètre, Le
Rappel La fonction template en est une version générique -- elle est utilisée pour déclarer le type
d'un rappel.
NOTE:
Les lecteurs peu familiarisés avec les modèles C++ peuvent consulter
http://www.cplusplus.com/doc/tutorial/templates/.
La Rappel template requiert un argument obligatoire (le type de retour de la fonction à
être affecté à ce rappel) et jusqu'à cinq arguments facultatifs, qui spécifient chacun le
type des arguments (si votre fonction de rappel particulière a plus de cinq arguments,
alors cela peut être géré en étendant l'implémentation de rappel).
Ainsi, dans l'exemple ci-dessus, nous avons déclaré un rappel nommé "one" qui finira par
tenir un pointeur de fonction. La signature de la fonction qu'elle va contenir doit retourner
double et doit supporter deux arguments doubles. Si on essaie de passer une fonction dont
signature ne correspond pas au rappel déclaré, une erreur de compilation se produira. Également si
on essaie d'affecter à un callback un callback incompatible, la compilation réussira mais un
l'exécution NS_FATAL_ERROR sera déclenchée. L'exemple de programme
src/core/examples/main-callback.cc démontre ces deux cas d'erreur à la fin de
le principale() .
Maintenant, nous devons lier cette instance de rappel et la fonction cible réelle
(CbOne). Notez ci-dessus que CbOne a les mêmes types de signature de fonction que le rappel--
c'est important. Nous pouvons passer n'importe quelle fonction correctement typée à ce rappel.
Regardons cela de plus près :
statique double CbOne (double a, double b) {}
^ ^ ^
| | |
| | |
Rappeler un;
Vous ne pouvez lier une fonction à un rappel que s'ils ont la signature correspondante. La première
l'argument de modèle est le type de retour, et les arguments de modèle supplémentaires sont les types
des arguments de la fonction signature.
Maintenant, lions notre callback "one" à la fonction qui correspond à sa signature :
// créer une instance de rappel qui pointe vers la fonction cbOne
un = MakeCallback (&CbOne);
Cet appel à FaireRappel consiste essentiellement à créer l'un des foncteurs spécialisés
mentionné ci-dessus. La variable déclarée à l'aide de la Rappel la fonction de modèle va
jouer le rôle du foncteur générique. La tâche UN = FaireRappel (&CbUn) is
le cast qui convertit le foncteur spécialisé connu de l'appelé en un foncteur générique
connu de l'appelant.
Ensuite, plus tard dans le programme, si le rappel est nécessaire, il peut être utilisé comme suit :
NS_ASSERT (!one.IsNull ());
// invoque la fonction cbOne via une instance de rappel
double retOne ;
retUn = un (10.0, 20.0);
Le chèque pour EstNull() garantit que le rappel n'est pas nul -- qu'il existe une fonction
d'appeler derrière ce rappel. Puis, un() exécute le générique opérateur() qui est vraiment
surchargé d'une implémentation spécifique de opérateur() et renvoie le même résultat que si
CbUn() avait été appelé directement.
En utilisant le Rappel API avec membre fonctions
En règle générale, vous n'appellerez pas de fonctions statiques mais plutôt des fonctions membres publiques de
un objet. Dans ce cas, un argument supplémentaire est nécessaire à la fonction MakeCallback, pour
dire au système sur quel objet la fonction doit être invoquée. Considérez cet exemple,
aussi de main-callback.cc :
classe MonCb {
publique:
int CbDeux (double a) {
std::cout << "invoquer cbTwo a=" << a << std::endl;
retour -5 ;
}
};
int main ()
{
// type de retour : entier
// premier type d'argument : double
Rappeler deux;
MyCb cb ;
// création d'une instance de rappel qui pointe vers MyCb::cbTwo
deux = MakeCallback (&MyCb::CbTwo, &cb);
}
Ici, nous passons un pointeur d'objet supplémentaire au Faire un rappel<> une fonction. Rappel de
la section d'arrière-plan au-dessus de cela Opérateur() utilisera le pointeur vers la syntaxe du membre lorsqu'il
s'exécute sur un objet :
opérateur int virtuel () (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
Et donc nous devions fournir les deux variables (m_p et m_pmi) lorsque nous avons fait le spécifique
foncteur. La ligne:
deux = MakeCallback (&MyCb::CbTwo, &cb);
fait précisément cela. Dans ce cas, quand deux () est invoqué :
résultat int = deux (1.0);
entraînera un appel au CbDeux fonction membre (méthode) sur l'objet pointé par
&cb.
Développement Null Rappels
Il est possible que les rappels soient nuls ; il peut donc être judicieux de vérifier avant de les utiliser.
Il existe une construction spéciale pour un rappel nul, qui est préférable au simple passage
"0" comme argument ; c'est le MakeNullCallback<> construction:
deux = MakeNullCallback ();
NS_ASSERT (deux.IsNull ());
L'invocation d'un rappel nul revient à invoquer un pointeur de fonction nul : il se bloquera à
Durée.
Bound Rappels
Une extension très utile du concept de foncteur est celle d'un Bound Callback. Auparavant, il
a été mentionné que les fermetures étaient à l'origine des appels de fonction emballés pour plus tard
exécution. Notez que dans toutes les descriptions de rappel ci-dessus, il n'y a aucun moyen de
empaqueter tous les paramètres pour une utilisation ultérieure - lorsque le Rappel est appelé via opérateur().
Tous les paramètres sont fournis par la fonction appelante.
Que faire si l'on souhaite autoriser la fonction client (celle qui fournit le rappel) à
fournir certains des paramètres? Alexandrescu appelle le processus permettant à un client de
spécifier l'un des paramètres "contraignant". L'un des paramètres de opérateur() a été
lié (fixé) par le client.
Une partie de notre code de traçage pcap en fournit un bon exemple. Il existe une fonction qui
doit être appelé chaque fois qu'un paquet est reçu. Cette fonction appelle un objet qui
écrit en fait le paquet sur le disque au format de fichier pcap. La signature de l'un de ces
les fonctions seront :
static void DefaultSink (Ptr fichier, Ptr p);
Le mot-clé static signifie qu'il s'agit d'une fonction statique qui n'a pas besoin d'un ceci. pointeur, donc
il utilisera des rappels de style C. Nous ne voulons pas que le code d'appel ait à connaître
tout sauf le paquet. Ce que nous voulons dans le code d'appel est juste un appel qui ressemble à :
m_promiscSnifferTrace (m_currentPkt);
Ce que nous voulons faire, c'est lier le Ptr filet au rappel spécifique
mise en œuvre lors de sa création et organiser la opérateur() du rappel à
fournir ce paramètre gratuitement.
Nous offrons le temps de réponse MakeBoundCallback fonction de modèle à cette fin. ça prend la même chose
paramètres comme le FaireRappel fonction de modèle mais prend également les paramètres pour être
bondir. Dans le cas de l'exemple ci-dessus :
MakeBoundCallback (&DefaultSink, fichier);
va créer une implémentation de rappel spécifique qui sait ajouter la limite supplémentaire
arguments. Conceptuellement, il étend le foncteur spécifique décrit ci-dessus avec un ou plusieurs
arguments liés :
modèle
classe SpecificFunctor : public Functor
{
publique:
SpecificFunctor(T* p, int (T ::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p ;
m_pmi = pmi ;
m_boundArg = boundArg ;
}
opérateur int virtuel () (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
privé:
void (T ::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg ;
};
Vous pouvez voir que lorsque le foncteur spécifique est créé, l'argument lié est enregistré dans le
foncteur / objet de rappel lui-même. Quand le opérateur() est invoqué avec le simple
paramètre, comme dans :
m_promiscSnifferTrace (m_currentPkt);
la mise en œuvre de la opérateur() ajoute le paramètre lié dans l'appel de fonction réel :
(*m_p.*m_pmi)(m_boundArg, arg);
Il est également possible de lier deux ou trois arguments. Disons que nous avons une fonction avec
Signature:
static void NotifyEvent (Ptr a, Ptr b, MyEventType e);
On peut créer une liaison de rappel liée avec les deux premiers arguments comme :
MakeBoundCallback (&NotifyEvent, a1, b1) ;
en supposant a1 et b1 sont des objets de type A et B respectivement. De même pour trois
arguments on aurait fonction avec une signature:
static void NotifyEvent (Ptr a, Ptr b, MyEventType e);
La liaison de trois arguments est terminée avec :
MakeBoundCallback (&NotifyEvent, a1, b1, c1) ;
en supposant à nouveau a1, b1 et c1 sont des objets de type A, B et C respectivement.
Ce type de liaison peut être utilisé pour échanger des informations entre des objets en simulation ;
en particulier, les rappels liés peuvent être utilisés comme rappels tracés, qui seront décrits dans
la section suivante.
Tracée Rappels
placeholder sous-section
Rappel restaurants in ns-3
Où les rappels sont-ils fréquemment utilisés dans ns-3? Voici quelques-uns des plus visibles à
utilisateurs typiques :
· API de socket
· API Couche-2/Couche-3
· Sous-système de traçage
· API entre IP et sous-systèmes de routage
Implantation détails
Les extraits de code ci-dessus sont simplistes et uniquement conçus pour illustrer le mécanisme
lui-même. Le code de rappel réel est assez compliqué et très intense en modèles et un
une compréhension approfondie du code n'est pas requise. S'ils sont intéressés, les utilisateurs experts peuvent trouver le
suivant utile.
Le code a été écrit à l'origine sur la base des techniques décrites dans
http://www.codeproject.com/cpp/TTLFunction.asp. Il a ensuite été réécrit pour suivre
l'architecture décrite dans une touche C + + conception Générique Programmation et Design Motifs
Appliqué, Alexandrescu, chapitre 5, Généralisé Fonctionneurs.
Ce code utilise :
· paramètres de modèle par défaut pour éviter aux utilisateurs d'avoir à spécifier des paramètres vides lorsque
le nombre de paramètres est inférieur au nombre maximum pris en charge
· l'idiome pimpl : la classe Callback est transmise par valeur et délègue le nœud de
le travail à son pointeur bouton.
· deux implémentations de pimpl qui dérivent de CallbackImpl FunctorCallbackImpl peuvent être utilisées
avec n'importe quel type de foncteur tandis que MemPtrCallbackImpl peut être utilisé avec des pointeurs vers le membre
fonctions.
· une implémentation de liste de référence pour implémenter la sémantique de valeur du Callback.
Ce code s'écarte notamment de l'implémentation Alexandrescu en ce qu'il ne
utilisez des listes de types pour spécifier et transmettre les types des arguments de rappel. Bien sûr,
il n'utilise pas non plus de sémantique de destruction de copie et s'appuie sur une liste de référence plutôt que sur
autoPtr pour contenir le pointeur.
Exlcusion modèle
ns-3 est fondamentalement un système objet C++. Les objets peuvent être déclarés et instanciés comme
d'habitude, selon les règles C++. ns-3 ajoute également quelques fonctionnalités aux objets C++ traditionnels, comme
décrit ci-dessous, pour fournir une plus grande fonctionnalité et fonctionnalités. Ce chapitre du manuel est
destiné à présenter au lecteur ns-3 modèle d'objet.
Cette section décrit la conception de classe C++ pour ns-3 objets. En bref, plusieurs conception
les modèles utilisés incluent la conception classique orientée objet (interfaces polymorphes et
implémentations), séparation de l'interface et de l'implémentation, le public non virtuel
modèle de conception d'interface, une fonction d'agrégation d'objets et un comptage de références pour
gestion de la mémoire. Ceux qui connaissent les modèles de composants tels que COM ou Bonobo
reconnaître les éléments de la conception dans le ns-3 modèle d'agrégation d'objets, bien que le ns-3
la conception n'est pas strictement conforme à l'un ou l'autre.
Orienté objet humain
Les objets C++, en général, fournissent des capacités orientées objet communes (abstraction,
encapsulation, héritage et polymorphisme) qui font partie des classiques orientés objet
l'oeuvre. ns-3 les objets utilisent ces propriétés ; par exemple:
classe Adresse
{
publique:
Adresse ();
Adresse (type uint8_t, const uint8_t *buffer, uint8_t len);
Adresse (const Adresse & adresse) ;
Adresse &opérateur = (const Adresse &adresse);
privé:
uint8_t m_type ;
uint8_t m_len ;
};
Exlcusion base les classes
Il existe trois classes de base spéciales utilisées dans ns-3. Les classes qui héritent de ces bases
les classes peuvent instancier des objets avec des propriétés spéciales. Ces classes de base sont :
· classer Exlcusion
· classer Base d'objets
· classer CompteRéfSimple
Il n'est pas obligatoire que ns-3 les objets héritent de ces classes, mais ceux qui obtiennent
propriétés spéciales. Classes dérivées de la classe Exlcusion obtenir les propriétés suivantes.
· les ns-3 type et système d'attributs (voir Attributs)
· un système d'agrégation d'objets
· un système de comptage de références à pointeur intelligent (classe Ptr)
Les classes qui dérivent de la classe Base d'objets obtenir les deux premières propriétés ci-dessus, mais ne
obtenir des pointeurs intelligents. Les classes qui dérivent de la classe CompteRéfSimple: n'obtenez que le
système de comptage de références à pointeur intelligent.
En pratique, la classe Exlcusion est la variante des trois ci-dessus que le ns-3 le développeur va
rencontre le plus souvent.
Mémoire gestion et classe Ptr
La gestion de la mémoire dans un programme C++ est un processus complexe et est souvent effectuée de manière incorrecte ou
de manière incohérente. Nous avons opté pour une conception de comptage de référence décrite comme suit.
Tous les objets utilisant le comptage de références conservent un comptage de références interne pour déterminer
lorsqu'un objet peut s'effacer en toute sécurité. Chaque fois qu'un pointeur est obtenu sur un
interface, le compteur de références de l'objet est incrémenté en appelant Réf(). C'est le
obligation de l'utilisateur du pointeur d'indiquer explicitement Annuler la référence() le pointeur une fois terminé. Lorsque
le compteur de références tombe à zéro, l'objet est supprimé.
· Lorsque le code client obtient un pointeur de l'objet lui-même via la création de l'objet,
ou via GetObject, il n'a pas besoin d'incrémenter le compteur de références.
· Lorsque le code client obtient un pointeur d'une autre source (par exemple, en copiant un pointeur), il doit
Appelez-nous Réf() pour incrémenter le compteur de référence.
· Tous les utilisateurs du pointeur d'objet doivent appeler Annuler la référence() pour libérer la référence.
Le fardeau d'appeler Annuler la référence() est quelque peu soulagée par l'utilisation du comptage de références
classe de pointeur intelligent décrite ci-dessous.
Utilisateurs utilisant une API de bas niveau qui souhaitent allouer explicitement des objets non comptés par référence
sur le tas, en utilisant l'opérateur new, sont responsables de la suppression de ces objets.
Références compte smart aiguille (Ptr)
appel Réf() et Annuler la référence() tout le temps serait encombrant, alors ns-3 fournit un smart
classe de pointeur Ptr similaire à Boost :: intrusif_ptr. Cette classe de pointeur intelligent suppose que
le type sous-jacent fournit une paire de Réf et Sans référence méthodes qui devraient
incrémenter et décrémenter le refcount interne de l'instance d'objet.
Cette implémentation vous permet de manipuler le pointeur intelligent comme s'il s'agissait d'un
pointeur : vous pouvez le comparer à zéro, le comparer à d'autres pointeurs, attribuer zéro à
ça, etc.
Il est possible d'extraire le pointeur brut de ce pointeur intelligent avec le ObtenirPointeur()
et Coup d'oeil() méthodes.
Si vous souhaitez stocker un nouvel objet dans un pointeur intelligent, nous vous recommandons d'utiliser le
Fonctions de modèle CreateObject pour créer l'objet et le stocker dans un pointeur intelligent pour
éviter les fuites de mémoire. Ces fonctions sont de très petites fonctions de commodité et leur objectif
est juste pour vous économiser un peu de frappe.
CreateObject et Créer
Les objets en C++ peuvent être créés de manière statique, dynamique ou automatique. Cela est vrai
pour ns-3 également, mais certains objets du système ont des cadres supplémentaires disponibles.
Plus précisément, les objets comptés par référence sont généralement alloués à l'aide d'un modèle Créer ou
Méthode CreateObject, comme suit.
Pour les objets dérivant de la classe Exlcusion:
Ptr appareil = CreateObject ();
Veuillez ne pas créer de tels objets en utilisant opérateur nouvelle; les créer en utilisant CréerObjet()
à la place.
Pour les objets dérivant de la classe CompteRéfSimple, ou d'autres objets qui prennent en charge l'utilisation du
classe de pointeur intelligent, une fonction d'assistance modélisée est disponible et recommandée pour être utilisée :
Ptr b = Créer ();
Il s'agit simplement d'un opérateur wrapper new qui gère correctement le comptage de références
système.
En résumé, utilisez Créer si B n'est pas un objet mais utilise simplement le comptage de références (par exemple
Paquet), et utilise CréerObjet si B dérive de ns3::Objet.
Agrégation
La ns-3 système d'agrégation d'objets est motivé en grande partie par la reconnaissance qu'un
cas d'utilisation courant pour ns-2 a été l'utilisation de l'héritage et du polymorphisme pour étendre
modèles de protocole. Par exemple, des versions spécialisées de TCP telles que RenoTcpAgent dérivent
de (et remplacer les fonctions de) la classe TcpAgent.
Cependant, deux problèmes qui se sont posés au ns-2 modèle sont downcasts et "faible base
classe." Le downcasting fait référence à la procédure d'utilisation d'un pointeur de classe de base vers un objet et
l'interroger au moment de l'exécution pour trouver des informations sur le type, utilisé pour convertir explicitement le pointeur
à un pointeur de sous-classe afin que l'API de sous-classe puisse être utilisée. La classe de base faible fait référence à la
problèmes qui surviennent lorsqu'une classe ne peut pas être effectivement réutilisée (dérivée de) parce qu'elle
manque des fonctionnalités nécessaires, ce qui conduit le développeur à devoir modifier la classe de base et
provoquant la prolifération des appels d'API de classe de base, dont certains peuvent ne pas être sémantiquement
correct pour toutes les sous-classes.
ns-3 utilise une version du modèle de conception d'interface de requête pour éviter ces problèmes.
Cette conception est basée sur des éléments de la Composant Exlcusion Modèle et GNOME Bonobo bien que
la compatibilité complète au niveau binaire des composants remplaçables n'est pas prise en charge et nous avons
essayé de simplifier la syntaxe et l'impact sur les développeurs de modèles.
Exemples
Agrégation (ici)
Nœud est un bon exemple de l'utilisation de l'agrégation dans ns-3. Notez qu'il n'y a pas de dérivés
classes de nœuds dans ns-3 comme la classe InternetNoeud. Au lieu de cela, les composants (protocoles) sont
agrégé à un nœud. Regardons comment certains protocoles IPv4 sont ajoutés à un nœud. :
vide statique
AjouterIpv4Stack(Ptr nœud)
{
Ptr ipv4 = CréerObjet ();
ipv4->SetNode (nœud);
nœud->AggregateObject (ipv4) ;
Ptr ipv4Impl = CréerObjet ();
ipv4Impl->SetIpv4 (ipv4) ;
nœud->AggregateObject (ipv4Impl);
}
Notez que les protocoles IPv4 sont créés en utilisant CréerObjet(). Ensuite, ils sont agrégés
au nœud. De cette manière, la classe de base Node n'a pas besoin d'être modifiée pour permettre aux utilisateurs
avec un pointeur Node de classe de base pour accéder à l'interface IPv4 ; les utilisateurs peuvent demander au nœud un
pointeur vers son interface IPv4 au moment de l'exécution. La manière dont l'utilisateur demande au nœud est décrite dans le
sous-section suivante.
Notez que c'est une erreur de programmation d'agréger plus d'un objet du même type pour
an ns3::Objet. Ainsi, par exemple, l'agrégation n'est pas une option pour stocker tous les
sockets actifs d'un nœud.
Getobject (ici)
GetObject est un moyen sûr de réaliser un downcasting sûr et de permettre aux interfaces d'être
trouvé sur un objet.
Considérons un pointeur de nœud m_node qui pointe vers un objet Node qui a une implémentation de
IPv4 y a été précédemment agrégé. Le code client souhaite configurer une route par défaut. À
pour ce faire, il doit accéder à un objet dans le nœud qui a une interface avec le transfert IP
configuration. Il effectue les opérations suivantes :
Ptr ipv4 = m_node->GetObject ();
Si le nœud n'a en fait pas d'objet IPv4 agrégé, alors la méthode
renvoie null. Par conséquent, il est recommandé de vérifier la valeur de retour d'une telle fonction
appeler. En cas de succès, l'utilisateur peut maintenant utiliser le Ptr vers l'objet IPv4 qui était auparavant
agrégé au nœud.
Un autre exemple d'utilisation de l'agrégation consiste à ajouter des modèles facultatifs aux objets. Pour
exemple, un objet Node existant peut avoir un objet "Modèle énergétique" qui lui est agrégé à
temps d'exécution (sans modifier et recompiler la classe de nœud). Un modèle existant (comme un
périphérique réseau sans fil) peut ensuite "GetObject" pour le modèle énergétique et agir de manière appropriée
si l'interface a été intégrée à l'objet Node sous-jacent ou agrégée à
il au moment de l'exécution. Cependant, les autres nœuds n'ont besoin de rien savoir sur les modèles énergétiques.
Nous espérons que ce mode de programmation nécessitera beaucoup moins de modifications aux développeurs
les classes de base.
Exlcusion usines
Un cas d'utilisation courant consiste à créer de nombreux objets configurés de manière similaire. On peut à plusieurs reprises
Appelez-nous CréerObjet() mais il existe également un modèle de conception d'usine utilisé dans le ns-3 système.
Il est fortement utilisé dans l'API "helper".
Classe Usine d'objets peut être utilisé pour instancier des objets et pour configurer les attributs sur
ces objets :
annuler SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
Ptr Créer (annuler) const ;
La première méthode permet d'utiliser le ns-3 Système TypeId pour spécifier le type d'objets
établi. La seconde permet de définir des attributs sur les objets à créer, et la
le troisième permet de créer les objets eux-mêmes.
Par exemple :
Usine ObjectFactory ;
// Faire en sorte que cette fabrique crée des objets de type FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Faire en sorte que cet objet d'usine change la valeur par défaut d'un attribut, par exemple
// objets créés par la suite
factory.Set ("SystemLoss", DoubleValue (2.0));
// Créer un tel objet
Ptr objet = usine.Créer ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Créer un autre objet avec un SystemLoss différent
Ptr objet = usine.Créer ();
Downcasting
Une question qui s'est posée plusieurs fois est : « Si j'ai un pointeur de classe de base (Ptr) vers un
objet et je veux le pointeur de classe dérivée, dois-je effectuer un downcast (via un cast dynamique C++) vers
obtenir le pointeur dérivé, ou devrais-je utiliser le système d'agrégation d'objets pour ObtenirObjet<> ()
pour trouver un Ptr à l'interface de l'API de sous-classe ?"
La réponse à cela est que dans de nombreuses situations, les deux techniques fonctionneront. ns-3 fournit une
fonction modélisée pour rendre la syntaxe de la coulée dynamique d'objets beaucoup plus utilisateur
amical:
modèle
Ptr
DynamicCast (Ptr const&p)
{
retour Ptr (diffusion_dynamique (PeekPointer (p)));
}
DynamicCast fonctionne lorsque le programmeur a un pointeur de type de base et teste par rapport à un
pointeur de sous-classe. GetObject fonctionne lors de la recherche de différents objets agrégés, mais aussi
fonctionne avec des sous-classes, de la même manière que DynamicCast. En cas de doute, le programmeur doit
utilisez GetObject, car cela fonctionne dans tous les cas. Si le programmeur connaît la hiérarchie des classes de
l'objet considéré, il est plus direct d'utiliser simplement DynamicCast.
Configuration et Attributs
In ns-3 simulations, il y a deux aspects principaux à la configuration :
· La topologie de simulation et comment les objets sont connectés.
· Les valeurs utilisées par les modèles instanciés dans la topologie.
Ce chapitre se concentre sur le deuxième élément ci-dessus : comment les nombreuses valeurs utilisées dans ns-3 are
organisé, documenté et modifiable par ns-3 utilisateurs. le ns-3 système d'attributs est également le
la manière dont les traces et les statistiques sont collectées dans le simulateur.
Au cours de ce chapitre, nous discuterons des différentes manières de définir ou de modifier les valeurs
utilisé par ns-3 objets modèles. Par ordre croissant de spécificité, ce sont :
?? ??
│Méthode │ Portée │
?? ??
│Valeurs d'attribut par défaut définies │ Affectent toutes les instances du │
│lorsque les attributs sont définis dans la classe │. ??
│ObtenirTypeId (). │
?? ??
│Ligne de commande Affecte toutes les instances futures. ??
│Config :: SetDefault () │
│Magasin de configuration │
?? ??
│Usine d'objets │ Affecte toutes les instances créées │
│ avec l'usine. ??
?? ??
│XHelperSetAttributeXHelperSetAttribute () │ Affecte toutes les instances créées par │
│ l'assistant. ??
?? ??
│MaClasse::SetX () │ Modifie cette instance particulière. ??
│Objet :: SetAttribute () Généralement c'est la seule forme │
│Config::Set() │ qui peut être programmé pour changer │
│ │ une instance une fois la simulation │
│ │ est en cours d'exécution. ??
?? ??
Par "spécificité", nous entendons que les méthodes des lignes ultérieures du tableau remplacent les valeurs définies
par, et affectent généralement moins d'instances que les méthodes précédentes.
Avant d'entrer dans les détails du système de valeur d'attribut, il sera utile d'examiner certains
propriétés de base de la classe Exlcusion.
Exlcusion Aperçu
ns-3 est fondamentalement un système à base d'objets C++. Nous entendons par là que les nouvelles classes C++
(types) peuvent être déclarés, définis et sous-classés comme d'habitude.
Merci beaucoup ns-3 les objets héritent du Exlcusion classe de base. Ces objets ont des
propriétés que nous exploitons pour organiser le système et améliorer la gestion de la mémoire
de nos objets :
· Système de « Métadonnées » qui relie le nom de la classe à de nombreuses méta-informations sur le
objet, comprenant :
· La classe de base de la sous-classe,
· L'ensemble des constructeurs accessibles dans la sous-classe,
· L'ensemble des "attributs" de la sous-classe,
· Si chaque attribut peut être défini ou est en lecture seule,
· La plage de valeurs autorisée pour chaque attribut.
· Mise en œuvre de pointeur intelligent de comptage de références, pour la gestion de la mémoire.
ns-3 les objets qui utilisent le système d'attributs dérivent soit de Exlcusion or Base d'objets. Plus
ns-3 les objets dont nous allons parler dérivent de Exlcusion, mais quelques-uns qui sont en dehors de la smart
cadre de gestion de mémoire de pointeur dérivé de Base d'objets.
Passons en revue quelques propriétés de ces objets.
Smart Pointers
Comme introduit dans le ns-3 Didacticiel, ns-3 les objets sont de la mémoire gérée par un référence
compte smart aiguille la mise en oeuvre, classer Ptr.
Les pointeurs intelligents sont largement utilisés dans le ns-3 API, pour éviter de passer des références à
objets alloués au tas qui peuvent provoquer des fuites de mémoire. Pour l'utilisation la plus basique (syntaxe), traitez
un pointeur intelligent comme un pointeur ordinaire :
Ptr nd = ...;
nd->CallSomeFunction ();
// etc.
Alors, comment obtenez-vous un pointeur intelligent vers un objet, comme dans la première ligne de cet exemple ?
CreateObject
Comme nous l'avons vu ci-dessus dans Memory-management-and-class-Ptr, au niveau de l'API le plus bas, les objets
De type Exlcusion ne sont pas instanciés à l'aide opérateur nouvelle comme d'habitude mais à la place par un modèle
fonction appelée CreateObject ().
Une manière typique de créer un tel objet est la suivante :
Ptr nd = CréerObjet ();
Vous pouvez considérer cela comme étant fonctionnellement équivalent à :
WifiNetDevice* nd = nouveau WifiNetDevice ();
Les objets qui dérivent de Exlcusion doit être alloué sur le tas en utilisant CreateObject (). Ceux
dérivant de Base d'objets tels que ns-3 fonctions d'assistance et en-têtes et remorques de paquets,
peuvent être alloués sur la pile.
Dans certains scripts, vous ne verrez peut-être pas beaucoup de CreateObject () appels dans le code ; c'est
car il y a des objets assistants en vigueur qui font le CreateObject () en cours
pour vous.
ID de type
ns-3 classes qui dérivent de classe Exlcusion peut inclure une classe de métadonnées appelée ID de type qui
enregistre des méta-informations sur la classe, à utiliser dans l'agrégation d'objets et le composant
systèmes de gestion :
· Une chaîne unique identifiant la classe.
· La classe de base de la sous-classe, dans le système de métadonnées.
· L'ensemble des constructeurs accessibles dans la sous-classe.
· Une liste des propriétés accessibles au public ("attributs") de la classe.
Exlcusion Résumé
En rassemblant tous ces concepts, regardons un exemple spécifique : la classe Nœud.
Le fichier d'en-tête public nœud.h a une déclaration qui inclut un statique ObtenirTypeId ()
appel de fonction:
Nœud de classe : objet public
{
publique:
statique TypeId GetTypeId (void);
Ceci est défini dans le nœud.cc fichier comme suit:
ID de type
Noeud :: GetTypeId (vide)
{
statique TypeId tid = TypeId ("ns3::Node")
.SetParent ()
.AddConstructor ()
.AddAttribute ("DeviceList",
"La liste des appareils associés à ce nœud.",
ObjetVectorValue (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker ())
.AddAttribute ("ApplicationList",
"La liste des applications associées à ce nœud.",
ObjetVectorValue (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker ())
.AddAttribute ("Id",
"L'id (entier unique) de ce nœud.",
TypeId::ATTR_GET, // permet seulement de l'obtenir.
Valeur Uinteger (0),
MakeUintegerAccessor (&Node::m_id),
MarqueUintegerChecker ())
;
retour tid;
}
Considérons le ID de type des ns-3 Exlcusion classe en tant que forme étendue de type d'exécution
informations (RTTI). Le langage C++ inclut un type simple de RTTI afin de prendre en charge
diffusion_dynamique et identifiant de type des opérateurs.
La SetParent () appel dans la définition ci-dessus est utilisé en conjonction avec notre
mécanismes d'agrégation d'objets pour permettre un transtypage ascendant et descendant sécurisé dans les arbres d'héritage
pendant Getobject (). Il permet également aux sous-classes d'hériter des attributs de leur parent
classe.
La Ajouter un constructeur () call est utilisé en conjonction avec notre fabrique d'objets abstraits
mécanismes pour nous permettre de construire des objets C++ sans forcer un utilisateur à connaître le
classe concrète de l'objet qu'elle construit.
Les trois appels à Ajouter un attribut () associer une chaîne donnée à une valeur fortement typée dans
la classe. Notez que vous devez fournir une chaîne d'aide qui peut être affichée, par exemple,
via processeurs de ligne de commande. Chaque Attribut est associé à des mécanismes d'accès
la variable membre sous-jacente dans l'objet (par exemple, MarqueUintegerAccessor () raconte
le générique Attribut code comment accéder à l'ID de nœud ci-dessus). Il y a aussi "Checker"
méthodes utilisées pour valider les valeurs par rapport aux limites de plage, telles que les valeurs maximales et
valeurs minimales autorisées.
Lorsque les utilisateurs souhaitent créer des nœuds, ils appellent généralement une forme de CreateObject (),:
Ptr n = CréerObjet ();
ou plus abstraitement, en utilisant une fabrique d'objets, vous pouvez créer un Nœud objet sans même
connaissant le type C++ concret :
Usine ObjectFactory ;
const std::string typeId = "ns3::Node'';
usine.SetTypeId (typeId);
Ptr nœud = usine.Créer ();
Ces deux méthodes permettent de disposer d'attributs entièrement initialisés dans le
résultant Exlcusion les instances.
Nous discutons ensuite de la façon dont les attributs (valeurs associées aux variables membres ou aux fonctions de
la classe) sont reliés à ce qui précède ID de type.
Attributs
Le but du système d'attributs est d'organiser l'accès aux objets membres internes d'un
simulation. Cet objectif survient parce que, généralement dans la simulation, les utilisateurs couperont et
coller/modifier des scripts de simulation existants, ou utilisera des constructions de simulation de niveau supérieur,
mais seront souvent intéressés par l'étude ou le traçage de variables internes particulières. Pour
exemple, des cas d'utilisation tels que :
· "I souhaitez à tracer le paquets on le sans fil interface uniquement on le premier accès point."
· "I souhaitez à tracer le Plus-value of le TCP congestion fenêtre (tous fois it changements) on a
particulier TCP prise."
· "I souhaitez a déverser of tous valeurs qui ont été d'utiliser in my simulation."
De même, les utilisateurs peuvent souhaiter un accès précis aux variables internes de la simulation, ou
peut vouloir modifier largement la valeur initiale utilisée pour un paramètre particulier dans tous les
objets créés par la suite. Enfin, les utilisateurs peuvent souhaiter savoir quelles variables sont réglables
et récupérable dans une configuration de simulation. Ce n'est pas seulement pour la simulation directe
interaction sur la ligne de commande ; envisager également une (future) interface utilisateur graphique qui
aimerait pouvoir fournir une fonctionnalité permettant à un utilisateur de cliquer avec le bouton droit de la souris sur un nœud de
le canevas et voir une liste hiérarchisée et organisée de paramètres réglables sur le
nœud et ses objets membres constitutifs, et le texte d'aide et les valeurs par défaut pour chaque
paramètre.
Définir Attributs
Nous offrons aux utilisateurs un moyen d'accéder aux valeurs en profondeur dans le système, sans avoir à sonder
accesseurs (pointeurs) à travers le système et parcourez les chaînes de pointeurs pour les atteindre. Considérez un
classe DropTailQueue qui a une variable membre qui est un entier non signé m_maxPaquets;
cette variable membre contrôle la profondeur de la file d'attente.
Si nous regardons la déclaration de DropTailQueue, on voit ceci :
classe DropTailQueue : file d'attente publique {
publique:
statique TypeId GetTypeId (void);
privé:
std::file d'attente > m_paquets ;
uint32_t m_maxPaquets ;
};
Considérons les choses qu'un utilisateur peut vouloir faire avec la valeur de m_maxPaquets:
· Définir une valeur par défaut pour le système, telle que chaque fois qu'un nouveau DropTailQueue est créé,
ce membre est initialisé à cette valeur par défaut.
· Définir ou obtenir la valeur sur une file d'attente déjà instanciée.
Les éléments ci-dessus nécessitent généralement de fournir Ensemble () et Obtenez () fonctions et certains types de
valeur globale par défaut.
Dans le ns-3 système d'attributs, ces définitions de valeurs et enregistrements de fonction d'accesseur
sont déplacés dans le ID de type classe; par exemple.:
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
ID de type
DropTailQueue :: GetTypeId (vide)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent ()
.AddConstructor ()
.AddAttribute ("MaxPackets",
"Le nombre maximum de paquets acceptés par cette DropTailQueue.",
Valeur Uinteger (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MarqueUintegerChecker ())
;
retour tid;
}
La Ajouter un attribut () méthode effectue un certain nombre de choses pour le m_maxPaquets valeur:
· Liaison de la variable membre (généralement privée) m_maxPaquets à une chaîne publique
"MaxPaquets".
· Fournir une valeur par défaut (100 paquets).
· Fournir un texte d'aide définissant la signification de la valeur.
· Fournir un "Checker" (non utilisé dans cet exemple) qui peut être utilisé pour définir des limites sur le
plage de valeurs autorisée.
Le point clé est que maintenant la valeur de cette variable et sa valeur par défaut sont accessibles
dans l'espace de noms d'attribut, qui est basé sur des chaînes telles que "MaxPaquets" et ID de type prénom
cordes. Dans la section suivante, nous fournirons un exemple de script qui montre comment les utilisateurs peuvent
manipuler ces valeurs.
Notez que l'initialisation de l'attribut repose sur la macro NS_OBJECT_ENSURE_REGISTERED
(DropTailQueue) être appelé; si vous omettez cela de votre nouvelle implémentation de classe, votre
les attributs ne seront pas initialisés correctement.
Bien que nous ayons décrit comment créer des attributs, nous n'avons toujours pas décrit comment accéder
et gérer ces valeurs. Par exemple, il n'y a pas globals.h fichier d'en-tête où ils se trouvent
stockée; les attributs sont stockés avec leurs classes. Les questions qui se posent naturellement sont de savoir comment
les utilisateurs apprennent-ils facilement tous les attributs de leurs modèles et comment un utilisateur
accéder à ces attributs, ou documenter leurs valeurs dans le cadre de l'enregistrement de leur
simulation?
Une documentation détaillée des attributs réels définis pour un type et une liste globale des
tous les attributs définis, sont disponibles dans la documentation de l'API. Pour le reste de ça
document, nous allons démontrer les différentes façons d'obtenir et de définir l'attribut
valeurs.
Paramètres Réglage par défaut Nos valeurs
Config :: SetDefault et Ligne de commande
Voyons comment un script utilisateur peut accéder à une valeur d'attribut spécifique. Nous allons
utiliser le src/point-à-point/examples/main-attribute-value.cc script pour illustration, avec
quelques détails supprimés. le principal la fonction commence :
// Ceci est un exemple basique d'utilisation du système d'attributs pour
// définit et obtient une valeur dans le système sous-jacent ; à savoir, un non signé
// entier du nombre maximum de paquets dans une file d'attente
//
int
principal (int argc, char *argv[])
{
// Par défaut, l'attribut MaxPackets a une valeur de 100 paquets
// (cette valeur par défaut peut être observée dans la fonction DropTailQueue::GetTypeId)
//
// Ici, nous le définissons à 80 paquets. Nous pourrions utiliser l'un des deux types de valeur :
// une valeur basée sur une chaîne ou une valeur Uinteger
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// L'appel de fonction ci-dessous est redondant
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
// Autoriser l'utilisateur à remplacer l'une des valeurs par défaut et ce qui précède
// SetDefaults () au moment de l'exécution, via des arguments de ligne de commande
// Par exemple, via "--ns3::DropTailQueue::MaxPackets=80"
cmd de ligne de commande ;
// Ceci fournit encore un autre moyen de définir la valeur à partir de la ligne de commande :
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);
La principale chose à remarquer dans ce qui précède sont les deux appels équivalents à Config :: SetDefault
(). C'est ainsi que nous définissons la valeur par défaut pour tous les instanciations ultérieures
DropTailQueues. Nous illustrons que deux types de Valeur cours, un Valeur de chaîne et
Valeur Uinteger class, peut être utilisé pour affecter la valeur à l'attribut nommé par
"ns3::DropTailQueue::MaxPackets".
Il est également possible de manipuler les attributs en utilisant le Ligne de commande; nous avons vu des exemples
au début du didacticiel. En particulier, il est simple d'ajouter un argument abrégé
nom, comme --maxPaquets, pour un attribut particulièrement pertinent pour votre modèle,
dans ce cas "ns3::DropTailQueue::MaxPackets". Cela a la caractéristique supplémentaire que le
La chaîne d'aide pour l'attribut sera imprimée dans le cadre du message d'utilisation du script.
Pour de plus amples renseignements, consultez le Ligne de commande Documentation API.
Maintenant, nous allons créer quelques objets en utilisant l'API de bas niveau. Nos files d'attente nouvellement créées
pas m_maxPaquets initialisé à 100 paquets, comme défini dans le
DropTailQueue ::GetTypeId () fonction, mais à 80 paquets, à cause de ce que nous avons fait ci-dessus avec
les valeurs par défaut.:
Ptr n0 = CréerObjet ();
Ptr net0 = CréerObjet ();
n0->Ajouter un appareil (net0) ;
Ptr q = CréerObjet ();
net0->AddQueue(q);
À ce stade, nous avons créé un seul Nœud (n0) et un seul PointVersPointNetDevice
(net0) et a ajouté un DropTailQueue (q) À net0.
Constructeurs, assistants et Usine d'objets
Des combinaisons arbitraires d'attributs peuvent être définies et récupérées à partir de l'assistant et de bas niveau
Apis; soit des constructeurs eux-mêmes :
Ptr p =
CréerObjetAvecAttributs
("MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
ou à partir des API d'assistance de niveau supérieur, telles que :
Mobility.SetPositionAllocator
("ns3::GridPositionAllocator",
"MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
Nous ne l'illustrons pas ici, mais vous pouvez également configurer un Usine d'objets avec de nouvelles valeurs
pour des attributs spécifiques. Les instances créées par le Usine d'objets auront ceux
attributs définis lors de la construction. Ceci est très similaire à l'utilisation de l'une des API d'aide
pour la classe.
Pour passer en revue, il existe plusieurs façons de définir des valeurs pour les attributs des instances de classe à be
créée in le avenir:
· Config :: SetDefault ()
· CommandLine::AjouterValeur ()
· CréerObjetAvecAttributs<> ()
· Diverses API d'aide
Mais que se passe-t-il si vous avez déjà créé une instance et que vous souhaitez modifier la valeur du
attribut? Dans cet exemple, comment pouvons-nous manipuler le m_maxPaquets valeur du déjà
instancié DropTailQueue? Voici différentes façons de le faire.
Changer Nos valeurs
Pointeur intelligent
Supposons qu'un pointeur intelligent (Ptr) vers un périphérique réseau pertinent est en cours ; dans l'actuel
exemple, c'est le net0 aiguille.
Une façon de changer la valeur est d'accéder à un pointeur vers la file d'attente sous-jacente et de modifier son
attribuer.
Tout d'abord, nous observons que nous pouvons obtenir un pointeur vers la (classe de base) Queue via le
PointVersPointNetDevice attributs, où il est appelé "TxQueue":
PointeurValue tmp ;
net0->GetAttribute ("TxQueue", tmp);
Ptr txQueue = tmp.GetObject ();
Le Getobject () fonction, nous pouvons effectuer un downcast en toute sécurité vers un DropTailQueue, Où
"MaxPaquets" est un attribut :
Ptr dtq = txQueue->GetObject ();
NS_ASSERT (dtq ! = 0);
Ensuite, nous pouvons obtenir la valeur d'un attribut sur cette file d'attente. Nous avons introduit l'emballage
Valeur des classes pour les types de données sous-jacents, similaires aux wrappers Java autour de ces types,
puisque le système d'attributs stocke des valeurs sérialisées dans des chaînes, et non des types disparates.
Ici, la valeur d'attribut est affectée à un Valeur Uintegerainsi que, Obtenez () méthode sur ce
valeur produit le (déballé) uint32_t.:
Limite UintegerValue ;
dtq->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " packets");
Notez que le downcast ci-dessus n'est pas vraiment nécessaire ; nous aurions pu obtenir l'attribut
valeur directement de txQueue, qui est un Exlcusion:
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " packets");
Maintenant, définissons-le sur une autre valeur (60 paquets) :
txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("3. Limite txQueue modifiée : " << limit.Get () << " paquets");
Config Espace de noms Chemin
Une autre façon d'accéder à l'attribut consiste à utiliser l'espace de noms de configuration. Ici,
cet attribut réside sur un chemin connu dans cet espace de noms ; cette approche est utile si l'on
n'a pas accès aux pointeurs sous-jacents et souhaite configurer un
attribut avec une seule instruction. :
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
Valeur Uinteger (25));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO (" 4. Limite txQueue modifiée via l'espace de noms : "
<< limit.Get () << " paquets");
Le chemin de configuration a souvent la forme de ".../
nom>/ /.../ / " faire référence à une instance spécifique par l'index d'un
objet dans le conteneur. Dans ce cas, le premier conteneur est la liste de tous Nœuds; les
le deuxième conteneur est la liste de tous NetDevices sur le choisi Nœud. Finalement, le
chemin de configuration se termine généralement par une succession d'attributs membres, dans ce cas le
"MaxPaquets" attribut de la "TxQueue" de l'élu NetDevice.
Nous aurions également pu utiliser des caractères génériques pour définir cette valeur pour tous les nœuds et tous les périphériques réseau
(qui dans cet exemple simple a le même effet que le précédent Configuration :: Définir ()):
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
Valeur Uinteger (15));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO (" 5. Limite txQueue modifiée via l'espace de noms générique : "
<< limit.Get () << " paquets");
Exlcusion Nom Service
Une autre façon d'accéder à l'attribut consiste à utiliser la fonction de service de nom d'objet. le
le service de nom d'objet nous permet d'ajouter des éléments à l'espace de noms de configuration sous le
"/Noms/" chemin avec une chaîne de nom définie par l'utilisateur. Cette approche est utile si l'on ne
avoir accès aux pointeurs sous-jacents et il est difficile de déterminer le
chemin d'espace de noms de configuration concret.
Names::Add ("serveur", n0) ;
Names::Add ("server/eth0", net0) ;
Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
Ici, nous avons ajouté les éléments de chemin "serveur" et "eth0" sous le "/Noms/" espace de noms, puis
utilisé le chemin de configuration résultant pour définir l'attribut.
Voir les noms d'objet pour un traitement plus complet du ns-3 espace de noms de configuration.
Implantation Plus de détails
Valeur Cours
Les lecteurs noteront le TypeValeur classes qui sont des sous-classes de Valeur d'attribut base
classer. Ceux-ci peuvent être considérés comme des classes intermédiaires qui sont utilisées pour convertir
types à la Valeur d'attributs qui sont utilisés par le système d'attributs. Rappelons que ce
La base de données contient des objets de nombreux types sérialisés en chaînes. Conversions vers ce type
peut être fait en utilisant une classe intermédiaire (telle que ValeurEntier, ou DoubleValeur pour
nombres à virgule flottante) ou via cordes. Conversion implicite directe des types en
Valeur d'attribut n'est pas vraiment pratique. Ainsi, dans ce qui précède, les utilisateurs ont le choix d'utiliser
chaînes ou valeurs :
p->Set ("cwnd", StringValue ("100")); // setter basé sur une chaîne
p->Set ("cwnd", IntegerValue (100)); // setter basé sur des entiers
Le système fournit des macros qui aident les utilisateurs à déclarer et à définir une nouvelle AttributeValue
sous-classes pour les nouveaux types qu'ils souhaitent introduire dans le système d'attributs :
· ATTRIBUT_HELPER_HEADER
· ATTRIBUT_HELPER_CPP
Consultez la documentation de l'API pour ces constructions pour plus d'informations.
Initialisation Order
Les attributs dans le système ne doivent pas dépendre de l'état d'un autre attribut dans ce
système. C'est parce qu'un ordre d'initialisation d'attribut n'est pas spécifié, ni
imposé, par le système. Un exemple spécifique de ceci peut être vu dans la configuration automatisée
des programmes tels que Magasin de configuration. Bien qu'un modèle donné puisse l'arranger de sorte que les Attributs
sont initialisés dans un ordre particulier, un autre configurateur automatique peut décider
indépendamment pour modifier les attributs, par exemple par ordre alphabétique.
En raison de cet ordre non spécifique, aucun attribut dans le système ne peut avoir de dépendance
sur tout autre attribut. En corollaire, les setters d'attributs ne doivent jamais échouer en raison de l'état
d'un autre Attribut. Aucun setter d'attribut ne peut changer (définir) une autre valeur d'attribut en tant que
résultat de la modification de sa valeur.
Il s'agit d'une restriction très forte et il y a des cas où les attributs doivent définir
systématiquement pour permettre un fonctionnement correct. À cette fin, nous permettons un contrôle de cohérence
quand le attribuer is d'utiliser (cf. NS_ASSERT_MSG or NS_ABORT_MSG).
En général, le code d'attribut pour affecter des valeurs aux variables membres de classe sous-jacentes
est exécuté après la construction d'un objet. Mais que faire si vous avez besoin des valeurs attribuées
avant que le corps du constructeur ne s'exécute, car vous en avez besoin dans la logique du
constructeur? Il existe un moyen de le faire, utilisé par exemple dans la classe Magasin de configuration: appel
ObjectBase ::ConstructSelf () comme suit:
ConfigStore ::ConfigStore ()
{
ObjectBase :: ConstructSelf (AttributeConstructionList ());
// continue avec le constructeur.
}
Attention, l'objet et toutes ses classes dérivées doivent également implémenter un ObtenirInstanceTypeId
() méthode. Sinon le ObjectBase ::ConstructSelf () ne pourra pas lire le
attributs.
L'ajout de Attributs
La ns-3 système placera un certain nombre de valeurs internes sous le système d'attributs, mais
sans aucun doute les utilisateurs voudront étendre cela pour récupérer ceux que nous avons manqués, ou pour ajouter leur
propres classes au système.
Il existe trois cas d'utilisation typiques :
· Rendre un membre de données de classe existant accessible en tant qu'attribut, lorsqu'il ne l'est pas déjà.
· Faire une nouvelle classe capable d'exposer certains membres de données en tant qu'attributs en lui donnant un TypeId.
· Création d'un Valeur d'attribut sous-classe pour une nouvelle classe afin qu'elle soit accessible en tant que
Attribut.
Existant Membre Variable
Considérez cette variable dans TcpSocket:
uint32_t m_cWnd; // Fenêtre d'encombrement
Supposons que quelqu'un travaillant avec TCP souhaite obtenir ou définir la valeur de cette variable
en utilisant le système de métadonnées. S'il n'était pas déjà fourni par ns-3, l'utilisateur peut déclarer
l'ajout suivant dans le système de métadonnées d'exécution (au ObtenirTypeId() définition pour
TcpSocket):
.AddAttribute ("Fenêtre d'encombrement",
"Fenêtre de congestion TCP (octets)",
Valeur Uinteger (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MarqueUintegerChecker ())
Maintenant, l'utilisateur avec un pointeur vers un TcpSocket l'instance peut effectuer des opérations telles que
définir et obtenir la valeur, sans avoir à ajouter ces fonctions explicitement.
De plus, des contrôles d'accès peuvent être appliqués, comme permettre la lecture du paramètre et
pas écrit, ou des limites vérifiant les valeurs admissibles peuvent être appliquées.
Équipement Classe ID de type
Ici, nous discutons de l'impact sur un utilisateur qui souhaite ajouter une nouvelle classe à ns-3. Que
des choses supplémentaires doivent être faites pour lui permettre de contenir des attributs ?
Supposons notre nouvelle classe, appelée ns3 :: Ma Mobilité, est un type de modèle de mobilité. D'abord,
la classe doit hériter de sa classe parente, ns3::MobilityModèle. Dans le ma-mobilite.h
En tête de fichier:
espace de noms ns3 {
classe MyClass : public MobilityModel
{
Cela nécessite que nous déclarions le ObtenirTypeId () une fonction. Il s'agit d'une fonction publique sur une ligne
déclaration:
publique:
/ **
* Enregistrez ce type.
* \return L'objet TypeId.
*/
statique TypeId GetTypeId (void);
Nous avons déjà présenté ce qu'est un ID de type la définition ressemblera à la ma-mobilite.cc
dossier d'implémentation :
NS_OBJECT_ENSURE_REGISTERED (MaMobilité);
ID de type
MyMobility :: GetTypeId (vide)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.SetParent ()
.SetGroupName ("Mobilité")
.AddConstructor ()
.AddAttribute ("Bounds",
"Les limites de la zone de croisière.",
ValeurRectangle (Rectangle (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
MakerectangleChecker ())
.AddAttribute ("Heure",
"Modifier la direction et la vitesse du courant après s'être déplacé pendant ce délai.",
TimeValue (Secondes (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// etc (plus de paramètres).
;
retour tid;
}
Si nous ne voulons pas sous-classer à partir d'une classe existante, dans le fichier d'en-tête, nous héritons simplement
de ns3::Objet, et dans le fichier objet, nous définissons la classe parente sur ns3::Objet avec
.SetParent ().
Les erreurs typiques ici impliquent :
· Ne pas appeler NS_OBJECT_ENSURE_REGISTERED ()
· Ne pas appeler le Setparent () méthode, ou en l'appelant avec le mauvais type.
· Ne pas appeler le Ajouter un constructeur () méthode, ou en l'appelant avec le mauvais type.
· L'introduction d'une erreur typographique dans le nom du ID de type dans son constructeur.
· Ne pas utiliser le nom de type C++ complet de la classe C++ englobante comme nom du
ID de type. Noter que "ns3 ::" est nécessaire.
Aucune de ces erreurs ne peut être détectée par le ns-3 base de code, il est donc conseillé aux utilisateurs de vérifier
soigneusement plusieurs fois qu'ils ont bien compris.
Équipement Valeur d'attribut Type
Du point de vue de l'utilisateur qui écrit une nouvelle classe dans le système et veut qu'elle soit
accessible en tant qu'attribut, il s'agit principalement d'écrire les conversions vers/depuis
chaînes et valeurs d'attributs. La plupart de ces éléments peuvent être copiés/collés avec du code macro-isé. Pour
exemple, considérons une déclaration de classe pour Rectangulaire dans le src/mobilité/modèle annuaire:
En-tête Fichier
/ **
* \brief un rectangle 2d
*/
classe Rectangle
{
double xMin ;
double xMax ;
yMin double ;
double yMax ;
};
Un appel de macro et deux opérateurs doivent être ajoutés sous la déclaration de classe afin de
transformer un Rectangle en une valeur utilisable par le Attribut système:
std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
std::istream &operator >> (std::istream &is, Rectangle &rectangle);
ATTRIBUT_HELPER_HEADER (Rectangle);
Implantation Fichier
Dans la définition de classe (. Cc fichier), le code ressemble à ceci :
ATTRIBUT_HELPER_CPP (Rectangle);
std::ostream &
opérateur << (std::ostream &os, const Rectangle &rectangle)
{
os << rectangle.xMin << "|" << rectangle.xMax << "|" << rectangle.yMin << "|"
<< rectangle.yMax;
retour os;
}
std::istream &
opérateur >> (std::istream &is, Rectangle &rectangle)
{
caractère c1, c2, c3 ;
est >> rectangle.xMin >> c1 >> rectangle.xMax >> c2 >> rectangle.yMin >> c3
>> rectangle.yMax;
si (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
le retour est ;
}
Ces opérateurs de flux convertissent simplement à partir d'une représentation sous forme de chaîne du Rectangle
("xMin|xMax|yMin|yMax") au Rectangle sous-jacent. Le modélisateur doit spécifier ces
opérateurs et la représentation syntaxique sous forme de chaîne d'une instance de la nouvelle classe.
Magasin de configuration
Valeurs pour ns-3 les attributs peuvent être stockés dans un fichier texte ASCII ou XML et chargés dans un
future simulation. Cette fonction est connue sous le nom de ns-3 ConfigStore. le Magasin de configuration is
une base de données spécialisée pour les valeurs d'attributs et les valeurs par défaut.
Bien qu'il s'agisse d'un module géré séparément dans le src/config-store/ répertoire, nous
documentez-le ici en raison de sa seule dépendance ns-3 module de base et attributs.
Nous pouvons explorer ce système en utilisant un exemple de
src/config-store/examples/config-store-save.cc.
Premièrement, tous les utilisateurs du Magasin de configuration doit inclure la déclaration suivante :
#include "ns3/config-store-module.h"
Ensuite, ce programme ajoute un exemple d'objet Exemple de configuration pour montrer comment le système est étendu :
classe ConfigExample : objet public
{
publique:
statique TypeId GetTypeId (void) {
statique TypeId tid = TypeId ("ns3::A")
.SetParent ()
.AddAttribute ("TestInt16", "texte d'aide",
ValeurEntier (-2),
MakeIntegerAccessor (&A::m_int16),
MakeIntegerChecker ())
;
retour tid;
}
int16_t m_int16 ;
};
NS_OBJECT_ENSURE_REGISTERED (exemple de configuration) ;
Ensuite, nous utilisons le sous-système Config pour remplacer les valeurs par défaut de plusieurs manières :
Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
Ptr a_obj = CréerObjet ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
"Impossible de définir l'attribut entier de ConfigExample via Config::SetDefault");
Ptr a2_obj = CréerObjet ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
ValeurEntier iv ;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Get() == -3,
"Impossible de définir l'attribut entier de ConfigExample via SetAttribute");
L'instruction suivante est nécessaire pour s'assurer que (l'un des) objets créés est enraciné
dans l'espace de noms de configuration en tant qu'instance d'objet. Cela se produit normalement lorsque vous
agréger des objets à un ns3::Noeud or ns3::Canal exemple, mais ici, puisque nous travaillons
au niveau du noyau, nous devons créer un nouvel objet d'espace de noms racine :
Config ::RegisterRootNamespaceObject (a2_obj);
Écriture
Ensuite, nous voulons sortir le magasin de configuration. Les exemples montrent comment le faire en deux
formats, XML et texte brut. En pratique, il faut effectuer cette étape juste avant d'appeler
Simulateur :: Exécuter () pour enregistrer la configuration finale juste avant de lancer la simulation.
Il existe trois attributs qui régissent le comportement du ConfigStore : "Mode",
"Nom de fichier" et "Format de fichier". Le Mode (par défaut "Rien") configure si ns-3 devrait
charger la configuration à partir d'un fichier précédemment enregistré (préciser "Mode=Charger") ou enregistrez-le dans un fichier
(spécifier "Mode=Enregistrer"). Le nom de fichier (par défaut "") est l'endroit où le ConfigStore doit lire ou
écrire ses données. Le format de fichier (par défaut "Texte brut") détermine si le format ConfigStore
est du texte brut ou du Xml ("Format de fichier=Xml")
L'exemple montre :
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore sortieConfig ;
sortieConfig.ConfigureDefaults ();
sortieConfig.ConfigureAttributes ();
// Sortie du magasin de configuration au format txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore outputConfig2 ;
sortieConfig2.ConfigureDefaults ();
sortieConfig2.ConfigureAttributes ();
Simulateur::Exécuter ();
Simulateur :: Détruire ();
Notez l'emplacement de ces déclarations juste avant la Simulateur :: Exécuter () déclaration.
Cette sortie enregistre toutes les valeurs en place juste avant le démarrage de la simulation (c'est à dire.
après que toute la configuration a eu lieu).
Après l'exécution, vous pouvez ouvrir le attributs-de-sortie.txt fichier et voir :
par défaut ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
par défaut ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
par défaut ns3::PcapFileWrapper::CaptureSize "65535"
par défaut ns3::PacketSocket::RcvBufSize "131072"
par défaut ns3::ErrorModel::IsEnabled "true"
par défaut ns3::RateErrorModel::ErrorUnit "EU_BYTE"
par défaut ns3::RateErrorModel::ErrorRate "0"
par défaut ns3::RateErrorModel::RanVar "Uniform:0:1"
par défaut ns3::DropTailQueue::Mode "Paquets"
par défaut ns3::DropTailQueue::MaxPackets "100"
par défaut ns3::DropTailQueue::MaxBytes "6553500"
par défaut ns3::Application::StartTime "+0.0ns"
par défaut ns3::Application::StopTime "+0.0ns"
par défaut ns3::ConfigStore::Mode "Enregistrer"
par défaut ns3::ConfigStore::Filename "output-attributes.txt"
par défaut ns3::ConfigStore::FileFormat "RawText"
par défaut ns3::ConfigExample::TestInt16 "-5"
global RngSeed "1"
RngRun global "1"
global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
global SchedulerType "ns3::MapScheduler"
global ChecksumEnabled "false"
valeur /$ns3::ConfigExample/TestInt16 "-3"
Dans ce qui précède, toutes les valeurs par défaut des attributs du module principal sont affichées.
Ensuite, toutes les valeurs du ns-3 les valeurs globales sont enregistrées. Enfin, la valeur de la
exemple de Exemple de configuration qui était enraciné dans l'espace de noms de configuration est affiché. Dans un
réal ns-3 programme, beaucoup plus de modèles, d'attributs et de valeurs par défaut seraient affichés.
Une version XML existe également en attributs-de-sortie.xml:
Ce fichier peut être archivé avec votre script de simulation et les données de sortie.
en cours
Ensuite, nous discutons de la configuration des simulations via un fichier de configuration d'entrée stocké. Il y a
quelques différences clés par rapport à l'écriture de la configuration de simulation finale.
Tout d'abord, nous devons placer des déclarations telles que celles-ci au début du programme, avant
les instructions de configuration de simulation sont écrites (ainsi les valeurs sont enregistrées avant d'être
utilisé dans la construction d'objets).
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore entréeConfig ;
inputConfig.ConfigureDefaults ();
Ensuite, notez que le chargement des données de configuration d'entrée est limité à l'attribut par défaut (c'est à dire.
pas instance) valeurs et valeurs globales. Les valeurs d'instance d'attribut ne sont pas prises en charge
car à ce stade de la simulation, avant la construction des objets, il n'y a pas
ces instances d'objet autour. (Remarque, les améliorations futures du magasin de configuration peuvent changer
Ce comportement).
Deuxièmement, tandis que la sortie de Magasin de configuration l'état listera tout dans la base de données, le
Le fichier d'entrée doit uniquement contenir les valeurs spécifiques à remplacer. Donc, une façon d'utiliser
cette classe pour la configuration du fichier d'entrée est de générer une configuration initiale en utilisant le
sortir ("Sauvegarder") "Mode" décrit ci-dessus, extrayez de ce fichier de configuration uniquement le
éléments que l'on souhaite modifier, et déplacer ces éléments minimaux vers un nouveau fichier de configuration
qui peut ensuite être édité et chargé en toute sécurité dans une simulation ultérieure.
When the Magasin de configuration l'objet est instancié, ses attributs "Nom de fichier", "Mode" et
"Format de fichier" doit être réglé, soit via ligne de commande ou via déclarations de programme.
Lecture écriture Exemple
Comme exemple plus compliqué, supposons que nous voulons lire dans une configuration de
par défaut à partir d'un fichier d'entrée nommé entrée-defaults.xml, et écrivez le résultat
attributs dans un fichier séparé appelé attributs-de-sortie.xml.:
#include "ns3/config-store-module.h"
int main (...)
{
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore entréeConfig ;
inputConfig.ConfigureDefaults ();
//
// Autoriser l'utilisateur à remplacer l'une des valeurs par défaut et le Bind () ci-dessus à
// runtime, arguments via la ligne de commande
//
cmd de ligne de commande ;
cmd.Parse (argc, argv);
// configuration de la topologie
// Appel juste avant d'entrer dans Simulator::Run ()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
ConfigStore sortieConfig ;
sortieConfig.ConfigureAttributes ();
Simulateur::Exécuter ();
}
Magasin de configuration GUI
Il existe un frontal basé sur GTK pour le ConfigStore. Cela permet aux utilisateurs d'utiliser une interface graphique pour
accéder et modifier les variables. Des captures d'écran de cette fonctionnalité sont disponibles dans le |ns3|
Aperçu présentation.
Pour utiliser cette fonctionnalité, il faut installer libgtk et libgtk-dev; un exemple Ubuntu
la commande d'installation est :
$ sudo apt-get install libgtk2.0-0 libgtk2.0-dev
Pour vérifier s'il est configuré ou non, vérifiez la sortie de l'étape :
$ ./waf configurer --enable-examples --enable-tests
---- Résumé des fonctionnalités optionnelles du NS-3 :
Liaisons Python : activées
Prise en charge de l'analyse de l'API Python : activée
Intégration NS-3 Click : activée
GtkConfigStore : non activé (bibliothèque 'gtk+-2.0 >= 2.12' introuvable)
Dans l'exemple ci-dessus, il n'a pas été activé, il ne peut donc pas être utilisé tant qu'une version appropriée n'est pas
installé et :
$ ./waf configurer --enable-examples --enable-tests
$ ./waf
est réexécuté.
L'utilisation est presque la même que la version non basée sur GTK, mais il n'y a pas Magasin de configuration
attributs impliqués :
// Appel juste avant d'entrer dans Simulator::Run ()
Configuration de GtkConfigStore ;
config.ConfigureDefaults ();
config.ConfigureAttributes ();
Maintenant, lorsque vous exécutez le script, une interface graphique devrait apparaître, vous permettant d'ouvrir les menus de
attributs sur différents nœuds/objets, puis lancez l'exécution de la simulation lorsque vous
sont fait.
A venir travail
Il y a quelques améliorations possibles :
· Enregistrez un numéro de version unique avec la date et l'heure au début du fichier.
· Enregistrez la graine initiale rng quelque part.
· Faire en sorte que chaque RandomVariable sérialise sa propre graine initiale et la relise plus tard.
Exlcusion noms
placeholder chapitre
Journal
La ns-3 la fonction de journalisation peut être utilisée pour surveiller ou déboguer la progression de la simulation
programmes. La sortie de journalisation peut être activée par des instructions de programme dans votre principale() programme ou
par l'utilisation du NS_LOG variable d'environnement.
Les instructions de journalisation ne sont pas compilées dans des versions optimisées de ns-3. Pour utiliser la journalisation, un
doit construire la version de débogage (par défaut) de ns-3.
Le projet ne garantit pas que la sortie de journalisation restera la même sur
temps. Les utilisateurs sont mis en garde contre la création de cadres de sortie de simulation en plus de la journalisation
code, car la sortie et la façon dont la sortie est activée peuvent changer avec le temps.
Aperçu
ns-3 les instructions de journalisation sont généralement utilisées pour enregistrer divers événements d'exécution de programme, tels que
comme l'occurrence d'événements de simulation ou l'utilisation d'une fonction particulière.
Par exemple, cet extrait de code provient de Ipv4L3Protocol ::IsDestinationAddress():
if (adresse == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("Pour moi (adresse de diffusion de l'interface)");
return true;
}
Si la journalisation a été activée pour le Protocole IPv4L3 composant à une gravité de LOGIQUE or
ci-dessus (voir ci-dessous à propos de la gravité du journal), la déclaration sera imprimée ; sinon, il
sera supprimée.
Activation Sortie
Les utilisateurs contrôlent généralement la sortie du journal de deux manières. La première consiste à régler le
NS_LOG variable d'environnement ; par exemple:
$ NS_LOG="*" ./waf --run first
exécutera le premier programme de tutoriel avec toutes les sorties de journalisation. (Les spécificités du NS_LOG
le format sera discuté ci-dessous.)
Cela peut être rendu plus granulaire en sélectionnant des composants individuels :
$ NS_LOG="Ipv4L3Protocol" ./waf --run first
La sortie peut être davantage adaptée avec des options de préfixe.
La deuxième façon d'activer la journalisation consiste à utiliser des instructions explicites dans votre programme, comme dans
le premier programme de tuto :
int
principal (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
(Le sens de LOG_LEVEL_INFO, et d'autres valeurs possibles, seront discutées ci-dessous.)
NS_LOG Syntaxe
La NS_LOG La variable d'environnement contient une liste de composants et d'options de journal. Enregistrer
les composants sont séparés par des caractères `:' :
$ NS_LOG=" : ..."
Les options pour chaque composant de journal sont données sous forme d'indicateurs après chaque composant de journal :
$ NS_LOG=" = | ... : ..."
Les options contrôlent la gravité et le niveau de ce composant, et si elles sont facultatives
des informations doivent être incluses, telles que le temps de simulation, le nœud de simulation, la fonction
nom, et la sévérité symbolique.
Historique Composants
Généralement, un composant de journal fait référence à un seul code source . Cc dossier et englobe le
dossier entier.
Certains assistants ont des méthodes spéciales pour activer la journalisation de tous les composants d'un module,
couvrant différentes unités de compilation, mais regroupées logiquement, comme le ns-3
code wifi :
WifiHelperwifiHelper ;
wifiHelper.EnableLogComponents ();
La NS_LOG Le caractère générique du composant de journal `*' activera tous les composants.
Pour voir quels composants de journal sont définis, n'importe lequel d'entre eux fonctionnera :
$ NS_LOG="liste-d'impression" ./waf --run ...
$ NS_LOG="foo" # un jeton ne correspondant à aucun composant de journal
Le premier formulaire imprimera le nom et les indicateurs activés pour tous les composants de journal qui sont
lié dans; essayez avec simulateur de scratch. Le deuxième formulaire imprime tous les journaux enregistrés
composants, puis quitter avec une erreur.
Gravité et Niveau Options
Les messages individuels appartiennent à une seule « classe de gravité », définie par la macro créant le
message. Dans l'exemple ci-dessus, NS_LOG_LOGIC(..) crée le message dans le LOG_LOGIQUE
classe de gravité.
Les classes de gravité suivantes sont définies comme enum constantes :
?? ??
│Classe de gravité │ Signification │
?? ??
│LOG_NONE │ La valeur par défaut, pas de journalisation │
?? ??
│LOG_ERREUR │ Messages d'erreur graves uniquement │
?? ??
│LOG_WARN Messages d'avertissement │
?? ??
│LOG_DEBUG │ Pour une utilisation dans le débogage │
?? ??
│LOG_INFO Informationnel │
?? ??
│LOG_FUNCTION │ Fonction traçage │
?? ??
│LOG_LOGIQUE │ Contrôler le traçage des flux au sein de │
│ fonctions │
?? ??
Généralement, on veut voir les messages à une classe de gravité donnée et augmentation. Ceci est fait par
définir des "niveaux" de journalisation inclusifs :
?? ??
│Niveau │ Signification │
?? ??
│LOG_LEVEL_ERROR Seulement LOG_ERREUR classe de gravité │
│ messages. ??
?? ??
│LOG_LEVEL_WARN │ LOG_WARN et ci-dessus. ??
?? ??
│LOG_LEVEL_DEBUG │ LOG_DEBUG et ci-dessus. ??
?? ??
│LOG_LEVEL_INFO │ LOG_INFO et ci-dessus. ??
?? ??
│LOG_LEVEL_FUNCTION │ LOG_FUNCTION et ci-dessus. ??
?? ??
│LOG_LEVEL_LOGIC │ LOG_LOGIQUE et ci-dessus. ??
?? ??
│LOG_LEVEL_ALL │ Toutes les classes de gravité. ??
?? ??
│LOG_TOUS Synonyme de LOG_LEVEL_ALL │
?? ??
Les options de classe et de niveau de gravité peuvent être indiquées dans le NS_LOG variable d'environnement par
ces jetons :
??
│Classe │ Niveau │
??
│erreur │ niveau_erreur │
??
│prévenir │ niveau_avertissement │
??
│déboguer │ niveau_debug │
??
│info │ info_niveau │
??
│fonction │ fonction_niveau │
??
│logique │ niveau_logique │
??
│ niveau_tous │
│ tous │
│ * │
??
L'utilisation d'un jeton de classe de gravité active les messages de journal à cette gravité uniquement. Par example,
NS_LOG="*=avertir" n'affichera pas de messages avec gravité erreur. NS_LOG="*=level_debug" sera
messages de sortie aux niveaux de gravité déboguer et ci-dessus.
Les classes et niveaux de gravité peuvent être combinés avec le `|' opérateur:
NS_LOG="*=level_warn|logique" affichera des messages aux niveaux de gravité erreur, prévenir et logique.
La NS_LOG caractère générique de niveau de gravité « * » et tous sont des synonymes de niveau_tous.
Pour les composants de journal simplement mentionnés dans NS_LOG
$ NS_LOG=" :..."
la gravité par défaut est LOG_LEVEL_ALL.
Titre Options
Un certain nombre de préfixes peuvent aider à identifier où et quand un message est originaire, et à quel
gravité.
Les options de préfixe disponibles (comme enum constantes) sont
?? ??
│Symbole de préfixe │ Signification │
?? ??
│LOG_PREFIX_FUNC │ Préfixe le nom de l'appelant │
│ fonction. ??
?? ??
│LOG_PREFIX_TIME │ Préfixez l'heure de la simulation. ??
?? ??
│LOG_PREFIX_NODE Préfixez l'identifiant du nœud. ??
?? ??
│LOG_PREFIX_LEVEL │ Préfixez le niveau de gravité. ??
?? ??
│LOG_PREFIX_ALL Activez tous les préfixes. ??
?? ??
Les options de préfixe sont décrites brièvement ci-dessous.
Les options peuvent être données dans le NS_LOG variable d'environnement par ces jetons :
??
│Jeton │ Suppléant │
??
│préfixe_fonc │ fonction │
??
│préfixe_heure │ fois │
??
│préfixe_node │ nœud │
??
│niveau_préfixe │ niveau │
??
│préfixe_all │ tous │
│ * │
??
Pour les composants de journal simplement mentionnés dans NS_LOG
$ NS_LOG=" :..."
les options de préfixe par défaut sont LOG_PREFIX_ALL.
Gravité Titre
La classe de gravité d'un message peut être incluse avec les options niveau_préfixe or niveau.
Par exemple, cette valeur de NS_LOG active la journalisation pour tous les composants de journal (`*') et tous
classes de gravité (= tout) et préfixe le message avec la classe de gravité (|prefix_level).
$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
Simulateur de grattage
Message d'erreur [ERREUR]
[WARN] message d'avertissement
[DEBUG] message de débogage
[INFO] message d'information
Message de fonction [FUNCT]
Message logique [LOGIQUE]
Heure Titre
Le temps de simulation peut être inclus avec les options préfixe_heure or fois. Cela imprime le
temps de simulation en secondes.
Nœud Titre
L'identifiant du nœud de simulation peut être inclus avec les options préfixe_node or nœud.
Fonction Titre
Le nom de la fonction appelante peut être inclus avec les options préfixe_fonc or fonction.
NS_LOG Wildcards
Le caractère générique du composant de journal « * » activera tous les composants. Pour activer tous les composants à un
niveau de gravité spécifique utilisation *=.
Le caractère générique de l'option de niveau de gravité « * » est synonyme de tous. Cela doit se produire avant tout
'|' caractères séparant les options. Pour activer toutes les classes de gravité, utilisez =*,
or =*|.
L'option joker `*' ou jeton tous active toutes les options de préfixe, mais doit se produire après a
'|' personnage. Pour activer une classe ou un niveau de gravité spécifique et tous les préfixes, utilisez
= |*.
Le joker de l'option combinée ** permet toutes les sévérités et tous les préfixes ; par example,
=**.
Le super joker *** active toutes les gravités et tous les préfixes pour tous les composants du journal.
Ceux-ci sont tous équivalents :
$ NS_LOG="***" ... $ NS_LOG="*=tous|*" ... $ NS_LOG="*=*|tous" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$ NS_LOG="*=*|*" ...
Soyez avisé: même le trivial simulateur de scratch produit plus de 46 XNUMX lignes de sortie avec
NS_LOG="***"!
Comment à ajouter enregistrement à votre code
L'ajout de la journalisation à votre code est très simple :
1. Appelez le NS_LOG_COMPONENT_DEFINE (...) ; macro à l'intérieur de namespace ns3.
Créez un identifiant de chaîne unique (généralement basé sur le nom du fichier et/ou de la classe
défini dans le fichier) et enregistrez-le avec un appel de macro comme suit :
espace de noms ns3 {
NS_LOG_COMPONENT_DEFINE ("Protocole Ipv4L3");
Cela enregistre Protocole IPv4L3 en tant que composant de journal.
(La macro a été soigneusement écrite pour permettre l'inclusion à l'intérieur ou à l'extérieur de
namespace ns3, et l'utilisation variera selon la base de code, mais l'intention initiale était de
enregistrer ceci au contrôle de l'espace de noms ns3 à la portée globale du fichier.)
2. Ajoutez des instructions de journalisation (appels de macro) à vos fonctions et corps de fonction.
Journal Macros
Les macros de journalisation et les niveaux de gravité associés sont
┌───────────────┬─────────────────────────
│Classe de gravité │ Macro │
├───────────────┼─────────────────────────
│LOG_NONE │ (pas nécessaire) │
├───────────────┼─────────────────────────
│LOG_ERREUR │ NS_LOG_ERROR (...) ; │
├───────────────┼─────────────────────────
│LOG_WARN │ NS_LOG_WARN (...) ; │
├───────────────┼─────────────────────────
│LOG_DEBUG │ NS_LOG_DEBUG (...) ; │
├───────────────┼─────────────────────────
│LOG_INFO │ NS_LOG_INFO (...) ; │
├───────────────┼─────────────────────────
│LOG_FUNCTION │ NS_LOG_FUNCTION (...) ; │
├───────────────┼─────────────────────────
│LOG_LOGIQUE │ NS_LOG_LOGIC (...) ; │
└───────────────┴─────────────────────── ─┘
Les macros fonctionnent comme des streamers de sortie, donc tout ce que vous pouvez envoyer à std :: cout, rejoint
by << opérateurs, est autorisé :
void MyClass::Check (valeur int, char * élément)
{
NS_LOG_FUNCTION (cet élément << arg <<);
si (arg > 10)
{
NS_LOG_ERROR ("valeur incorrecte rencontrée" << valeur <
" lors de la vérification de " << nom << "!");
}
}
Notez que NS_LOG_FUNCTION insère automatiquement un `,' (virgule-espace) séparateur entre
chacun de ses arguments. Cela simplifie la journalisation des arguments de fonction ; simplement concaténer
avec les << comme dans l'exemple ci-dessus.
Inconditionnel Journal
Par commodité, le NS_LOG_UNCOND (...) ; macro enregistrera toujours ses arguments, même si
le composant de journal associé n'est activé à aucune gravité. Cette macro n'utilise aucun
des options de préfixe. Notez que la journalisation n'est activée que dans les versions de débogage ; cette macro
ne produira pas de sortie dans les versions optimisées.
Recommandations
· Commencez chaque méthode de classe avec NS_LOG_FUNCTION (ce << arguments...); Cela permet facilement
suivi des appels de fonction.
· Sauf : ne consignez pas les opérateurs ou les constructeurs de copie explicites, car ceux-ci entraîneront
récursivité infinie et débordement de pile.
· Pour les méthodes sans arguments, utilisez le même formulaire : NS_LOG_FUNCTION (cette);
· Pour les fonctions statiques :
· Avec utilisation d'arguments NS_LOG_FUNCTION (...) ; comme d'habitude.
· Utilisation sans arguments NS_LOG_FUNCTION_NOARGS ();
· Utilisation NS_LOG_ERROR pour les conditions d'erreurs graves qui invalident probablement la simulation
exécution.
· Utilisation NS_LOG_WARN pour des conditions inhabituelles qui peuvent être corrigées. Merci de donner quelques conseils
quant à la nature du problème et comment il pourrait être corrigé.
· NS_LOG_DEBUG est généralement utilisé dans un ad hoc façon de comprendre l'exécution d'un modèle.
· Utilisation NS_LOG_INFO pour plus d'informations sur l'exécution, telles que la taille d'un
structure de données lors de son ajout/suppression.
· Utilisation NS_LOG_LOGIC tracer des branches logiques importantes au sein d'une fonction.
· Vérifiez que vos modifications de journalisation ne cassent pas le code. Exécutez quelques exemples de programmes avec
tous les composants du journal sont activés (par exemple NS_LOG="***").
Traçant
Le sous-système de traçage est l'un des mécanismes les plus importants à comprendre dans ns-3. En
la plupart des cas, ns-3 les utilisateurs auront une idée brillante pour une mise en réseau nouvelle et améliorée
caractéristique. Afin de vérifier que cette idée fonctionne, le chercheur apportera des modifications à un
système existant, puis exécutez des expériences pour voir comment la nouvelle fonctionnalité se comporte en rassemblant
statistiques qui capturent le comportement de la fonctionnalité.
En d'autres termes, l'intérêt d'exécuter une simulation est de générer une sortie pour d'autres
étude. Dans ns-3, le sous-système qui permet à un chercheur de le faire est le traçage
sous-système.
Traçant motivation
Il existe de nombreuses façons d'extraire des informations d'un programme. Le moyen le plus direct est
pour imprimer directement les informations sur la sortie standard, comme dans,
#inclure
int main ()
{
std::cout << "La valeur de x est " << x << std::endl;
}
Ceci est réalisable dans de petits environnements, mais à mesure que vos simulations deviennent de plus en plus
compliqué, vous vous retrouvez avec de plus en plus d'impressions et la tâche d'analyser et d'effectuer
les calculs sur la sortie commencent à devenir de plus en plus difficiles.
Une autre chose à considérer est que chaque fois qu'une nouvelle friandise est nécessaire, le noyau du logiciel
doit être édité et une autre impression introduite. Il n'existe aucun moyen standardisé de contrôler tous
de cette production, de sorte que la quantité de production tend à croître sans limites. Finalement, le
la bande passante nécessaire pour simplement sortir ces informations commence à limiter le temps d'exécution
de la simulation. Les fichiers de sortie atteignent des tailles énormes et les analyser devient un
problème.
ns-3 fournit un mécanisme simple pour la journalisation et un certain contrôle sur la sortie via
Historique Composants, mais le niveau de contrôle n'est pas du tout très fin. La journalisation
module est un instrument relativement émoussé.
Il est souhaitable d'avoir une installation qui permette d'accéder au système central et seulement
obtenir les informations requises sans avoir à modifier et recompiler le système central. Même
mieux serait un système qui avertirait l'utilisateur lorsqu'un élément d'intérêt a changé ou un
événement intéressant s'est produit.
La ns-3 Le système de traçage est conçu pour fonctionner dans ce sens et est bien intégré avec
les sous-ensembles Attribut et Config permettant des scénarios d'utilisation relativement simples.
Aperçu
Le sous-système de traçage s'appuie fortement sur le ns-3 Mécanismes de rappel et d'attribut. Vous
doit lire et comprendre les sections correspondantes du manuel avant d'essayer de
comprendre le système de traçage.
La ns-3 système de traçage repose sur les concepts de sources de traçage indépendantes et
puits de traçage; ainsi qu'un mécanisme uniforme pour connecter les sources aux puits.
Les sources de trace sont des entités qui peuvent signaler des événements qui se produisent dans une simulation et fournir
accès à des données sous-jacentes intéressantes. Par exemple, une source de trace peut indiquer quand un
le paquet est reçu par un dispositif réseau et fournit un accès au contenu du paquet pour
les puits de trace intéressés. Une source de trace peut également indiquer quand un état intéressant
le changement se produit dans un modèle. Par exemple, la fenêtre de congestion d'un modèle TCP est un nombre premier
candidat pour une source de trace.
Les sources de traces ne sont pas utiles en elles-mêmes ; ils doivent être connectés à d'autres morceaux de code
qui font réellement quelque chose d'utile avec les informations fournies par la source. le
les entités qui consomment des informations de trace sont appelées puits de trace. Les sources de traces sont
les générateurs d'événements et les puits de trace sont des consommateurs.
Cette division explicite permet à un grand nombre de sources de traces d'être dispersées autour
le système à des endroits qui, selon les auteurs du modèle, pourraient être utiles. Sauf si un utilisateur connecte un
trace sink vers l'une de ces sources, rien n'est produit. Cette disposition permet relativement
les utilisateurs non avertis d'attacher de nouveaux types de puits aux sources de traçage existantes, sans
nécessitant l'édition et la recompilation du cœur ou des modèles du simulateur.
Il peut y avoir zéro ou plusieurs consommateurs d'événements de trace générés par une source de trace. On peut
considérez une source de trace comme une sorte de lien d'information point à multipoint.
Le "protocole de transport" pour cette liaison conceptuelle point à multipoint est un ns-3 Rappel.
Rappelez-vous de la section de rappel que la fonction de rappel est un moyen d'autoriser deux modules dans
au système de communiquer via des appels de fonction tout en découplant l'appel
complètement la fonction de la classe appelée. Il s'agit de la même exigence que celle décrite ci-dessus
pour le système de traçage.
Fondamentalement, une source de trace is un rappel auquel plusieurs fonctions peuvent être enregistrées.
Lorsqu'un récepteur de trace exprime son intérêt à recevoir des événements de trace, il ajoute un rappel à un
liste des rappels détenus par la source de trace. Lorsqu'un événement intéressant se produit, la trace
source invoque son opérateur() fournissant zéro ou plusieurs paramètres. Cela indique à la source de
parcourir sa liste de rappels en invoquant chacun à son tour. Ainsi, le(s) paramètre(s)
sont communiquées aux puits de trace, qui ne sont que des fonctions.
La Le plus simple Exemple
Il sera utile d'aller parcourir un exemple rapide juste pour renforcer ce que nous avons dit. :
#include "ns3/objet.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h""
#include "ns3/trace-source-accessor.h"
#inclure
en utilisant l'espace de noms ns3 ;
La première chose à faire est d'inclure les fichiers requis. Comme mentionné ci-dessus, le système de trace
fait un usage intensif des systèmes d'objets et d'attributs. Les deux premiers incluent apporter le
déclarations pour ces systèmes. Le fichier, valeur-tracée.h apporte le nécessaire
déclarations pour tracer des données qui obéissent à la sémantique des valeurs.
En général, la sémantique des valeurs signifie simplement que vous pouvez passer l'objet, pas un
adresse. Pour utiliser la sémantique de valeur, vous devez avoir un objet avec un
constructeur de copie associé et opérateur d'affectation disponibles. Nous étendons les exigences
pour parler de l'ensemble d'opérateurs prédéfinis pour les types de données anciennes (POD).
Opérateur=, opérateur++, opérateur--, opérateur+, opérateur==, etc.
Tout cela signifie que vous serez en mesure de suivre les modifications apportées à un objet à l'aide de
ces opérateurs. :
classe MonObjet : Objet public
{
publique:
TypeId statique GetTypeId (vide)
{
statique TypeId tid = TypeId ("MonObjet")
.SetParent (Objet :: GetTypeId ())
.AddConstructor ()
.AddTraceSource ("MonEntier",
"Une valeur entière à tracer.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
retour tid;
}
MonObjet () {}
Valeur tracée m_monInt ;
};
Étant donné que le système de traçage est intégré aux attributs et que les attributs fonctionnent avec les objets,
il doit y avoir un ns-3 Exlcusion pour que la source de traces vive. Les deux lignes importantes de
code sont les .AddTraceSource et de la Valeur tracée déclaration.
La .AddTraceSource fournit les "hooks" utilisés pour connecter la source de trace au
monde extérieur. le Valeur tracée déclaration fournit l'infrastructure qui surcharge le
opérateurs mentionnés ci-dessus et pilote le processus de rappel :
annuler
IntTrace (Int ancienne valeur, Int nouvelle valeur)
{
std::cout << "Tracé" << oldValue << " à " << newValue << std::endl;
}
C'est la définition du puits de trace. Elle correspond directement à une fonction callback.
Cette fonction sera appelée à chaque fois qu'un des opérateurs du Valeur tracée is
réalisé.:
int
principal (int argc, char *argv[])
{
Ptr monObjet = CréerObjet ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
monObjet->m_myInt = 1234 ;
}
Dans cet extrait, la première chose à faire est de créer l'objet dans lequel
la trace source vit.
La prochaine étape, la TraceConnexionSansContexte, forme le lien entre la trace
source et le puits de trace. Remarquez le FaireRappel fonction de modèle. Rappel de la
section de rappel que cela crée le foncteur spécialisé chargé de fournir le
surchargé opérateur() utilisé pour "déclencher" le rappel. Les opérateurs surchargés (++, --, etc.)
va utiliser ça opérateur() pour invoquer réellement le rappel. le TraceConnexionSansContexte,
prend un paramètre de chaîne qui fournit le nom de l'attribut affecté à la trace
la source. Ignorons le peu de contexte pour l'instant car ce n'est pas encore important.
Enfin, la ligne :
monObjet->m_myInt = 1234 ;
doit être interprété comme une invocation opérateur= sur la variable membre m_myInt avec
l'entier 1234 passé en paramètre. Il s'avère que cet opérateur est défini (par
Valeur tracée) pour exécuter un rappel qui renvoie void et prend deux valeurs entières comme
paramètres -- une ancienne valeur et une nouvelle valeur pour l'entier en question. C'est exactement
la signature de fonction pour la fonction de rappel que nous avons fournie -- IntTrace.
Pour résumer, une source de trace est, par essence, une variable qui contient une liste de rappels. UNE
trace sink est une fonction utilisée comme cible d'un rappel. L'attribut et le type d'objet
les systèmes d'information sont utilisés pour fournir un moyen de connecter les sources de trace aux puits de trace. le
l'acte de "toucher" une source de trace exécute un opérateur sur la source de trace qui se déclenche
rappels. Il en résulte que les rappels de récepteur de trace enregistrent l'intérêt pour la source
étant appelé avec les paramètres fournis par la source.
En utilisant le Config Sous-système à Connectez à Tracer Sources
La TraceConnexionSansContexte l'appel montré ci-dessus dans l'exemple simple est en fait très
rarement utilisé dans le système. Plus typiquement, le Config sous-système est utilisé pour permettre la sélection
une source de trace dans le système en utilisant ce qu'on appelle un config chemin.
Par exemple, on peut trouver quelque chose qui ressemble à ce qui suit dans le système (pris
de exemples/tcp-large-transfer.cc):
annuler CwndTracer (uint32_t oldval, uint32_t newval) {}
Config::ConnectWithoutContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback (&CwndTracer));
Cela devrait vous sembler très familier. C'est la même chose que l'exemple précédent, sauf que
une fonction membre statique de classe Config est appelé au lieu d'une méthode sur Exlcusion;
et au lieu d'un Attribut nom, un chemin est fourni.
La première chose à faire est de lire le chemin à l'envers. Le dernier segment du chemin doit être
an Attribut d'un Exlcusion. En fait, si vous aviez un pointeur vers le Exlcusion qui a le
"Fenêtre Congestion" Attribut pratique (appelez-le L'object), vous pouvez écrire ceci comme le
exemple précédent :
annuler CwndTracer (uint32_t oldval, uint32_t newval) {}
theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
Il s'avère que le code de Config :: ConnectWithoutContext fait exactement cela. Cette
fonction prend un chemin qui représente une chaîne de Exlcusion pointeurs et les suit jusqu'à ce qu'il
arrive à la fin du chemin et interprète le dernier segment comme un Attribut le dernier
objet. Passons en revue ce qui se passe.
Le premier caractère "/" dans le chemin fait référence à ce que l'on appelle un espace de noms. Un de
espaces de noms prédéfinis dans le système de configuration est "NodeList" qui est une liste de tous les
nœuds dans la simulation. Les éléments de la liste sont référencés par des indices dans la liste, donc
"/NodeList/0" fait référence au nœud zéro dans la liste des nœuds créés par la simulation.
Ce nœud est en fait un Ptr et est donc une sous-classe d'un ns3::Objet.
Comme décrit dans la section Modèle d'objet, ns-3 prend en charge un modèle d'agrégation d'objets. le
le segment de chemin suivant commence par le caractère "$" qui indique un Getobject l'appel devrait être
faites la recherche du type qui suit. Lorsqu'un noeud est initialisé par un
InternetStackHelper un certain nombre d'interfaces sont agrégées au nœud. L'un d'eux est le
Protocole TCP de niveau quatre. Le type d'exécution de cet objet protocole est ns3::TcpL4Protocol''.
Lorsque le ``ObtenirObjet est exécuté, il retourne un pointeur sur l'objet de ce type.
La Protocole TcpL4 définit un attribut appelé "SocketList" qui est une liste de
prises. Chaque socket est en fait un ns3::Objet avec son propre Attributs. Les articles dans
la liste des sockets est référencée par index comme dans la NodeList, donc "SocketList/0"
fait référence au socket zéro dans la liste des sockets sur le nœud zéro dans la NodeList --
le premier nœud construit dans la simulation.
Cette douille, dont le type s'avère être une ns3 ::TcpSocketImpl définit un attribut
appelé "CongestionWindow" qui est un Valeur tracéeL’
Config :: ConnectWithoutContext fait maintenant un, :
objet->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
en utilisant le pointeur d'objet de "SocketList/0" qui fait le lien entre la trace
source définie dans le socket au rappel -- CwndTracerName.
Désormais, chaque fois qu'une modification est apportée au Valeur tracée représentant l'encombrement
fenêtre dans le socket TCP, le rappel enregistré sera exécuté et la fonction
CwndTracerName s'appellera imprimer les anciennes et les nouvelles valeurs de la congestion TCP
fenêtre.
En utilisant le Traçant API
Il existe trois niveaux d'interaction avec le système de traçage :
· L'utilisateur de début peut facilement commander quels objets participent au traçage ;
· Les utilisateurs intermédiaires peuvent étendre le système de traçage pour modifier le format de sortie généré
ou utiliser les sources de trace existantes de différentes manières, sans modifier le cœur du
simulateur;
· Les utilisateurs avancés peuvent modifier le noyau du simulateur pour ajouter de nouvelles sources et puits de traçage.
En utilisant Tracer assistants
La ns-3 les assistants de trace fournissent un environnement riche pour configurer et sélectionner différents
suivre les événements et les écrire dans des fichiers. Dans les sections précédentes, principalement "Construire
Topologies", nous avons vu plusieurs variétés de méthodes d'aide à la trace conçues pour être utilisées
à l'intérieur d'autres aides (de périphérique).
Peut-être vous souviendrez-vous avoir vu certaines de ces variations :
pointToPoint.EnablePcapAll ("seconde");
pointToPoint.EnablePcap ("deuxième", p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap ("troisième", csmaDevices.Get (0), vrai);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
Ce qui n'est peut-être pas évident, cependant, c'est qu'il existe un modèle cohérent pour tous les
méthodes liées à la trace trouvées dans le système. Nous allons maintenant prendre un peu de temps et jeter un œil
à la "grande image".
Il existe actuellement deux principaux cas d'utilisation des assistants de traçage dans ns-3: Assistants de périphérique
et aides au protocole. Les assistants de périphérique examinent le problème de la spécification des traces à
être activé via un nœud, une paire d'appareils. Par exemple, vous pouvez spécifier que pcap
le traçage doit être activé sur un périphérique particulier sur un nœud spécifique. Cela découle de la
ns-3 modèle conceptuel de l'appareil, ainsi que les modèles conceptuels des différents appareils
aides. Suite naturellement à cela, les fichiers créés suivent un
- - convention de nommage.
Les assistants de protocole examinent le problème de la spécification des traces à activer via
une paire de protocole et d'interface. Cela découle de la ns-3 modèle conceptuel de la pile de protocoles,
ainsi que les modèles conceptuels des aides de pile Internet. Naturellement, les fichiers de trace
doit suivre une - - convention de nommage.
Les assistants de trace s'inscrivent donc naturellement dans une taxonomie bidimensionnelle. Il y a
subtilités qui empêchent les quatre classes de se comporter de manière identique, mais nous nous efforçons de
faire en sorte qu'ils fonctionnent tous de la manière la plus similaire possible ; et chaque fois que possible, il existe des analogues pour
toutes les méthodes dans toutes les classes.
┌────────────────┬──────┬───────┐
│ │ pcap │ ascii │
├────────────────┼──────┼───────┤
│Aide de l'appareil │ │ │
├────────────────┼──────┼───────┤
│Aide au protocole │ │ │
└────────────────┴──────┴───────┘
Nous utilisons une approche appelée mixin pour ajouter une fonctionnalité de traçage à nos classes d'assistance. UNE
mixin est une classe qui fournit des fonctionnalités à qui est héritée par une sous-classe.
L'héritage d'un mixin n'est pas considéré comme une forme de spécialisation mais est vraiment un moyen de
collecter des fonctionnalités.
Jetons un coup d'œil rapide à ces quatre cas et à leurs mélanges.
Pcap Traçant Appareil assistants
L'objectif de ces assistants est de faciliter l'ajout d'une fonction de trace pcap cohérente à un
ns-3 appareil. Nous voulons que toutes les différentes versions de traçage pcap fonctionnent de la même manière
tous les périphériques, de sorte que les méthodes de ces assistants sont héritées par les assistants de périphérique. Regarde
at src/réseau/helper/trace-helper.h si vous voulez suivre la discussion tout en regardant
code réel.
La classe PcapHelperForDevice est une mixin fournit la fonctionnalité de haut niveau pour l'utilisation
pcap traçage dans un ns-3 appareil. Chaque appareil doit implémenter une seule méthode virtuelle
hérité de cette classe. :
vide virtuel EnablePcapInternal (std :: préfixe de chaîne, Ptr nd, bool promiscuité) = 0 ;
La signature de cette méthode reflète la vision centrée sur l'appareil de la situation à ce
niveau. Toutes les méthodes publiques héritées de la classe PcapUserHelperForDevice réduire à
appeler cette méthode de mise en œuvre dépendante de l'appareil unique. Par exemple, le niveau le plus bas
méthode pcap, :
void EnablePcap (std::string prefix, Ptr nd, bool promiscuous = false, bool explicitFilename = false);
appellera l'implémentation de l'appareil de ActiverPcapInterne directement. Tous les autres pcap publics
les méthodes de suivi s'appuient sur cette implémentation pour fournir des informations supplémentaires au niveau de l'utilisateur
Fonctionnalité. Cela signifie pour l'utilisateur que tous les assistants de périphérique du système
disposer de toutes les méthodes de trace pcap ; et ces méthodes fonctionneront toutes de la même manière
chemin à travers les appareils si l'appareil implémente ActiverPcapInterne correctement.
Pcap Traçant Appareil Auxiliaire Méthodologie
void EnablePcap (std::string prefix, Ptr sd,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (préfixe std::string, std::string ndName,
bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (préfixe std::string, NetDeviceContainer d,
bool promiscuité = faux);
void EnablePcap (préfixe std::string, NodeContainer n,
bool promiscuité = faux);
void EnablePcap (préfixe std::string, uint32_t nodeid, uint32_t deviceid,
bool promiscuité = faux);
void EnablePcapAll (préfixe std::string, bool promiscuous = false);
Dans chacune des méthodes présentées ci-dessus, il existe un paramètre par défaut appelé immoral qui
par défaut à faux. Ce paramètre indique que la trace ne doit pas être collectée dans
mode de promiscuité. Si vous souhaitez que vos traces incluent tout le trafic vu par l'appareil
(et si l'appareil prend en charge un mode de promiscuité), ajoutez simplement un vrai paramètre à l'un des
appels ci-dessus. Par example,:
Ptr sd ;
helper.EnablePcap ("prefix", nd, true);
activera les captures en mode promiscuité sur le NetDevice spécifié par nd.
Les deux premières méthodes incluent également un paramètre par défaut appelé expliciteNomFichier cela va
être discuté ci-dessous.
Vous êtes encouragé à parcourir le Doxygen pour la classe PcapHelperForDevice pour trouver les détails
de ces méthodes ; mais pour résumer...
Vous pouvez activer le traçage pcap sur une paire nœud/réseau-périphérique particulière en fournissant un
Ptr à un ActiverPcap méthode. le Ptr est implicite puisque le net device
doit appartenir à exactement un Nœud. Par example,:
Ptr sd ;
helper.EnablePcap ("préfixe", nd);
Vous pouvez activer le traçage pcap sur une paire nœud/réseau-périphérique particulière en fournissant un
std :: string représentant une chaîne de service de nom d'objet à un ActiverPcap méthode. le
Ptr est recherché à partir de la chaîne de nom. Encore une fois, le est implicite puisque le
le périphérique réseau nommé doit appartenir à exactement un Nœud. Par example,:
Names::Add ("serveur" ...);
Names::Add ("serveur/eth0" ...);
helper.EnablePcap ("préfixe", "serveur/ath0");
Vous pouvez activer le traçage pcap sur une collection de paires nœud/réseau-périphérique en fournissant un
NetDeviceContainer. Pour chaque NetDevice dans le conteneur, le type est vérifié. Pour chaque
périphérique du type approprié (le même type que celui géré par l'assistant de périphérique), le traçage est
activée. Encore une fois, le est implicite puisque le périphérique réseau trouvé doit appartenir exactement à
UN Nœud. Par example,:
NetDeviceContainer d = ...;
helper.EnablePcap ("préfixe", d);
Vous pouvez activer le traçage pcap sur une collection de paires nœud/réseau-périphérique en fournissant un
Conteneur de nœud. Pour chaque Nœud dans le Conteneur de nœud c'est attaché NetDevices sont itérés.
Pour chaque NetDevice attaché à chaque nœud dans le conteneur, le type de ce périphérique est
vérifié. Pour chaque appareil du type approprié (le même type que celui géré par l'appareil
helper), le traçage est activé. :
NodeContainer n ;
helper.EnablePcap ("préfixe", n);
Vous pouvez activer le traçage pcap sur la base de l'ID de nœud et de l'ID de périphérique ainsi qu'avec des
Ptr. Chaque Nœud dans le système a un ID de nœud entier et chaque appareil connecté à un nœud
a un ID de périphérique entier :
helper.EnablePcap ("préfixe", 21, 1);
Enfin, vous pouvez activer le traçage pcap pour tous les périphériques du système, avec le même type que
celle gérée par l'assistant de l'appareil. :
helper.EnablePcapAll ("préfixe");
Pcap Traçant Appareil Auxiliaire Nom de fichier Sélection
Implicite dans les descriptions de méthodes ci-dessus est la construction d'un nom de fichier complet par
la méthode de mise en œuvre. Par convention, pcap trace dans le ns-3 système sont de la forme
- identifiant>- identifiant>.pcap
Comme mentionné précédemment, chaque nœud du système aura un identifiant de nœud attribué par le système ; et
chaque périphérique aura un index d'interface (également appelé identifiant de périphérique) relatif à son nœud.
Par défaut, un fichier de trace pcap créé à la suite de l'activation de la trace sur le premier
périphérique du nœud 21 utilisant le préfixe "préfixe" serait préfixe-21-1.pcap.
Vous pouvez toujours utiliser le ns-3 service de nom d'objet pour rendre cela plus clair. Par exemple, si
vous utilisez le service de nom d'objet pour attribuer le nom "serveur" au nœud 21, le pcap résultant
le nom du fichier de trace deviendra automatiquement, préfixe-serveur-1.pcap et si vous affectez également le
nom "eth0" à l'appareil, votre nom de fichier pcap le détectera automatiquement et sera
appelé préfixe-serveur-eth0.pcap.
Enfin, deux des méthodes présentées ci-dessus :
void EnablePcap (std::string prefix, Ptr nd, bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (préfixe std::string, std::string ndName, bool promiscuous = false, bool explicitFilename = false);
avoir un paramètre par défaut appelé expliciteNomFichier. Lorsqu'il est défini sur vrai, ce paramètre
désactive le mécanisme de complétion automatique des noms de fichiers et vous permet de créer un
nom de fichier. Cette option n'est disponible que dans les méthodes qui activent le traçage pcap sur un
appareil unique.
Par exemple, afin de faire en sorte qu'un assistant de périphérique crée un seul pcap promiscuité
fichier de capture d'un nom spécifique (mon-fichier-pcap.pcap) sur un appareil donné, on pourrait :
Ptr sd ;
helper.EnablePcap ("mon-fichier-pcap.pcap", nd, vrai, vrai);
La première oui le paramètre active les traces en mode promiscuité et le second indique à l'assistant
interpréter le préfixe paramètre comme un nom de fichier complet.
ASCII Traçant Appareil assistants
Le comportement de l'assistant de trace ascii mixin est sensiblement similaire à la version pcap.
Jetez un oeil à src/réseau/helper/trace-helper.h si vous voulez suivre la discussion
tout en regardant du vrai code.
La classe AsciiTraceHelperForDevice ajoute la fonctionnalité de haut niveau pour l'utilisation d'ascii
suivi d'une classe d'assistance de périphérique. Comme dans le cas pcap, chaque appareil doit implémenter un
méthode virtuelle unique héritée de la trace ascii mixin.:
vide virtuel EnableAsciiInternal (Ptr flux, std :: préfixe de chaîne, Ptr nd) = 0 ;
La signature de cette méthode reflète la vision centrée sur l'appareil de la situation à ce
niveau; et aussi le fait que l'assistant peut écrire dans un flux de sortie partagé. Tous
les méthodes publiques liées à la trace ascii héritées de la classe AsciiTraceHelperForDevice
réduire à appeler cette seule méthode d'implémentation dépendante de l'appareil. Par exemple, le
méthodes de trace ascii de niveau le plus bas :
void EnableAscii (std::string prefix, Ptr nd);
annuler EnableAscii (Ptr flux, Ptr nd);
appellera l'implémentation de l'appareil de ActiverAsciiInterne directement, en fournissant soit un
préfixe ou flux valide. Toutes les autres méthodes de traçage ASCII publiques s'appuieront sur ces
fonctions de bas niveau pour fournir des fonctionnalités supplémentaires au niveau de l'utilisateur. Ce que cela signifie pour le
l'utilisateur est que tous les assistants de périphérique du système auront toutes les méthodes de trace ascii
disponible; et ces méthodes fonctionneront toutes de la même manière sur tous les appareils si les appareils
Mettre en oeuvre ActiverAsciiInterne correctement.
ASCII Traçant Appareil Auxiliaire Méthodologie
void EnableAscii (std::string prefix, Ptr nd);
annuler EnableAscii (Ptr flux, Ptr nd);
void EnableAscii (std::string préfixe, std::string ndName);
annuler EnableAscii (Ptr flux, std :: chaîne ndName);
void EnableAscii (préfixe std::string, NetDeviceContainer d);
annuler EnableAscii (Ptr flux, NetDeviceContainer d);
void EnableAscii (std::string préfixe, NodeContainer n);
annuler EnableAscii (Ptr flux, NodeContainer n);
void EnableAscii (préfixe std::string, uint32_t nodeid, uint32_t deviceid);
annuler EnableAscii (Ptr flux, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiAll (std :: préfixe de chaîne);
annuler EnableAsciiAll (Ptr flux);
Vous êtes encouragé à parcourir le Doxygen pour la classe TraceHelperForDevice pour trouver le
détails de ces méthodes; mais pour résumer...
Il existe deux fois plus de méthodes disponibles pour le traçage ascii que pour pcap
tracé. En effet, en plus du modèle de style pcap où les traces de chaque
une paire nœud/dispositif unique sont écrites dans un fichier unique, nous prenons en charge un modèle dans lequel la trace
les informations pour de nombreuses paires nœud/dispositif sont écrites dans un fichier commun. Cela signifie que le
- - mécanisme de génération de nom de fichier est remplacé par un mécanisme
se référer à un dossier commun ; et le nombre de méthodes API est doublé pour permettre à tous
combinaisons.
Tout comme dans le traçage pcap, vous pouvez activer le traçage ascii sur une paire nœud/net-device particulière
en fournissant un Ptr à un ActiverAscii méthode. le Ptr est implicite puisque
le périphérique réseau doit appartenir à exactement un Nœud. Par example,:
Ptr sd ;
helper.EnableAscii ("préfixe", nd);
Dans ce cas, aucun contexte de trace n'est écrit dans le fichier de trace ascii car ils seraient
redondant. Le système choisira le nom du fichier à créer en utilisant les mêmes règles que
décrit dans la section pcap, sauf que le fichier aura le suffixe ".tr" au lieu de
".pcap".
Si vous souhaitez activer le suivi ascii sur plusieurs périphériques réseau et que tous les suivis soient envoyés
à un seul fichier, vous pouvez également le faire en utilisant un objet pour faire référence à un seul fichier :
Ptr nd1 ;
Ptr nd2 ;
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAscii (flux, nd1);
helper.EnableAscii (flux, nd2);
Dans ce cas, les contextes de trace sont écrits dans le fichier de trace ascii puisqu'ils sont nécessaires
pour lever l'ambiguïté des traces des deux appareils. Notez que puisque l'utilisateur est complètement
en spécifiant le nom du fichier, la chaîne doit inclure le ".tr" pour plus de cohérence.
Vous pouvez activer le traçage ASCII sur une paire nœud/réseau-périphérique particulière en fournissant un
std :: string représentant une chaîne de service de nom d'objet à un ActiverPcap méthode. le
Ptr est recherché à partir de la chaîne de nom. Encore une fois, le est implicite puisque le
le périphérique réseau nommé doit appartenir à exactement un Nœud. Par example,:
Noms ::Ajouter ("client" ...);
Names::Add ("client/eth0" ...);
Names::Add ("serveur" ...);
Names::Add ("serveur/eth0" ...);
helper.EnableAscii ("préfixe", "client/eth0");
helper.EnableAscii ("préfixe", "serveur/eth0");
Cela donnerait deux fichiers nommés préfixe-client-eth0.tr et préfixe-serveur-eth0.tr avec
traces pour chaque périphérique dans le fichier de trace respectif. Puisque tous les EnableAscii
les fonctions sont surchargées pour prendre un wrapper de flux, vous pouvez également utiliser cette forme :
Noms ::Ajouter ("client" ...);
Names::Add ("client/eth0" ...);
Names::Add ("serveur" ...);
Names::Add ("serveur/eth0" ...);
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAscii (flux, "client/eth0");
helper.EnableAscii (flux, "serveur/eth0");
Cela se traduirait par un seul fichier de trace appelé nom-fichier-trace.tr qui contient tout
les événements de trace pour les deux appareils. Les événements seraient désambiguïsés par le contexte de trace
cordes.
Vous pouvez activer le traçage ASCII sur une collection de paires nœud/réseau-périphérique en fournissant un
NetDeviceContainer. Pour chaque NetDevice dans le conteneur, le type est vérifié. Pour chaque
périphérique du type approprié (le même type que celui géré par l'assistant de périphérique), le traçage est
activée. Encore une fois, le est implicite puisque le périphérique réseau trouvé doit appartenir exactement à
UN Nœud. Par example,:
NetDeviceContainer d = ...;
helper.EnableAscii ("préfixe", d);
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, chacun suivant
la - - convention .tr. Regroupant toutes les traces dans un
fichier unique est réalisé de la même manière que dans les exemples ci-dessus :
NetDeviceContainer d = ...;
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAscii (flux, d);
Vous pouvez activer le traçage ASCII sur une collection de paires nœud/réseau-périphérique en fournissant un
Conteneur de nœud. Pour chaque Nœud dans le Conteneur de nœud c'est attaché NetDevices sont itérés.
Pour chaque NetDevice attaché à chaque nœud dans le conteneur, le type de ce périphérique est
vérifié. Pour chaque appareil du type approprié (le même type que celui géré par l'appareil
helper), le traçage est activé. :
NodeContainer n ;
helper.EnableAscii ("préfixe", n);
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, chacun suivant
la - - convention .tr. Regroupant toutes les traces dans un
fichier unique est réalisé de la même manière que dans les exemples ci-dessus :
Vous pouvez activer le traçage pcap sur la base de l'ID de nœud et de l'ID de périphérique ainsi qu'avec des
Ptr. Chaque Nœud dans le système a un ID de nœud entier et chaque appareil connecté à un nœud
a un ID de périphérique entier :
helper.EnableAscii ("préfixe", 21, 1);
Bien sûr, les traces peuvent être combinées dans un seul fichier comme indiqué ci-dessus.
Enfin, vous pouvez activer le traçage pcap pour tous les périphériques du système, avec le même type que
celle gérée par l'assistant de l'appareil. :
helper.EnableAsciiAll ("préfixe");
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, un pour chaque périphérique dans
le système du type géré par l'assistant. Tous ces fichiers suivront la
- - convention .tr. Regroupant toutes les traces en une seule
fichier est accompli de la même manière que dans les exemples ci-dessus.
ASCII Traçant Appareil Auxiliaire Nom de fichier Sélection
Implicite dans les descriptions de méthodes de style préfixe ci-dessus est la construction du
noms de fichiers par la méthode d'implémentation. Par convention, les traces ascii dans le ns-3 système sont
de la forme - identifiant>- identifiant>.tr.
Comme mentionné précédemment, chaque nœud du système aura un identifiant de nœud attribué par le système ; et
chaque périphérique aura un index d'interface (également appelé identifiant de périphérique) relatif à son nœud.
Par défaut, un fichier de trace ascii créé à la suite de l'activation de la trace sur le premier
périphérique du nœud 21, utilisant le préfixe "préfixe", serait préfixe-21-1.tr.
Vous pouvez toujours utiliser le ns-3 service de nom d'objet pour rendre cela plus clair. Par exemple, si
vous utilisez le service de nom d'objet pour attribuer le nom "serveur" au nœud 21, le résultat
le nom du fichier de trace ascii deviendra automatiquement, préfixe-serveur-1.tr et si vous attribuez également
le nom "eth0" à l'appareil, votre nom de fichier de trace ascii le détectera automatiquement
et être appelé préfixe-serveur-eth0.tr.
Pcap Traçant Passerelle assistants
Le but de ces mélanges est de faciliter l'ajout d'une fonction de trace pcap cohérente à
protocoles. Nous voulons que toutes les différentes saveurs de traçage pcap fonctionnent de la même manière sur tous
protocoles, de sorte que les méthodes de ces assistants sont héritées par les assistants de pile. Jeter un coup d'œil à
src/réseau/helper/trace-helper.h si vous voulez suivre la discussion tout en regardant
code réel.
Dans cette section, nous allons illustrer les méthodes appliquées au protocole Ipv4. Pour
spécifiez des traces dans des protocoles similaires, remplacez simplement le type approprié. Par example,
utiliser un Ptr au lieu d'une Ptr et appelle ActiverPcapIpv6 au lieu de ActiverPcapIpv4.
La classe PcapHelperForIpv4 fournit la fonctionnalité de haut niveau pour l'utilisation du traçage pcap
dans le Ipv4 protocole. Chaque assistant de protocole permettant ces méthodes doit implémenter un seul
méthode virtuelle héritée de cette classe. Il y aura une implémentation séparée pour
Ipv6, par exemple, mais la seule différence sera dans les noms de méthode et les signatures.
Différents noms de méthode sont nécessaires pour désambiguïser la classe Ipv4 de Ipv6 qui sont les deux
dérivé de la classe Exlcusion, et les méthodes qui partagent la même signature :
vide virtuel EnablePcapIpv4Internal (std :: préfixe de chaîne, Ptr ipv4, interface uint4_t) = 32 ;
La signature de cette méthode reflète la vision centrée sur le protocole et l'interface du
situation à ce niveau. Toutes les méthodes publiques héritées de la classe PcapHelperForIpv4
réduire à appeler cette seule méthode de mise en œuvre dépendante de l'appareil. Par exemple, le
méthode pcap du niveau le plus bas :
annuler EnablePcapIpv4 (std :: préfixe de chaîne, Ptr ipv4, interface uint4_t);
appellera l'implémentation de l'appareil de ActiverPcapIpv4Internal directement. Tous les autres publics
Les méthodes de suivi pcap s'appuient sur cette implémentation pour fournir des informations supplémentaires au niveau de l'utilisateur.
Fonctionnalité. Cela signifie pour l'utilisateur que tous les assistants de protocole du système
disposer de toutes les méthodes de trace pcap ; et ces méthodes fonctionneront toutes de la même manière
chemin à travers les protocoles si l'assistant implémente ActiverPcapIpv4Internal correctement.
Pcap Traçant Passerelle Auxiliaire Méthodologie
Ces méthodes sont conçues pour être en correspondance biunivoque avec les Nœud- Et
NetDevice- versions centrées des versions de l'appareil. À la place de Nœud et NetDevice paire
contraintes, nous utilisons des contraintes de protocole et d'interface.
Notez que tout comme dans la version de l'appareil, il existe six méthodes :
annuler EnablePcapIpv4 (std :: préfixe de chaîne, Ptr ipv4, interface uint4_t);
annuler EnablePcapIpv4 (std :: string préfixe, std :: string ipv4Name, interface uint32_t);
void EnablePcapIpv4 (std :: préfixe de chaîne, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std :: préfixe de chaîne, NodeContainer n);
void EnablePcapIpv4 (préfixe std::string, uint32_t nodeid, interface uint32_t) ;
annuler EnablePcapIpv4All (std::string préfixe);
Vous êtes encouragé à parcourir le Doxygen pour la classe PcapHelperForIpv4 pour trouver les détails
de ces méthodes ; mais pour résumer...
Vous pouvez activer le traçage pcap sur une paire protocole/interface particulière en fournissant un
Ptr et interface à un ActiverPcap méthode. Par example,:
Ptr ipv4 = nœud->GetObject ();
helper.EnablePcapIpv4 ("préfixe", ipv4, 0);
Vous pouvez activer le traçage pcap sur une paire nœud/réseau-périphérique particulière en fournissant un
std :: string représentant une chaîne de service de nom d'objet à un ActiverPcap méthode. le
Ptr est recherché à partir de la chaîne de nom. Par example,:
Names::Add ("serverIPv4" ...);
helper.EnablePcapIpv4 ("préfixe", "serverIpv4", 1);
Vous pouvez activer le traçage pcap sur une collection de paires protocole/interface en fournissant un
Conteneur d'interface IPv4. Pour chaque Ipv4 / paire d'interface dans le conteneur le type de protocole
est vérifié. Pour chaque protocole du bon type (le même type que celui géré par le
device helper), le traçage est activé pour l'interface correspondante. Par example,:
nœuds NodeContainer ;
Périphériques NetDeviceContainer = deviceHelper.Install (nœuds);
Ipv4AddressHelper ipv4 ;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
interfaces Ipv4InterfaceContainer = ipv4.Assign (périphériques);
helper.EnablePcapIpv4 ("préfixe", interfaces);
Vous pouvez activer le traçage pcap sur une collection de paires protocole/interface en fournissant un
Conteneur de nœud. Pour chaque Nœud dans le Conteneur de nœud le protocole approprié est trouvé. Pour
chaque protocole, ses interfaces sont énumérées et le traçage est activé sur le résultat
paires. Par example,:
NodeContainer n ;
helper.EnablePcapIpv4 ("préfixe", n);
Vous pouvez également activer le traçage pcap sur la base de l'ID de nœud et de l'interface. Dans ce cas,
le node-id est traduit en un Ptr et le protocole approprié est recherché dans le
nœud. Le protocole et l'interface résultants sont utilisés pour spécifier la trace résultante
la source.:
helper.EnablePcapIpv4 ("préfixe", 21, 1);
Enfin, vous pouvez activer le traçage pcap pour toutes les interfaces du système, avec les
protocole étant du même type que celui géré par le device helper :
helper.EnablePcapIpv4All ("préfixe");
Pcap Traçant Passerelle Auxiliaire Nom de fichier Sélection
Implicite dans toutes les descriptions de méthodes ci-dessus est la construction du
noms de fichiers par la méthode d'implémentation. Par convention, les traces pcap prises pour les appareils dans
le ns-3 système sont de la forme - identifiant>- identifiant>.pcap. Dans le cas de
traces de protocole, il existe une correspondance un à un entre les protocoles et Nodes. C'est
parce que le protocole Objets sont agrégés à Nœud Objets. Puisqu'il n'y a pas de protocole global
id dans le système, nous utilisons l'identifiant de nœud correspondant dans la dénomination du fichier. Il existe donc un
possibilité de collisions de noms de fichiers dans les noms de fichiers de trace automatiquement choisis. Pour ça
raison, la convention de nom de fichier est modifiée pour les traces de protocole.
Comme mentionné précédemment, chaque nœud du système aura un identifiant de nœud attribué par le système.
Puisqu'il existe une correspondance un à un entre les instances de protocole et les instances de nœud
nous utilisons l'identifiant du nœud. Chaque interface a un identifiant d'interface relatif à son protocole. Nous utilisons
la Convention " -n -je .pcap" pour nommer le fichier de trace dans
aides au protocole.
Par conséquent, par défaut, un fichier de trace pcap créé à la suite de l'activation du traçage sur
l'interface 1 du protocole Ipv4 du noeud 21 utilisant le préfixe "prefix" serait
"préfixe-n21-i1.pcap".
Vous pouvez toujours utiliser le ns-3 service de nom d'objet pour rendre cela plus clair. Par exemple, si
vous utilisez le service de nom d'objet pour attribuer le nom "serverIpv4" au Ptr sur le nœud
21, le nom du fichier de trace pcap résultant deviendra automatiquement,
"préfixe-nserverIpv4-i1.pcap".
ASCII Traçant Passerelle assistants
Le comportement des assistants de trace ascii est sensiblement similaire au cas pcap. Prenez un
regarder src/réseau/helper/trace-helper.h si vous voulez suivre la discussion pendant
regarder du vrai code.
Dans cette section, nous allons illustrer les méthodes appliquées au protocole Ipv4. Pour
spécifiez des traces dans des protocoles similaires, remplacez simplement le type approprié. Par example,
utiliser un Ptr au lieu d'une Ptr et appelle ActiverAsciiIpv6 au lieu de
ActiverAsciiIpv4.
La classe AsciitraceHelperForIpv4 ajoute la fonctionnalité de haut niveau pour l'utilisation d'ascii
traçage vers un assistant de protocole. Chaque protocole qui permet ces méthodes doit implémenter un
méthode virtuelle unique héritée de cette classe. :
vide virtuel EnableAsciiIpv4Internal (Ptr flux, std :: préfixe de chaîne,
Ptr ipv4, interface uint4_t) = 32 ;
La signature de cette méthode reflète la vision centrée sur le protocole et l'interface de la
situation à ce niveau ; et aussi le fait que l'assistant peut écrire à un partage
flux de sortie. Toutes les méthodes publiques héritées de la classe
PcapAndAsciiTraceHelperForIpv4 réduire à appeler ce seul appareil dépendant
méthode de mise en œuvre. Par exemple, les méthodes de trace ascii de niveau le plus bas :
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interface uint4_t);
annuler EnableAsciiIpv4 (Ptr flux, Ptr ipv4, interface uint4_t);
appellera l'implémentation de l'appareil de ActiverAsciiIpv4Interne directement, en fournissant soit
le préfixe ou le flux. Toutes les autres méthodes de traçage ASCII publiques s'appuieront sur ces
fonctions de bas niveau pour fournir des fonctionnalités supplémentaires au niveau de l'utilisateur. Ce que cela signifie pour le
l'utilisateur est que tous les assistants de périphérique du système auront toutes les méthodes de trace ascii
disponible; et ces méthodes fonctionneront toutes de la même manière à travers les protocoles si le
les protocoles implémentent ActiverAsciiIpv4Interne correctement.
ASCII Traçant Appareil Auxiliaire Méthodologie
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interface uint4_t);
annuler EnableAsciiIpv4 (Ptr flux, Ptr ipv4, interface uint4_t);
void EnableAsciiIpv4 (préfixe std::string, std::string ipv4Name, interface uint32_t);
annuler EnableAsciiIpv4 (Ptr flux, std :: string ipv4Name, interface uint32_t);
void EnableAsciiIpv4 (std :: préfixe de chaîne, Ipv4InterfaceContainer c);
annuler EnableAsciiIpv4 (Ptr flux, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (std :: préfixe de chaîne, NodeContainer n);
annuler EnableAsciiIpv4 (Ptr flux, NodeContainer n);
void EnableAsciiIpv4 (std :: préfixe de chaîne, uint32_t nodeid, uint32_t deviceid) ;
annuler EnableAsciiIpv4 (Ptr flux, uint32_t nodeid, interface uint32_t);
annuler EnableAsciiIpv4All (std::string préfixe);
annuler EnableAsciiIpv4All (Ptr flux);
Vous êtes encouragé à parcourir le Doxygen pour la classe PcapAndAsciiHelperForIpv4 pour trouver le
détails de ces méthodes; mais pour résumer...
Il existe deux fois plus de méthodes disponibles pour le traçage ascii que pour pcap
tracé. En effet, en plus du modèle de style pcap où les traces de chaque
paire protocole/interface unique sont écrites dans un fichier unique, nous prenons en charge un modèle dans lequel
les informations de trace pour de nombreuses paires protocole/interface sont écrites dans un fichier commun. Cette
signifie que le -n - le mécanisme de génération de nom de fichier est remplacé
par un mécanisme de référence à un fichier commun ; et le nombre de méthodes API est doublé pour
permettre toutes les combinaisons.
Tout comme dans le traçage pcap, vous pouvez activer le traçage ascii sur un protocole/interface particulier
paire en fournissant un Ptr et le interface à un ActiverAscii méthode. Par example,:
Ptr ipv4 ;
helper.EnableAsciiIpv4 ("préfixe", ipv4, 1);
Dans ce cas, aucun contexte de trace n'est écrit dans le fichier de trace ascii car ils seraient
redondant. Le système choisira le nom du fichier à créer en utilisant les mêmes règles que
décrit dans la section pcap, sauf que le fichier aura le suffixe ".tr" au lieu de
".pcap".
Si vous souhaitez activer le traçage ascii sur plusieurs interfaces et que toutes les traces soient envoyées à
un seul fichier, vous pouvez également le faire en utilisant un objet pour faire référence à un seul fichier. Nous
ont déjà quelque chose de similaire à ceci dans l'exemple "cwnd" ci-dessus :
Ptr protocol4 = node1->GetObject ();
Ptr protocol4 = node2->GetObject ();
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAsciiIpv4 (flux, protocole1, 1);
helper.EnableAsciiIpv4 (flux, protocole2, 1);
Dans ce cas, les contextes de trace sont écrits dans le fichier de trace ascii puisqu'ils sont nécessaires
pour désambiguïser les traces des deux interfaces. Notez que puisque l'utilisateur est complètement
en spécifiant le nom du fichier, la chaîne doit inclure le ".tr" pour plus de cohérence.
Vous pouvez activer le traçage ascii sur un protocole particulier en fournissant un std :: string
représentant une chaîne de service de nom d'objet à un ActiverPcap méthode. le Ptr is
recherché à partir de la chaîne de nom. le dans les noms de fichiers résultants est implicite puisque
il existe une correspondance un à un entre les instances de protocole et les nœuds, par exemple :
Noms ::Ajouter ("node1Ipv4" ...);
Noms ::Ajouter ("node2Ipv4" ...);
helper.EnableAsciiIpv4 ("préfixe", "node1Ipv4", 1);
helper.EnableAsciiIpv4 ("préfixe", "node2Ipv4", 1);
Cela se traduirait par deux fichiers nommés "prefix-nnode1Ipv4-i1.tr" et
"prefix-nnode2Ipv4-i1.tr" avec des traces pour chaque interface dans le fichier de trace respectif.
Étant donné que toutes les fonctions EnableAscii sont surchargées pour prendre un wrapper de flux, vous pouvez
utilisez également ce formulaire :
Noms ::Ajouter ("node1Ipv4" ...);
Noms ::Ajouter ("node2Ipv4" ...);
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAsciiIpv4 (flux, "node1Ipv4", 1);
helper.EnableAsciiIpv4 (flux, "node2Ipv4", 1);
Cela se traduirait par un seul fichier de trace appelé "trace-file-name.tr" qui contient tous les
les événements de trace pour les deux interfaces. Les événements seraient désambiguïsés par le contexte de trace
cordes.
Vous pouvez activer le traçage ascii sur une collection de paires protocole/interface en fournissant un
Conteneur d'interface IPv4. Pour chaque protocole du type approprié (le même type que celui qui est géré
par l'assistant de périphérique), le traçage est activé pour l'interface correspondante. Encore une fois, le
est implicite puisqu'il existe une correspondance un à un entre chaque protocole et
son nœud. Par example,:
nœuds NodeContainer ;
Périphériques NetDeviceContainer = deviceHelper.Install (nœuds);
Ipv4AddressHelper ipv4 ;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
interfaces Ipv4InterfaceContainer = ipv4.Assign (périphériques);
helper.EnableAsciiIpv4 ("préfixe", interfaces);
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, chacun suivant
la -n -je convention .tr. Regroupant toutes les traces dans un
fichier unique est réalisé de la même manière que dans les exemples ci-dessus :
nœuds NodeContainer ;
Périphériques NetDeviceContainer = deviceHelper.Install (nœuds);
Ipv4AddressHelper ipv4 ;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
interfaces Ipv4InterfaceContainer = ipv4.Assign (périphériques);
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
helper.EnableAsciiIpv4 (flux, interfaces) ;
Vous pouvez activer le traçage ascii sur une collection de paires protocole/interface en fournissant un
Conteneur de nœud. Pour chaque Nœud dans le Conteneur de nœud le protocole approprié est trouvé. Pour
chaque protocole, ses interfaces sont énumérées et le traçage est activé sur le résultat
paires. Par example,:
NodeContainer n ;
helper.EnableAsciiIpv4 ("préfixe", n);
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, chacun suivant
la - - convention .tr. Regroupant toutes les traces dans un
fichier unique est réalisé de la même manière que dans les exemples ci-dessus :
Vous pouvez également activer le traçage pcap sur la base de l'ID de nœud et de l'ID de périphérique. Dans ce cas,
le node-id est traduit en un Ptr et le protocole approprié est recherché dans le
nœud. Le protocole et l'interface résultants sont utilisés pour spécifier la trace résultante
la source.:
helper.EnableAsciiIpv4 ("préfixe", 21, 1);
Bien sûr, les traces peuvent être combinées dans un seul fichier comme indiqué ci-dessus.
Enfin, vous pouvez activer le traçage ascii pour toutes les interfaces du système, avec les
protocole étant du même type que celui géré par le device helper :
helper.EnableAsciiIpv4All ("préfixe");
Cela entraînerait la création d'un certain nombre de fichiers de trace ascii, un pour chaque interface
dans le système lié à un protocole du type géré par l'assistant. Tous ces fichiers
suivra la -n -je
dans un seul fichier est accompli de la même manière que dans les exemples ci-dessus.
ASCII Traçant Appareil Auxiliaire Nom de fichier Sélection
Implicite dans les descriptions de méthodes de style préfixe ci-dessus est la construction du
noms de fichiers par la méthode d'implémentation. Par convention, les traces ascii dans le ns-3 système sont
de la forme " - - .tr."
Comme mentionné précédemment, chaque nœud du système aura un identifiant de nœud attribué par le système.
Puisqu'il existe une correspondance un à un entre les protocoles et les nœuds, nous utilisons node-id
pour identifier l'identité du protocole. Chaque interface sur un protocole donné aura un
index d'interface (également appelé simplement une interface) relatif à son protocole. Par défaut,
puis, un fichier de trace ascii créé à la suite de l'activation du traçage sur le premier appareil de
le nœud 21, utilisant le préfixe "prefix", serait "prefix-n21-i1.tr". Utilisez le préfixe pour
lever l'ambiguïté de plusieurs protocoles par nœud.
Vous pouvez toujours utiliser le ns-3 service de nom d'objet pour rendre cela plus clair. Par exemple, si
vous utilisez le service de nom d'objet pour attribuer le nom "serverIpv4" au protocole sur le nœud
21, et spécifiez également l'interface un, le nom du fichier de trace ascii résultant sera automatiquement
devenir "prefix-nserverIpv4-1.tr".
Traçant la mise en oeuvre détails
Sauvegarde de Collection
Ce chapitre décrit le cadre de collecte de données ns-3 (DCF), qui fournit
capacités pour obtenir des données générées par des modèles dans le simulateur, pour effectuer en ligne
la réduction et le traitement des données, et de rassembler les données brutes ou transformées en diverses sorties
formats.
Le framework prend actuellement en charge les exécutions ns-3 autonomes qui ne reposent sur aucun
contrôle de l'exécution du programme. Les objets fournis par le DCF peuvent être accrochés à ns-3 tracer
sources pour permettre le traitement des données.
Le code source des classes réside dans le répertoire source/statistiques.
Ce chapitre est organisé comme suit. Tout d'abord, un aperçu de l'architecture est
présenté. Ensuite, les aides pour ces classes sont présentées ; ce premier traitement
devrait permettre une utilisation de base du cadre de collecte de données pour de nombreux cas d'utilisation. Les utilisateurs qui
souhaitent produire une sortie en dehors de la portée des assistants actuels, ou qui souhaitent créer
leurs propres objets de collecte de données, doivent lire le reste du chapitre, qui va
en détail sur tous les types d'objets DCF de base et fournit un codage de bas niveau
exemples.
Design
Le DCF se compose de trois classes de base :
· ASAP est un mécanisme pour instrumenter et contrôler la sortie des données de simulation qui est
utilisé pour surveiller les événements intéressants. Il produit une sortie sous la forme d'un ou plusieurs ns-3
sources de traces. Les objets sonde sont reliés à une ou plusieurs traces (Appelée
Collecteurs), qui traitent les échantillons en ligne et les préparent pour la sortie.
· Collector consomme les données générées par un ou plusieurs objets Probe. Il effectue
transformations sur les données, telles que la normalisation, la réduction et le calcul de
statistiques de base. Les objets collecteurs ne produisent pas de données qui sont directement sorties par le
course ns-3 ; au lieu de cela, ils sortent des données en aval vers un autre type d'objet, appelé
Aggregator, qui remplit cette fonction. En règle générale, les collecteurs sortent leurs données dans
également sous forme de sources traces, permettant de chaîner les collecteurs en série.
· Aggregator est le point final des données collectées par un réseau de Sondes et de Collecteurs.
La principale responsabilité de l'agrégateur est de rassembler les données et leurs correspondances
métadonnées, dans différents formats de sortie tels que des fichiers de texte brut, des fichiers de tableur ou
bases de données.
Ces trois classes offrent la possibilité de s'activer ou de se désactiver dynamiquement
tout au long d'une simulation.
Tout autonome ns-3 l'exécution de la simulation qui utilise le DCF créera généralement au moins un
instance de chacune des trois classes ci-dessus.
[image] Présentation du cadre de collecte de données.UNINDENT
Le flux global de traitement des données est décrit dans Sauvegarde de Collection Cadre vue d'ensemble.
Sur le côté gauche, une course ns-3 la simulation est illustrée. Au cours de l'exécution du
simulation, les données sont rendues disponibles par des modèles via des sources de trace ou par d'autres moyens.
Le schéma montre que des sondes peuvent être connectées à ces sources de trace pour recevoir des données
de manière asynchrone, ou les sondes peuvent interroger les données. Les données sont ensuite transmises à un objet collecteur
qui transforme les données. Enfin, un agrégateur peut être connecté aux sorties du
collector, pour générer des tracés, des fichiers ou des bases de données.
[image] Agrégation du cadre de collecte de données.UNINDENT
Une variante de la figure ci-dessus est fournie dans Sauvegarde de Collection Cadre agrégation.
Cette deuxième figure illustre que les objets DCF peuvent être chaînés de manière
que les objets en aval reçoivent des entrées de plusieurs objets en amont. La figure
montre conceptuellement que plusieurs sondes peuvent générer une sortie qui est introduite dans un seul
collectionneur; par exemple, un collecteur qui produit un rapport de deux compteurs serait
acquièrent généralement chaque donnée de compteur à partir de sondes distinctes. Plusieurs collecteurs peuvent également
alimenter un seul agrégateur, qui (comme son nom l'indique) peut collecter un certain nombre de données
flux à inclure dans un tracé, un fichier ou une base de données unique.
Sauvegarde de Collection assistants
La pleine flexibilité du cadre de collecte des données est assurée par l'interconnexion
de sondes, de collecteurs et d'agrégateurs. La réalisation de toutes ces interconnexions conduit à
de nombreuses instructions de configuration dans les programmes utilisateur. Pour faciliter l'utilisation, certains des plus courants
les opérations peuvent être combinées et encapsulées dans des fonctions d'assistance. De plus, certains
déclarations impliquant ns-3 les sources de trace n'ont pas de liaisons Python, en raison de limitations dans
les reliures.
Sauvegarde de Collection assistants Aperçu
Dans cette section, nous donnons un aperçu de certaines classes d'assistance qui ont été créées pour
faciliter la configuration du cadre de collecte de données pour certains cas d'utilisation courants. le
les aides permettent aux utilisateurs de former des opérations courantes avec seulement quelques instructions dans leur C++ ou
Programmes Python. Mais, cette facilité d'utilisation se fait au prix de beaucoup moins
flexibilité que la configuration de bas niveau peut fournir, et la nécessité de coder explicitement
prise en charge de nouveaux types de sonde dans les assistants (pour contourner un problème décrit ci-dessous).
L'accent mis sur les assistants actuels est de rassembler les données hors de ns-3 tracer les sources dans
graphiques gnuplot ou fichiers texte, sans un degré élevé de personnalisation de sortie ou de statistiques
traitement (initialement). De plus, l'utilisation est limitée aux types de sondes disponibles dans
ns-3. Les sections ultérieures de cette documentation entreront plus en détail sur la création de nouveaux
Types de sondes, ainsi que des détails sur la connexion des sondes, des collecteurs et des agrégateurs
dans des arrangements personnalisés.
À ce jour, deux aides à la collecte de données ont été mises en place :
· GnuplotHelper
· Aide-fichier
GnuplotHelperGenericName
Le GnuplotHelper est une classe d'assistance pour produire des fichiers de sortie utilisés pour créer des gnuplots. le
l'objectif global est de permettre aux utilisateurs de créer rapidement des tracés à partir des données exportées
in ns-3 sources de traces. Par défaut, une quantité minimale de transformation de données est effectuée ;
l'objectif est de générer des tracés avec aussi peu d'instructions de configuration (par défaut) que
de qualité.
GnuplotHelperGenericName Aperçu
Le GnuplotHelper créera 3 fichiers différents à la fin de la simulation :
· Un fichier de données gnuplot séparé par des espaces
· Un fichier de contrôle gnuplot
· Un script shell pour générer le gnuplot
Deux instructions de configuration sont nécessaires pour produire des tracés. La première
configure le tracé (nom de fichier, titre, légendes et type de sortie, où la sortie
le type par défaut est PNG s'il n'est pas spécifié) :
void ConfigurePlot (const std ::string &outputFileNameWithoutExtension,
const std ::chaîne &titre,
const std ::chaîne &xLégende,
const std::string &yLégende,
const std ::string &terminalType = ".png");
La deuxième déclaration accroche la source de trace d'intérêt :
void PlotProbe (const std::string &typeId,
const std :: chaîne & chemin,
const std::chaîne &probeTraceSource,
const std ::chaîne &titre);
Les arguments sont les suivants :
· typeId : Le ns-3 TypeId de la sonde
· path : Le chemin dans le ns-3 espace de noms de configuration à une ou plusieurs sources de trace
· probeTraceSource : quelle sortie de la sonde (elle-même une source de trace) doit être tracée
· title : Le titre à associer au(x) jeu(x) de données (dans la légende de gnuplot)
Une variante du PlotProbe ci-dessus consiste à spécifier un cinquième argument facultatif qui contrôle
où dans l'intrigue la clé (légende) est placée.
Un exemple entièrement travaillé (de septième.cc) est illustré ci-dessous :
// Crée l'assistant gnuplot.
GnuplotHelper plotHelper ;
// Configurez le tracé.
// Configurez le tracé. Le premier argument est le préfixe du nom de fichier
// pour les fichiers de sortie générés. Les deuxième, troisième et quatrième
// les arguments sont, respectivement, le titre du graphique, les étiquettes de l'axe des x et de l'axe des y
plotHelper.ConfigurePlot ("nombre d'octets du septième paquet",
"Nombre d'octets de paquets par rapport au temps",
"Temps (secondes)",
"Compte d'octets de paquets",
"png");
// Spécifiez le type de sonde, le chemin de la source de trace (dans l'espace de noms de configuration) et
// sonde la source de trace de sortie ("OutputBytes") à tracer. Le quatrième argument
// spécifie le nom de l'étiquette de la série de données sur le tracé. Le dernier
// l'argument formate le tracé en spécifiant où la clé doit être placée.
plotHelper.PlotProbe (probeType,
tracePath,
"Octets de sortie",
"Compte d'octets de paquets",
GnuplotAggregator ::KEY_BELOW);
Dans cet exemple, le type de sonde et tracePath sont les suivants (pour IPv4) :
probeType = "ns3::Ipv4PacketProbe" ;
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx" ;
Le probeType est un paramètre clé pour que cet assistant fonctionne. Ce TypeId doit être enregistré
dans le système, et la signature sur le puits de trace de la sonde doit correspondre à celle de la trace
source à laquelle il est accroché. Les types de sonde sont prédéfinis pour un certain nombre de types de données
correspondant à ns-3 valeurs tracées, et pour quelques autres signatures de source de trace telles que
la source de trace 'Tx' de ns3 :: Protocole Ipv4L3 classe.
Notez que le chemin source de la trace spécifié peut contenir des caractères génériques. Dans ce cas, plusieurs
les ensembles de données sont tracés sur une parcelle ; un pour chaque chemin correspondant.
La sortie principale produite sera trois fichiers :
septième-paquet-octet-count.dat
septième-paquet-octet-count.plt
nombre-d'octets-de-septième-paquet.sh
À ce stade, les utilisateurs peuvent soit modifier manuellement le fichier .plt pour d'autres personnalisations, soit
exécutez-le simplement via gnuplot. Fonctionnement sh nombre-d'octets-de-septième-paquet.sh exécute simplement l'intrigue
via gnuplot, comme indiqué ci-dessous.
[image] Gnuplot 2-D Créé par seventh.cc Example..UNINDENT
On peut voir que les éléments clés (légende, titre, emplacement de la légende, xlabel, ylabel,
et le chemin des données) sont tous placés sur le tracé. Puisqu'il y avait deux matchs à la
chemin de configuration fourni, les deux séries de données sont affichées :
· Packet Byte Count-0 correspond à /NodeList/0/$ns3::Ipv4L3Protocol/Tx
· Packet Byte Count-1 correspond à /NodeList/1/$ns3::Ipv4L3Protocol/Tx
GnuplotHelperGenericName ConfigurePlot
Le GnuplotHelper ConfigurePlot() La fonction peut être utilisée pour configurer les tracés.
Il a le prototype suivant :
void ConfigurePlot (const std ::string &outputFileNameWithoutExtension,
const std ::chaîne &titre,
const std ::chaîne &xLégende,
const std::string &yLégende,
const std ::string &terminalType = ".png");
Il a les arguments suivants :
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┐
│Argument │ Description │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nom des fichiers liés à gnuplot à │
│ │ écrire sans extension. │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│title │ Chaîne de titre du tracé à utiliser pour │
│ │ cette parcelle. │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│xLegend │ La légende de l'horizontale x │
│ │ axe. │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│yLegend │ La légende de la verticale y │
│ │ axe. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┘
│terminalType │ Chaîne de réglage du type de borne pour │
│ │ sortie. Le terminal par défaut │
│ │ type est "png". │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┘
Le GnuplotHelper ConfigurePlot() la fonction configure les paramètres liés au tracé pour cette
gnuplot helper afin qu'il crée un fichier de données gnuplot séparé par des espaces nommé
outputFileNameWithoutExtension + ".dat", un fichier de contrôle gnuplot nommé
outputFileNameWithoutExtension + ".plt", et un script shell pour générer le gnuplot nommé
sortieNomFichierSansExtension + ".sh".
Vous trouverez un exemple d'utilisation de cette fonction dans le septième.cc code décrit ci-dessus
où il a été utilisé comme suit :
plotHelper.ConfigurePlot ("nombre d'octets du septième paquet",
"Nombre d'octets de paquets par rapport au temps",
"Temps (secondes)",
"Compte d'octets de paquets",
"png");
GnuplotHelperGenericName PlotSonde
Le GnuplotHelper TracerSonde() La fonction peut être utilisée pour tracer les valeurs générées par les sondes.
Il a le prototype suivant :
void PlotProbe (const std::string &typeId,
const std :: chaîne & chemin,
const std::chaîne &probeTraceSource,
const std ::chaîne &titre,
enum GnuplotAggregator :: KeyLocation keyLocation = GnuplotAggregator :: KEY_INSIDE);
Il a les arguments suivants :
?? ??
│Argument │ Description │
?? ??
│typeId │ L'ID de type pour la sonde │
│ │ créé par cet assistant. │
?? ??
│path │ Config path pour accéder à la trace │
│ │ origine. │
?? ??
│probeTraceSource │ La source de trace de la sonde vers │
│ │ accès. │
?? ??
│title │ Le titre à associer à │
│ │ cet ensemble de données │
?? ??
│keyLocation │ L'emplacement de la clé dans le │
│ │ parcelle. L'emplacement par défaut est │
│ │ à l'intérieur. │
?? ??
Le GnuplotHelper TracerSonde() la fonction trace un ensemble de données généré en accrochant la ns-3
source de trace avec une sonde créée par l'assistant, puis tracer les valeurs de la
sondeTraceSource. L'ensemble de données portera le titre fourni et comprendra le
'newValue' à chaque horodatage.
Si le chemin de configuration a plus d'une correspondance dans le système parce qu'il y a un caractère générique, alors
un ensemble de données pour chaque match sera tracé. Les titres des ensembles de données seront suivis du suffixe
caractères correspondants pour chacun des caractères génériques du chemin de configuration, séparés par des espaces. Pour
exemple, si le titre du jeu de données proposé est la chaîne "octets", et qu'il y a deux caractères génériques
dans le chemin, les titres de jeu de données comme "bytes-0 0" ou "bytes-12 9" seront possibles comme
étiquettes pour les ensembles de données qui sont tracés.
Vous trouverez un exemple d'utilisation de cette fonction dans le septième.cc code décrit ci-dessus
où il a été utilisé (avec substitution de variable) comme suit :
plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Octets de sortie",
"Compte d'octets de paquets",
GnuplotAggregator ::KEY_BELOW);
Autres Exemples
GnuplotGenericName Auxiliaire Exemple
Un exemple un peu plus simple que le septième.cc exemple peut être trouvé dans
src/stats/examples/gnuplot-helper-example.cc. Le gnuplot 2-D suivant a été créé en utilisant
l'exemple.
[image] Gnuplot 2-D Créé par gnuplot-helper-example.cc Exemple..UNINDENT
Dans cet exemple, il y a un objet Emetteur qui incrémente son compteur selon un
Processus de Poisson, puis émet la valeur du compteur en tant que source de trace.
Ptr émetteur = CreateObject ();
Names::Add ("/Noms/Emetteur", émetteur);
Notez qu'étant donné qu'il n'y a pas de caractères génériques dans le chemin utilisé ci-dessous, un seul flux de données a été
dessiné dans l'intrigue. Ce flux de données unique dans le tracé est simplement étiqueté "Emitter Count",
sans suffixes supplémentaires comme on le verrait s'il y avait des caractères génériques dans le chemin.
// Crée l'assistant gnuplot.
GnuplotHelper plotHelper ;
// Configurez le tracé.
plotHelper.ConfigurePlot ("gnuplot-helper-example",
"Comptage de l'émetteur en fonction du temps",
"Temps (secondes)",
"Compte d'émetteur",
"png");
// Tracez les valeurs générées par la sonde. Le chemin que nous proposons
// aide à désambiguïser la source de la trace.
plotHelper.PlotProbe ("ns3::Uinteger32Probe",
"/Noms/Emetteur/Compteur",
"Sortir",
"Compte d'émetteur",
GnuplotAggregator ::KEY_INSIDE);
Aide-fichier
Le FileHelper est une classe d'assistance utilisée pour mettre des valeurs de données dans un fichier. L'objectif global est
pour permettre aux utilisateurs de créer rapidement des fichiers texte formatés à partir des données exportées
in ns-3 sources de traces. Par défaut, une quantité minimale de transformation de données est effectuée ;
l'objectif est de générer des fichiers avec aussi peu d'instructions de configuration (par défaut) que
de qualité.
Aide-fichier Aperçu
Le FileHelper créera 1 ou plusieurs fichiers texte à la fin de la simulation.
Le FileHelper peut créer 4 types différents de fichiers texte :
· Formaté
· Espace séparé (par défaut)
· Séparées par des virgules
· Onglet séparé
Les fichiers formatés utilisent des chaînes de format de style C et la fonction sprintf() pour imprimer leur
valeurs dans le fichier en cours d'écriture.
Le fichier texte suivant avec 2 colonnes de valeurs formatées nommées
septième-paquet-octet-compte-0.txt a été créé en utilisant plus de nouveau code qui a été ajouté au
original ns-3 Code de l'exemple du tutoriel. Seules les 10 premières lignes de ce fichier sont affichées
ici pour la brièveté.
Temps (secondes) = 1.000e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.004e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.004e+00 Nombre d'octets de paquets = 576
Temps (secondes) = 1.009e+00 Nombre d'octets de paquets = 576
Temps (secondes) = 1.009e+00 Nombre d'octets de paquets = 576
Temps (secondes) = 1.015e+00 Nombre d'octets de paquets = 512
Temps (secondes) = 1.017e+00 Nombre d'octets de paquets = 576
Temps (secondes) = 1.017e+00 Nombre d'octets de paquets = 544
Temps (secondes) = 1.025e+00 Nombre d'octets de paquets = 576
Temps (secondes) = 1.025e+00 Nombre d'octets de paquets = 544
Le fichier texte différent suivant avec 2 colonnes de valeurs formatées nommées
septième-paquet-octet-compte-1.txt a également été créé en utilisant le même nouveau code qui a été ajouté à
l'original ns-3 Code de l'exemple du tutoriel. Seules les 10 premières lignes de ce fichier sont affichées
ici pour la brièveté.
Temps (secondes) = 1.002e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.007e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.013e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.020e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.028e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.036e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.045e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.053e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.061e+00 Nombre d'octets de paquets = 40
Temps (secondes) = 1.069e+00 Nombre d'octets de paquets = 40
Le nouveau code qui a été ajouté pour produire les deux fichiers texte est ci-dessous. Plus de détails sur
cette API sera couverte dans une section ultérieure.
Notez qu'étant donné qu'il y avait 2 correspondances pour le caractère générique dans le chemin, 2 fichiers texte distincts
ont été créés. Le premier fichier texte, nommé "seventh-packet-byte-count-0.txt",
correspond à la correspondance générique avec le "*" remplacé par "0". Le deuxième fichier texte,
qui est nommé "seventh-packet-byte-count-1.txt", correspond à la correspondance générique avec
le "*" remplacé par "1". Notez également que l'appel de fonction à ÉcrireProbe() donnera un
message d'erreur s'il n'existe aucune correspondance pour un chemin contenant des caractères génériques.
// Crée le fichier d'assistance.
FileHelper fileHelper;
// Configure le fichier à écrire.
fileHelper.ConfigureFile ("nombre d'octets du septième paquet",
FileAggregator : FORMATTÉ );
// Définissez les étiquettes pour ce fichier de sortie formaté.
fileHelper.Set2dFormat ("Time (Seconds) = %.3e\tPacket Byte Count = %.0f");
// Ecrit les valeurs générées par la sonde.
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"OutputBytes" );
Aide-fichier ConfigureFichier
Les FileHelper ConfigureFile() La fonction peut être utilisée pour configurer des fichiers texte.
Il a le prototype suivant :
void ConfigureFile (const std::string &outputFileNameWithoutExtension,
enum FileAggregator :: FileType fileType = FileAggregator :: SPACE_SEPARATED);
Il a les arguments suivants :
┌──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┐
│Argument │ Description │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nom du fichier de sortie à écrire │
│ │ sans extension. │
├──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┤
│fileType │ Type de fichier à écrire. Le │
│ │ le type de fichier par défaut est l'espace │
│ │ séparés. │
└──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ─────────────────┘
Les FileHelper ConfigureFile() la fonction configure les paramètres liés au fichier texte pour le
file helper pour qu'il crée un fichier nommé outputFileNameWithoutExtension plus
informations supplémentaires possibles à partir de correspondances génériques plus ".txt" avec des valeurs imprimées comme
spécifié par fileType. Le type de fichier par défaut est séparé par des espaces.
Vous trouverez un exemple d'utilisation de cette fonction dans le septième.cc code décrit ci-dessus
où il a été utilisé comme suit :
fileHelper.ConfigureFile ("nombre d'octets du septième paquet",
FileAggregator : FORMATTÉ );
Aide-fichier ÉcrireProbe
Les FileHelper ÉcrireProbe() La fonction peut être utilisée pour écrire des valeurs générées par des sondes dans
fichiers texte.
Il a le prototype suivant :
void WriteProbe (const std::string &typeId,
const std :: chaîne & chemin,
const std::chaîne &probeTraceSource);
Il a les arguments suivants :
?? ??
│Argument │ Description │
?? ??
│typeId │ L'ID de type de la sonde à │
│ │ créé. │
?? ??
│path │ Config path pour accéder à la trace │
│ │ origine. │
?? ??
│probeTraceSource │ La source de trace de la sonde vers │
│ │ accès. │
?? ??
Les FileHelper ÉcrireProbe() La fonction crée des fichiers texte de sortie générés en accrochant le
source de trace ns-3 avec une sonde créée par l'assistant, puis en écrivant les valeurs à partir du
sondeTraceSource. Les noms des fichiers de sortie auront le texte stocké dans la variable membre
m_outputFileNameWithoutExtension plus ".txt", et sera composé de la "newValue" à chaque
horodatage.
Si le chemin de configuration a plus d'une correspondance dans le système parce qu'il y a un caractère générique, alors
un fichier de sortie pour chaque match sera créé. Les noms des fichiers de sortie contiendront les
texte dans m_outputFileNameWithoutExtension plus les caractères correspondants pour chacun des
caractères génériques dans le chemin de configuration, séparés par des tirets, plus ".txt". Par exemple, si la valeur
dans m_outputFileNameWithoutExtension est la chaîne "packet-byte-count", et il y a deux
des caractères génériques dans le chemin, puis des noms de fichiers de sortie tels que "packet-byte-count-0-0.txt" ou
"packet-byte-count-12-9.txt" sera possible comme noms pour les fichiers qui seront créés.
Vous trouverez un exemple d'utilisation de cette fonction dans le septième.cc code décrit ci-dessus
où il a été utilisé comme suit :
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"OutputBytes" );
Autres Exemples
Fichier Auxiliaire Exemple
Un exemple un peu plus simple que le septième.cc exemple peut être trouvé dans
src/stats/examples/file-helper-example.cc. Cet exemple utilise uniquement FileHelper.
Le fichier texte suivant avec 2 colonnes de valeurs formatées nommées fichier-helper-exemple.txt
a été créé à l'aide de l'exemple. Seules les 10 premières lignes de ce fichier sont présentées ici pour
brièveté.
Temps (secondes) = 0.203 Nombre = 1
Temps (secondes) = 0.702 Nombre = 2
Temps (secondes) = 1.404 Nombre = 3
Temps (secondes) = 2.368 Nombre = 4
Temps (secondes) = 3.364 Nombre = 5
Temps (secondes) = 3.579 Nombre = 6
Temps (secondes) = 5.873 Nombre = 7
Temps (secondes) = 6.410 Nombre = 8
Temps (secondes) = 6.472 Nombre = 9
Dans cet exemple, il y a un objet Emetteur qui incrémente son compteur selon un
Processus de Poisson, puis émet la valeur du compteur en tant que source de trace.
Ptr émetteur = CreateObject ();
Names::Add ("/Noms/Emetteur", émetteur);
Notez qu'étant donné qu'il n'y a pas de caractères génériques dans le chemin utilisé ci-dessous, un seul fichier texte a été
établi. Ce fichier texte unique est simplement nommé "file-helper-example.txt", sans supplément
suffixes comme vous le verriez s'il y avait des caractères génériques dans le chemin.
// Crée le fichier d'assistance.
FileHelper fileHelper;
// Configure le fichier à écrire.
fileHelper.ConfigureFile ("fichier-helper-exemple",
FileAggregator : FORMATTÉ );
// Définissez les étiquettes pour ce fichier de sortie formaté.
fileHelper.Set2dFormat ("Heure (secondes) = %.3e\tCount = %.0f");
// Ecrit les valeurs générées par la sonde. Le chemin que nous
// provide aide à lever l'ambiguïté de la source de la trace.
fileHelper.WriteProbe ("ns3::Uinteger32Probe",
"/Noms/Emetteur/Compteur",
"Sortir");
Domaine et Limites
Actuellement, seules ces sondes ont été implémentées et connectées à GnuplotHelper et
au FileHelper :
· Sonde booléenne
· DoubleSonde
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· Packet Probe
· ApplicationPacketProbe
· Ipv4PacketProbe
Ces sondes sont donc les seuls TypeId disponibles pour être utilisés dans TracerSonde() et
ÉcrireProbe().
Dans les prochaines sections, nous couvrons chacun des types d'objets fondamentaux (sonde, collecteur,
et Aggregator) plus en détail, et montrer comment ils peuvent être connectés ensemble en utilisant
API de niveau inférieur.
Sondes
Cette section détaille les fonctionnalités fournies par la classe Probe à un ns-3
simulation, et donne des exemples sur la façon de les coder dans un programme. Cette rubrique est destinée à
utilisateurs intéressés par le développement de simulations avec ns-3 outils et utilisation des Données
Collection Framework, dont la classe Probe fait partie, pour générer une sortie de données avec
les résultats de leur simulation.
ASAP Aperçu
Un objet Sonde est supposé être connecté à une variable de la simulation dont les valeurs
tout au long de l'expérience sont pertinentes pour l'utilisateur. La sonde enregistrera ce qui a été
valeurs supposées par la variable tout au long de la simulation et transmettre ces données à un autre
membre du cadre de collecte de données. Bien qu'il soit hors de la portée de cette section de
discuter de ce qui se passe après que la sonde a produit sa sortie, il suffit de dire que, en
la fin de la simulation, l'utilisateur disposera d'informations détaillées sur les valeurs
stocké à l'intérieur de la variable sondée pendant la simulation.
Généralement, une sonde est connectée à un ns-3 origine des traces. De cette manière, chaque fois que le
trace source exporte une nouvelle valeur, la sonde consomme la valeur (et l'exporte en aval
à un autre objet via sa propre source de trace).
La sonde peut être considérée comme une sorte de filtre sur les sources de traces. Les principales raisons de
éventuellement se raccorder à une sonde plutôt que directement à une source de trace sont les suivants :
· Les sondes peuvent être dynamiquement activées et désactivées pendant la simulation avec des appels à Activer()
et Désactiver(). Par exemple, la sortie des données peut être désactivée pendant la
phase d'échauffement de la simulation.
· Les sondes peuvent effectuer des opérations sur les données pour extraire des valeurs à partir de données plus complexes.
ouvrages d'art ; par exemple, sortie de la valeur de taille de paquet à partir d'un ns3::Packet reçu.
· Les sondes enregistrent un nom dans l'espace de noms ns3::Config (en utilisant Noms ::Ajouter ()) de sorte que d'autres
les objets peuvent s'y référer.
· Les sondes fournissent une méthode statique qui permet de manipuler une sonde par son nom, comme
ce qui se fait dans ns2measure [Cic06]
Stat ::put ("my_metric", ID, sample);
L'équivalent ns-3 du code ns2measure ci-dessus est, par exemple
DoubleProbe::SetValueByPath ("/path/to/probe", exemple);
Création
Notez qu'un objet de classe de base de sonde ne peut pas être créé car il s'agit d'une base abstraite
class, c'est-à-dire qu'il a des fonctions virtuelles pures qui n'ont pas été implémentées. Un objet de
type DoubleProbe, qui est une sous-classe de la classe Probe, sera créé ici pour montrer
ce qui doit être fait.
On déclare un DoubleProbe en mémoire dynamique en utilisant la classe smart pointer (Ptr ). À
créer un DoubleProbe en mémoire dynamique avec des pointeurs intelligents, il suffit d'appeler le
ns-3 méthode CréerObjet():
Ptr masonde = CréerObjet ();
La déclaration ci-dessus crée DoubleProbes en utilisant les valeurs par défaut pour ses attributs.
Il existe quatre attributs dans la classe DoubleProbe ; deux dans l'objet de classe de base
DataCollectionObject, et deux dans la classe de base Probe :
· "Nom" (DataCollectionObject), une StringValue
· "Enabled" (DataCollectionObject), une BooleanValue
· "Démarrer" (Probe), une TimeValue
· "Stop" (Probe), une TimeValue
On peut définir de tels attributs lors de la création d'un objet en utilisant la méthode suivante :
Ptr myprobe = CreateObjectWithAttributes (
"Nom", StringValue ("masonde"),
"Activé", BooleanValue (faux),
"Début", TimeValue (Secondes (100.0)),
"Stop", TimeValue (Secondes (1000.0)) );
Start et Stop sont des variables de temps qui déterminent l'intervalle d'action de la sonde. le
La sonde ne sortira des données que si l'heure actuelle de la simulation est à l'intérieur de cette heure.
intervalle. La valeur de temps spéciale de 0 seconde pour Stop désactivera cet attribut (c'est-à-dire
garder la sonde allumée pendant toute la simulation). Enabled est un indicateur qui active ou désactive la sonde.
off et doit être défini sur true pour que la sonde exporte les données. Le nom est le nom de l'objet
dans le cadre du DCF.
Importation et l'exportation données,
ns-3 les sources de trace sont fortement typées, de sorte que les mécanismes d'accrochage des sondes à une trace
source et pour exporter des données appartiennent à ses sous-classes. Par exemple, la valeur par défaut
distribution de ns-3 fournit une classe DoubleProbe conçue pour s'accrocher à une trace
source exportant une valeur double. Nous détaillerons ensuite le fonctionnement du DoubleProbe, et
discutez ensuite de la façon dont d'autres classes de sonde peuvent être définies par l'utilisateur.
DoubleSonde Aperçu
Le DoubleProbe se connecte à une double valeur ns-3 source de trace, et exporte lui-même un
différent à double valeur ns-3 origine des traces.
Le code suivant, tiré de src/stats/examples/double-probe-example.cc, montre la base
opérations de plomberie du DoubleProbe dans une simulation, où il sonde un compteur
exporté par un objet émetteur (classe Emitter).
Ptr émetteur = CreateObject ();
Names::Add ("/Noms/Emetteur", émetteur);
Ptr sonde1 = CréerObjet ();
// Connecte la sonde au compteur de l'émetteur
bool connected = probe1->ConnectByObject ("Compteur", émetteur);
Le code suivant teste le même compteur exporté par le même objet émetteur. Cette
DoubleProbe, cependant, utilise un chemin dans l'espace de noms de configuration pour rendre le
lien. Notez que l'émetteur s'est enregistré dans l'espace de noms de configuration après
cela a été créé; sinon, le ConnectByPath ne fonctionnerait pas.
Ptr sonde2 = CréerObjet ();
// Remarque, aucune valeur de retour n'est vérifiée ici
probe2->ConnectByPath ("/Names/Emitter/Counter");
La prochaine DoubleProbe illustrée ci-dessous aura sa valeur définie à l'aide de son chemin dans
l'espace de noms de configuration. Notez que cette fois le DoubleProbe s'est enregistré dans le
espace de noms de configuration après sa création.
Ptr sonde3 = CréerObjet ();
probe3->SetName ("StaticallyAccessedProbe");
// Nous devons l'ajouter à la base de données de configuration
Names::Add ("/Names/Probes", probe3->GetName(), probe3);
La fonction Count() de l'émetteur est maintenant capable de définir la valeur de ce DoubleProbe comme
suit:
annuler
Emetteur :: Compter (vide)
{
m_counter += 1.0 ;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
}
L'exemple ci-dessus montre comment le code appelant la sonde n'a pas besoin d'avoir un explicite
référence à la sonde, mais peut diriger le paramètre de valeur via l'espace de noms Config.
Ceci est similaire dans la fonctionnalité à la Stat :: Mettre méthode introduite par l'article ns2measure
[Cic06], et permet aux utilisateurs d'insérer temporairement des instructions Probe comme printf déclarations
dans l'existant ns-3 des modèles. Notez que pour pouvoir utiliser la DoubleProbe dans ce
exemple comme celui-ci, 2 choses étaient nécessaires :
1. le fichier d'en-tête du module de statistiques a été inclus dans l'exemple de fichier .cc
2. l'exemple a été rendu dépendant du module stats dans son fichier wscript.
Des choses analogues doivent être faites pour ajouter d'autres sondes à d'autres endroits du ns-3
base de code.
Les valeurs de DoubleProbe peuvent également être définies à l'aide de la fonction DoubleProbe::SetValue(),
tandis que les valeurs de DoubleProbe peuvent être obtenues à l'aide de la fonction
DoubleProbe :: GetValue ().
Le DoubleProbe exporte les valeurs doubles dans sa source de trace "Output" ; un objet en aval
peut accrocher un récepteur de trace (NotifyViaProbe) à ceci comme suit :
connecté = probe1->TraceConnect ("Sortie", probe1->GetName (), MakeCallback (&NotifyViaProbe));
Autres sondes
Outre la DoubleProbe, les sondes suivantes sont également disponibles :
· Uinteger8Probe se connecte à un ns-3 source de trace exportant un uint8_t.
· Uinteger16Probe se connecte à un ns-3 source de trace exportant un uint16_t.
· Uinteger32Probe se connecte à un ns-3 source de trace exportant un uint32_t.
· PacketProbe se connecte à un ns-3 source de trace exportant un paquet.
· ApplicationPacketProbe se connecte à un ns-3 source de trace exportant un paquet et un socket
adresse.
· Ipv4PacketProbe se connecte à un ns-3 source de trace exportant un paquet, un objet IPv4 et
une interface.
La création nouvelle ASAP types
Pour créer un nouveau type de sonde, vous devez effectuer les étapes suivantes :
· Assurez-vous que votre nouvelle classe Probe est dérivée de la classe de base Probe.
· Assurez-vous que les fonctions virtuelles pures dont votre nouvelle classe Probe hérite de la
La classe de base de la sonde est implémentée.
· Trouver une classe Probe existante qui utilise une source de trace dont le type est le plus proche de
type de source de trace que votre sonde utilisera.
· Copiez le fichier d'en-tête (.h) et le fichier d'implémentation (.cc) de cette classe Probe existante sur deux
de nouveaux fichiers avec des noms correspondant à votre nouvelle sonde.
· Remplacez les types, les arguments et les variables dans les fichiers copiés par les
tapez pour votre sonde.
· Apportez les modifications nécessaires pour que le code soit compilé et qu'il se comporte comme vous le feriez
le souhaitez.
Exemples
Deux exemples seront discutés en détail ici :
· Exemple de double sonde
· Exemple de tracé de paquets IPv4
Double ASAP Exemple
L'exemple de la double sonde a été discuté précédemment. Le programme d'exemple peut être trouvé
in src/stats/examples/double-probe-example.cc. Pour résumer ce qui se passe dans ce programme,
il y a un émetteur qui exporte un compteur qui s'incrémente selon un processus de Poisson.
En particulier, deux manières d'émettre des données sont présentées :
1. via une variable tracée accrochée à une sonde :
Valeur tracée m_counter ; // normalement ce serait de type entier
2. par l'intermédiaire d'un compteur dont la valeur est affichée sur une deuxième sonde, référencée par son nom dans
le système de configuration :
annuler
Emetteur :: Compter (vide)
{
NS_LOG_FUNCTION (ceci);
NS_LOG_DEBUG ("Comptage à " << Simulator::Now ().GetSeconds ());
m_counter += 1.0 ;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (Seconds (m_var->GetValue ()), &Emitter::Count, this);
}
Regardons la sonde plus attentivement. Les sondes peuvent recevoir leurs valeurs dans un multiple
façons:
1. par la sonde accédant directement à la source de trace et y connectant un récepteur de trace
2. par la sonde accédant à la source de trace via l'espace de noms de configuration et connectant un
trace couler jusqu'à lui
3. par le code appelant appelant explicitement la sonde DéfinirValeur() méthode
4. par le code appelant appelant explicitement DéfinirValueByPath
("/path/through/Config/namespace", ...)
Les deux premières techniques devraient être les plus courantes. Toujours dans l'exemple, le
l'accrochage d'une fonction de rappel normale est illustré, comme cela se fait généralement dans ns-3. Ce
la fonction de rappel n'est pas associée à un objet Sonde. Nous appellerons ce cas 0) ci-dessous.
// Ceci est une fonction pour tester l'accrochage d'une fonction brute à la source de la trace
annuler
NotifyViaTraceSource (std :: contexte de chaîne, double oldVal, double newVal)
{
NS_LOG_DEBUG ("contexte : " << contexte << " ancien " << oldVal << " new " << newVal);
}
Tout d'abord, l'émetteur doit être configuré :
Ptr émetteur = CreateObject ();
Names::Add ("/Noms/Emetteur", émetteur);
// L'objet Emitter n'est pas associé à un nœud ns-3, donc
// il ne démarrera pas automatiquement, nous devons donc le faire nous-mêmes
Simulator::Schedule (Seconds (0.0), &Emitter::Start, émetteur);
Les différents DoubleProbes interagissent avec l'émetteur dans l'exemple comme indiqué ci-dessous.
Cas 0):
// L'exemple ci-dessous montre les fonctionnalités typiques sans sonde
// (connecte une fonction de puits à une source de trace)
//
connecté = émetteur->TraceConnect ("Compteur", "exemple de contexte", MakeCallback (&NotifyViaTraceSource) );
NS_ASSERT_MSG (connecté, "Source de trace non connectée");
cas 1):
//
// Probe1 sera accroché directement à l'objet source de la trace de l'émetteur
//
// la sonde1 sera accrochée à la source de trace de l'émetteur
Ptr sonde1 = CréerObjet ();
// le nom de la sonde peut servir de contexte dans le traçage
sonde1->SetName ("ObjectProbe");
// Connecte la sonde au compteur de l'émetteur
connecté = probe1->ConnectByObject ("Compteur", émetteur);
NS_ASSERT_MSG (connecté, "Source de la trace non connectée à la sonde1" );
cas 2):
//
// Probe2 sera accroché à l'objet source de la trace de l'émetteur par
// y accéder par nom de chemin dans la base de données Config
//
// Crée une autre sonde similaire ; cela se connectera via un chemin de configuration
Ptr sonde2 = CréerObjet ();
probe2->SetName ("PathProbe");
// Remarque, aucune valeur de retour n'est vérifiée ici
probe2->ConnectByPath ("/Names/Emitter/Counter");
cas 4) (le cas 3 n'est pas représenté dans cet exemple) :
//
// Probe3 sera appelé par l'émetteur directement via le
// méthode statique SetValueByPath().
//
Ptr sonde3 = CréerObjet ();
probe3->SetName ("StaticallyAccessedProbe");
// Nous devons l'ajouter à la base de données de configuration
Names::Add ("/Names/Probes", probe3->GetName(), probe3);
Et enfin, l'exemple montre comment les sondes peuvent être accrochées pour générer une sortie :
// La sonde elle-même doit générer une sortie. Le contexte que nous proposons
// à cette sonde (dans ce cas, le nom de la sonde) aidera à lever l'ambiguïté
// la source de la trace
connecté = probe3->TraceConnect ("Sortie",
"/Names/Probes/StaticallyAccessedProbe/Output",
MakeCallback (&NotifyViaProbe) );
NS_ASSERT_MSG (connecté, "La source de la trace n'est pas .. connectée à la sortie de la sonde 3" );
Le rappel suivant est lié à la sonde dans cet exemple à des fins d'illustration ;
normalement, la sonde serait accrochée à un objet Collector.
// Ceci est une fonction pour tester l'accrochage à la sortie de la sonde
annuler
NotifyViaProbe (std :: contexte de chaîne, double oldVal, double newVal)
{
NS_LOG_DEBUG ("contexte : " << contexte << " ancien " << oldVal << " new " << newVal);
}
IPv4 Paquet Terrain Exemple
L'exemple de tracé de paquets IPv4 est basé sur l'exemple cinquième.cc du ns-3 Didacticiel. Ce
peuvent être trouvés dans src/stats/examples/ipv4-packet-plot-example.cc.
nœud 0 nœud 1
+----------------+ +----------------+
| ns-3TCP | | ns-3TCP |
+----------------+ +----------------+
| 10.1.1.1 | | 10.1.1.2 |
+----------------+ +----------------+
| point à point | | point à point |
+----------------+ +----------------+
| |
+ --------------------- +
Nous allons juste regarder la sonde, car elle illustre que les sondes peuvent également décompresser les valeurs de
structures (dans ce cas, des paquets) et signaler ces valeurs en tant que sorties de source de trace, plutôt
que de simplement passer par le même type de données.
Il y a d'autres aspects de cet exemple qui seront expliqués plus tard dans la documentation.
Les deux types de données exportées sont le paquet lui-même (Sortie) et un décompte des
nombre d'octets dans le paquet (Octets de sortie).
ID de type
Ipv4PacketProbe ::GetTypeId ()
{
statique TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
.SetParent ()
.AddConstructor ()
.AddTraceSource ( "Sortie",
"Le paquet plus son objet IPv4 et son interface qui servent de sortie pour cette sonde",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource ( "OutputBytes",
"Le nombre d'octets dans le paquet",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
retour tid;
}
Lorsque le puits de trace de la sonde reçoit un paquet, si la sonde est activée, elle émettra
le paquet sur son Sortie source de trace, mais il affichera également le nombre d'octets sur le
Octets de sortie origine des traces.
annuler
Ipv4PacketProbe :: TraceSink (Ptr paquet, Ptr ipv4, interface uint4_t)
{
NS_LOG_FUNCTION (cette interface << paquet << ipv4 <<);
si (est activé ())
{
m_paquet = paquet ;
m_ipv4 = ipv4 ;
m_interface = interface ;
m_output (paquet, ipv4, interface);
uint32_t packetSizeNew = paquet->GetSize ();
m_outputBytes (m_packetSizeOld, packetSizeNew);
m_packetSizeOld = packetSizeNew ;
}
}
Références
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, "Un cadre intégré pour
Permettre une collecte de données et une analyse statistique efficaces avec ns2, atelier sur
ns-2 (WNS2), Pise, Italie, octobre 2006.
Collecteurs
Cette section est un espace réservé pour détailler les fonctionnalités fournies par le collecteur
classe à un ns-3 simulation, et donne des exemples sur la façon de les coder dans un programme.
Attention: À partir de ns-3.18, les collecteurs sont toujours en cours de développement et ne sont pas encore fournis dans le cadre
du cadre.
Agrégateurs
Cette section détaille les fonctionnalités fournies par la classe Aggregator à un ns-3
simulation. Cette section est destinée aux utilisateurs intéressés par le développement de simulations avec
ns-3 outils et en utilisant le Data Collection Framework, dont la classe Aggregator est un
partie, pour générer une sortie de données avec les résultats de leur simulation.
Aggregator Aperçu
Un objet Aggregator est censé être accroché à une ou plusieurs sources de trace afin de
recevoir une entrée. Les agrégateurs sont le point final des données collectées par le réseau de
Sondes et collecteurs pendant la simulation. C'est le travail de l'agrégateur de prendre ces
valeurs et les transformer dans leur format de sortie final tel que des fichiers de texte brut,
des fichiers de feuille de calcul, des tracés ou des bases de données.
En règle générale, un agrégateur est connecté à un ou plusieurs collecteurs. De cette manière, chaque fois
les sources de trace des collecteurs exportent de nouvelles valeurs, l'agrégateur peut traiter la valeur afin
qu'il peut être utilisé dans le format de sortie final où les valeurs de données résideront après le
simulation.
Notez les points suivants concernant les agrégateurs :
· Les agrégateurs peuvent être dynamiquement activés et désactivés pendant la simulation avec des appels à
Activer() et Désactiver(). Par exemple, l'agrégation des données peut être désactivée pendant
la phase d'échauffement de la simulation, ce qui signifie que ces valeurs ne seront pas incluses dans la version finale
support de sortie.
· Les agrégateurs reçoivent des données des collecteurs via des rappels. Lorsqu'un collecteur est associé
à un agrégateur, un appel à TraceConnect est effectué pour établir la trace de l'agrégateur
méthode de puits en tant que rappel.
A ce jour, deux Agrégateurs ont été mis en place :
· GnuplotAggregator
· Agrégateur de fichiers
GnuplotAggregator
Le GnuplotAggregator produit des fichiers de sortie utilisés pour créer des gnuplots.
Le GnuplotAggregator créera 3 fichiers différents à la fin de la simulation :
· Un fichier de données gnuplot séparé par des espaces
· Un fichier de contrôle gnuplot
· Un script shell pour générer le gnuplot
Création
Un objet de type GnuplotAggregator sera créé ici pour montrer ce qui doit être fait.
On déclare un GnuplotAggregator en mémoire dynamique en utilisant la classe smart pointer
(Ptr ). Pour créer un GnuplotAggregator en mémoire dynamique avec des pointeurs intelligents, il suffit
doit appeler le ns-3 méthode CréerObjet(). Le code suivant de
src/stats/examples/gnuplot-aggregator-example.cc montre comment procéder :
string fileNameWithoutExtension = "gnuplot-aggregator" ;
// Crée un agrégateur.
Ptr agrégateur =
CréerObjet (NomFichierSansExtension);
Le premier argument du constructeur, fileNameWithoutExtension, est le nom du
fichiers liés à gnuplot à écrire sans extension. Ce GnuplotAggregator créera un
fichier de données gnuplot séparé par des espaces nommé "gnuplot-aggregator.dat", un fichier de contrôle gnuplot
nommé "gnuplot-aggregator.plt", et un script shell pour générer le gnuplot nommé +
"gnuplot-aggregator.sh".
Le gnuplot créé peut avoir sa clé à 4 emplacements différents :
· Pas de clé
· Clé à l'intérieur de l'intrigue (par défaut)
· Clé au-dessus de la parcelle
· Clé sous le tracé
Les valeurs d'énumération d'emplacement de clé gnuplot suivantes sont autorisées pour spécifier la position de la clé :
enum KeyLocation {
NO_KEY,
CLÉ_INSIDE,
CLÉ_ABOVE,
KEY_BELOW
};
Si l'on souhaitait avoir la clé en dessous plutôt que la position par défaut de l'intérieur, alors
vous pourriez faire ce qui suit.
aggregator->SetKeyLocation(GnuplotAggregator ::KEY_BELOW);
Exemples
Un exemple sera discuté en détail ici :
· Exemple d'agrégateur Gnuplot
GnuplotGenericName Aggregator Exemple
Un exemple qui exerce le GnuplotAggregator peut être trouvé dans
src/stats/examples/gnuplot-aggregator-example.cc.
Le gnuplot 2-D suivant a été créé à l'aide de l'exemple.
[image] Gnuplot 2-D Créé par gnuplot-aggregator-example.cc Exemple..UNINDENT
Ce code de l'exemple montre comment construire le GnuplotAggregator comme cela a été discuté
au dessus.
annuler Create2dPlot ()
{
Utilisant l'espace de noms std;
string fileNameWithoutExtension = "gnuplot-aggregator" ;
string plotTitle = "Tracé d'agrégation Gnuplot" ;
string plotXAxisHeading = "Temps (secondes)" ;
string plotYAxisHeading = "Valeurs doubles" ;
string plotDatasetLabel = "Valeurs de données" ;
string datasetContext = "Ensemble de données/Contexte/Chaîne" ;
// Crée un agrégateur.
Ptr agrégateur =
CréerObjet (NomFichierSansExtension);
Divers attributs GnuplotAggregator sont définis, y compris l'ensemble de données 2D qui sera
tracé.
// Définit les propriétés de l'agrégateur.
agrégateur->SetTerminal ("png");
aggregator->SetTitle (plotTitle);
aggregator->SetLegend (plotXAxisHeading, plotYAxisHeading);
// Ajoute un ensemble de données à l'agrégateur.
aggregator->Add2dDataset (datasetContext, plotDatasetLabel);
// l'agrégateur doit être activé
agrégateur->Activer ();
Ensuite, les valeurs 2-D sont calculées et chacune est écrite individuellement dans le
GnuplotAggregator utilisant le Ecrire2d() la fonction.
temps double;
valeur double ;
// Crée le jeu de données 2-D.
pour (temps = -5.0 ; temps <= +5.0 ; temps += 1.0)
{
// Calcule la courbe 2-D
//
// 2
// valeur = heure .
//
valeur = temps * temps ;
// Ajoute ce point au tracé.
aggregator->Write2d (datasetContext, time, value);
}
// Désactiver la journalisation des données pour l'agrégateur.
agrégateur->Désactiver ();
}
Agrégateur de fichiers
Le FileAggregator envoie les valeurs qu'il reçoit dans un fichier.
Le FileAggregator peut créer 4 types de fichiers différents :
· Formaté
· Espace séparé (par défaut)
· Séparées par des virgules
· Onglet séparé
Les fichiers formatés utilisent des chaînes de format de style C et la fonction sprintf() pour imprimer leur
valeurs dans le fichier en cours d'écriture.
Création
Un objet de type FileAggregator sera créé ici pour montrer ce qui doit être fait.
On déclare un FileAggregator en mémoire dynamique en utilisant la classe smart pointer (Ptr ).
Pour créer un FileAggregator en mémoire dynamique avec des pointeurs intelligents, il suffit d'appeler
le ns-3 méthode CreateObject. Le code suivant de
src/stats/examples/file-aggregator-example.cc montre comment procéder :
string fileName = "file-aggregator-formatted-values.txt" ;
// Crée un agrégateur qui aura des valeurs formatées.
Ptr agrégateur =
CréerObjet (fileName, FileAggregator::FORMATTED);
Le premier argument du constructeur, filename, est le nom du fichier à écrire ; la
le deuxième argument, fileType, est le type de fichier à écrire. Ce FileAggregator créera un
fichier nommé "file-aggregator-formatted-values.txt" avec ses valeurs imprimées comme spécifié par
fileType, c'est-à-dire formaté dans ce cas.
Les valeurs d'énumération de type de fichier suivantes sont autorisées :
enum TypeFichier {
FORMATÉ,
ESPACE_SEPARÉ,
SÉPARÉES PAR DES VIRGULES,
TAB_SEPARATED
};
Exemples
Un exemple sera discuté en détail ici :
· Exemple d'agrégateur de fichiers
Fichier Aggregator Exemple
Un exemple qui exerce le FileAggregator peut être trouvé dans
src/stats/examples/file-aggregator-example.cc.
Le fichier texte suivant avec 2 colonnes de valeurs séparées par des virgules a été créé en utilisant le
Exemple.
- 5,25
- 4,16
- 3,9
- 2,4
- 1,1
0,0
1,1
2,4
3,9
4,16
5,25
Ce code de l'exemple montre comment construire le FileAggregator comme cela a été discuté
au dessus.
annuler CreateCommaSeparatedFile ()
{
Utilisant l'espace de noms std;
string fileName = "file-aggregator-comma-separated.txt" ;
string datasetContext = "Ensemble de données/Contexte/Chaîne" ;
// Crée un agrégateur.
Ptr agrégateur =
CréerObjet (fileName, FileAggregator::COMMA_SEPARATED);
Les attributs FileAggregator sont définis.
// l'agrégateur doit être activé
agrégateur->Activer ();
Ensuite, les valeurs 2-D sont calculées et chacune est écrite individuellement dans le
FileAggregator utilisant le Ecrire2d() la fonction.
temps double;
valeur double ;
// Crée le jeu de données 2-D.
pour (temps = -5.0 ; temps <= +5.0 ; temps += 1.0)
{
// Calcule la courbe 2-D
//
// 2
// valeur = heure .
//
valeur = temps * temps ;
// Ajoute ce point au tracé.
aggregator->Write2d (datasetContext, time, value);
}
// Désactiver la journalisation des données pour l'agrégateur.
agrégateur->Désactiver ();
}
Le fichier texte suivant avec 2 colonnes de valeurs formatées a également été créé à l'aide de la
Exemple.
Temps = -5.000e+00 Valeur = 25
Temps = -4.000e+00 Valeur = 16
Temps = -3.000e+00 Valeur = 9
Temps = -2.000e+00 Valeur = 4
Temps = -1.000e+00 Valeur = 1
Temps = 0.000e+00 Valeur = 0
Temps = 1.000e+00 Valeur = 1
Temps = 2.000e+00 Valeur = 4
Temps = 3.000e+00 Valeur = 9
Temps = 4.000e+00 Valeur = 16
Temps = 5.000e+00 Valeur = 25
Ce code de l'exemple montre comment construire le FileAggregator comme cela a été discuté
au dessus.
annuler CreateFormattedFile ()
{
Utilisant l'espace de noms std;
string fileName = "file-aggregator-formatted-values.txt" ;
string datasetContext = "Ensemble de données/Contexte/Chaîne" ;
// Crée un agrégateur qui aura des valeurs formatées.
Ptr agrégateur =
CréerObjet (fileName, FileAggregator::FORMATTED);
Les attributs FileAggregator sont définis, y compris la chaîne de format de style C à utiliser.
// Définit le format des valeurs.
aggregator->Set2dFormat ("Time = %.3e\tValue = %.0f");
// l'agrégateur doit être activé
agrégateur->Activer ();
Ensuite, les valeurs 2-D sont calculées et chacune est écrite individuellement dans le
FileAggregator utilisant le Ecrire2d() la fonction.
temps double;
valeur double ;
// Crée le jeu de données 2-D.
pour (temps = -5.0 ; temps <= +5.0 ; temps += 1.0)
{
// Calcule la courbe 2-D
//
// 2
// valeur = heure .
//
valeur = temps * temps ;
// Ajoute ce point au tracé.
aggregator->Write2d (datasetContext, time, value);
}
// Désactiver la journalisation des données pour l'agrégateur.
agrégateur->Désactiver ();
}
Adaptateurs
Cette section détaille les fonctionnalités fournies par la classe Adapter à un ns-3
simulation. Cette section est destinée aux utilisateurs intéressés par le développement de simulations avec
ns-3 outils et en utilisant le Data Collection Framework, dont la classe Adapter fait partie,
pour générer une sortie de données avec les résultats de leur simulation.
Remarque : le terme « adaptateur » peut également être orthographié « adaptateur » ; nous avons choisi l'orthographe alignée
avec la norme C++.
Adaptateur Aperçu
Un adaptateur est utilisé pour établir des connexions entre différents types d'objets DCF.
À ce jour, un adaptateur a été implémenté :
· Adaptateur TimeSeries
Heure Série Adaptateur
Le TimeSeriesAdaptor permet aux sondes de se connecter directement aux agrégateurs sans avoir besoin d'aucune
Collectionneur entre les deux.
Les deux assistants DCF implémentés utilisent TimeSeriesAdaptors afin de prendre en compte
valeurs de différents types et sortir l'heure actuelle plus la valeur avec les deux convertis
à doubler.
Le rôle de la classe TimeSeriesAdaptor est celui d'un adaptateur, qui prend des valeurs brutes
sonde des données de différents types et génère un tuple de deux valeurs doubles. Le premier est un
horodatage, qui peut être défini sur différentes résolutions (par exemple secondes, millisecondes, etc.) dans
le futur mais qui est actuellement codé en dur sur Seconds. La seconde est la conversion d'un
valeur non double en une valeur double (éventuellement avec perte de précision).
Portée/Limites
Cette section traite de la portée et des limites du cadre de collecte de données.
Actuellement, seules ces sondes ont été implémentées dans DCF :
· Sonde booléenne
· DoubleSonde
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· Packet Probe
· ApplicationPacketProbe
· Ipv4PacketProbe
Actuellement, aucun Collector n'est disponible dans le DCF, bien qu'un BasicStatsCollector soit sous
Actuellement, seuls ces agrégateurs ont été implémentés dans DCF :
· GnuplotAggregator
· Agrégateur de fichiers
Actuellement, seul cet adaptateur a été implémenté dans DCF :
Adaptateur de série chronologique.
A venir Réalisations
Cette section traite des travaux futurs à effectuer sur le cadre de collecte de données.
Voici certaines choses qui doivent encore être faites :
· Branchez plus de sources de trace dans ns-3 code pour obtenir plus de valeurs du simulateur.
· Implémenter plus de types de sondes qu'il n'y en a actuellement.
· Mettre en œuvre plus que le seul collecteur 2D actuel, BasicStatsCollector.
· Implémenter plus d'agrégateurs.
· Mettre en œuvre plus que de simples adaptateurs.
Statistique Cadre
Ce chapitre décrit les travaux sur la collecte de données de simulation et le cadre statistique pour
ns-3.
Le code source du cadre statistique se trouve dans le répertoire source/statistiques.
Objectifs
Les principaux objectifs de cet effort sont les suivants :
· Fournir des fonctionnalités pour enregistrer, calculer et présenter des données et des statistiques à des fins d'analyse
de simulations de réseau.
· Améliorez les performances de simulation en réduisant le besoin de générer des journaux de suivi étendus dans
afin de collecter des données.
· Activez le contrôle de la simulation via des statistiques en ligne, par exemple en terminant des simulations ou
répétition des essais.
Les sous-objectifs dérivés et autres fonctionnalités cibles incluent les éléments suivants :
· Intégration avec le système de suivi ns-3 existant en tant que cadre d'instrumentation de base
du moteur de simulation interne, par exemple les piles réseau, les périphériques réseau et les canaux.
· Permettre aux utilisateurs d'utiliser le cadre de statistiques sans nécessiter l'utilisation du traçage
système.
· Aider les utilisateurs à créer, agréger et analyser des données sur plusieurs essais.
· Prise en charge de l'instrumentation créée par l'utilisateur, par exemple des événements spécifiques à l'application et
les mesures.
· Faible surcharge de mémoire et de processeur lorsque le package n'est pas utilisé.
· Exploiter autant que possible les outils d'analyse et de sortie existants. Le cadre peut
fournir quelques statistiques de base, mais l'accent est mis sur la collecte de données et leur
accessible pour la manipulation dans les outils établis.
· La prise en charge éventuelle de la distribution de réplications indépendantes est importante mais n'est pas incluse
dans la première série de fonctionnalités.
Aperçu
Le cadre de statistiques comprend les fonctionnalités suivantes :
· Le cadre de base et deux collecteurs de données de base : un compteur et un min/max/avg/total
observateur.
· Extensions de celles-ci pour travailler facilement avec les temps et les paquets.
· Sortie en texte brut formatée pour OMNet++.
· Sortie de base de données à l'aide SQLite, un moteur SQL autonome, léger et hautes performances.
· Métadonnées obligatoires et ouvertes pour décrire et travailler avec les exécutions.
· Un exemple basé sur l'expérience théorique d'examen des propriétés des NS-3
performances Wi-Fi ad hoc par défaut. Il intègre les éléments suivants :
· Constructions d'un réseau WiFi ad hoc à deux nœuds, avec les nœuds à une distance paramétrée
une part.
· Applications de source et de puits de trafic UDP avec un comportement légèrement différent et
crochets de mesure que les classes de stock.
· Collecte de données du noyau NS-3 via des signaux de trace existants, en particulier des données sur
trames émises et reçues par les objets WiFi MAC.
· Instrumentation d'applications personnalisées en connectant de nouveaux signaux de trace au stat
framework, ainsi que via des mises à jour directes. Les informations sont enregistrées sur le nombre total de paquets
envoyés et reçus, octets transmis et délai de bout en bout.
· Un exemple d'utilisation de balises de paquets pour suivre le retard de bout en bout.
· Un script de contrôle simple qui exécute un certain nombre d'essais de l'expérience à différents
distances et interroge la base de données résultante pour produire un graphique à l'aide de GNUPlot.
Faire
Les éléments hautement prioritaires comprennent :
· Inclusion d'un code de statistiques en ligne, par exemple pour des intervalles de confiance efficaces en mémoire.
· Dispositions dans les collecteurs de données pour terminer les exécutions, c'est-à-dire lorsqu'un seuil ou
la confiance est au rendez-vous.
· Collecteurs de données pour la journalisation des échantillons au fil du temps et sortie dans les différents formats.
· Démontrer l'écriture d'une colle d'événement cyclique simple pour interroger régulièrement une certaine valeur.
Chacun de ceux-ci devrait s'avérer simple à intégrer dans le cadre actuel.
approche
Le cadre est basé sur les principes fondamentaux suivants :
· Un essai expérimental est mené par une instance d'un programme de simulation, que ce soit dans
parallèle ou en série.
· Un script de contrôle exécute des instances de la simulation, en faisant varier les paramètres si nécessaire.
· Les données sont collectées et stockées pour le traçage et l'analyse à l'aide de scripts externes et
outils existants.
· Les mesures au sein du noyau ns-3 sont prises en connectant le cadre stat à l'existant
traces de signaux.
· Des signaux de trace ou une manipulation directe du cadre peuvent être utilisés pour instrumenter
code de simulation.
Ces composants de base du cadre et leurs interactions sont décrits dans le
figure suivante. [image]
Exemple
Cette section décrit le processus de construction d'une expérience dans le cadre et
produire des données pour l'analyse (graphiques) à partir de celui-ci, démontrant la structure et l'API le long
le chemin.
Question
''Quelle est la performance (simulée) des NetDevices WiFi de ns-3 (en utilisant la valeur par défaut
réglages)? À quelle distance les nœuds sans fil peuvent-ils se trouver dans une simulation avant qu'ils ne puissent
communiquer de manière fiable ?''
· Hypothèse : sur la base de la connaissance des performances réelles, les nœuds doivent communiquer
raisonnablement bien à au moins 100m de distance. La communication au-delà de 200 m ne devrait pas être
réalisable.
Bien qu'il ne s'agisse pas d'une question très courante dans les contextes de simulation, il s'agit d'une propriété importante
dont les développeurs de simulation doivent avoir une compréhension de base. C'est aussi une commune
étude réalisée sur du matériel live.
Simulation Programme
La première chose à faire dans la mise en œuvre de cette expérience est de développer la simulation
programme. Le code de cet exemple se trouve dans exemples/stats/wifi-exemple-sim.cc.
Il effectue les principales étapes suivantes.
· Déclaration des paramètres et analyse de la ligne de commande à l'aide de ns3 :: Ligne de commande.
distance double = 50.0 ;
format de chaîne ("OMNet++");
expérience de chaîne ("wifi-distance-test");
stratégie de chaîne ("wifi-default");
chaîne runID ;
cmd de ligne de commande ;
cmd.AddValue("distance", "Distance pour placer les nœuds (en mètres).", distance);
cmd.AddValue("format", "Format à utiliser pour la sortie des données.", format);
cmd.AddValue("expérience", "Identifiant de l'expérience.", expérience);
cmd.AddValue("strategy", "Identifiant pour la stratégie.", strategy);
cmd.AddValue("run", "Identifiant pour run.", runID);
cmd.Parse (argc, argv);
· Création de nœuds et de piles de réseau à l'aide de ns3 :: NodeContainer, ns3 ::WiFiHelper et
ns3 :: InternetStackHelper.
nœuds NodeContainer ;
nœuds.Créer(2);
Wi-FiAide Wi-Fi ;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(nodes);
InternetStackHelper Internet ;
internet.Install(nœuds);
Ipv4AddressHelper ipAddrs ;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(nodeDevices);
· Positionnement des nœuds à l'aide ns3 :: MobilityHelper. Par défaut, les nœuds ont statique
mobilité et ne bougera pas, mais doit être positionné à la distance donnée. Il y a
plusieurs façons de le faire ; c'est fait ici en utilisant ns3 :: ListPositionAllocator, qui dessine
postes d'une liste donnée.
MobilityAide à la mobilité ;
Ptr positionAlloc =
CréerObjet ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, distance, 0.0));
mobilité.SetPositionAllocator(positionAlloc);
mobilité.Install(nœuds);
· Installation d'un générateur de trafic et d'un puits de trafic. Le stock Applications pourrait être
utilisé, mais l'exemple inclut des objets personnalisés dans src/test/test02-apps.(cc|h). Ces
ont un comportement simple, générant un nombre donné de paquets espacés à un intervalle donné.
Comme il n'y en a qu'un de chaque, ils sont installés manuellement ; pour un ensemble plus grand, le
ns3 ::ApplicationHelper classe pourrait être utilisée. Le commenté Configuration :: Définir changements de ligne
la destination des paquets, définie sur broadcast par défaut dans cet exemple. Notez que
en général, le WiFi peut avoir des performances différentes pour les trames de diffusion et de monodiffusion en raison de
différentes politiques de contrôle de débit et de retransmission MAC.
Ptr appSource = NodeList ::ObtenirNode(0);
Ptr expéditeur = CreateObject ();
appSource->AjouterApplication(expéditeur);
expéditeur->Démarrer(Sec(1));
Ptr appSink = NodeList ::ObtenirNode(1);
Ptr récepteur = CréerObjet ();
appSink->AddApplication (récepteur);
récepteur->Démarrer(Sec(0));
//Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
// Ipv4AddressValue("192.168.0.2"));
· Configuration des données et statistiques à collecter. Le paradigme de base est qu'un
ns3 :: Collecteur de données objet est créé pour contenir des informations sur cette exécution particulière, pour
auxquels les observateurs et les calculatrices sont attachés pour générer réellement des données. Surtout,
les informations d'exécution incluent des étiquettes pour l'''expérience'', la ''stratégie'', l'''entrée'' et
''Cours''. Ceux-ci sont utilisés pour identifier ultérieurement et regrouper facilement les données de plusieurs essais.
· L'expérience est l'étude dont cet essai fait partie. Le voici en Wi-Fi
performances et distances.
· La stratégie est le code ou les paramètres examinés dans cet essai. Dans cet exemple
c'est corrigé, mais une extension évidente serait d'enquêter sur différents bits WiFi
taux, dont chacun serait une stratégie différente.
· L'entrée est le problème particulier donné à cet essai. Ici c'est simplement le
distance entre les deux nœuds.
· Le runID est un identifiant unique pour cet essai avec lequel ses informations sont étiquetées
pour identification dans une analyse ultérieure. Si aucun ID d'exécution n'est donné, le programme d'exemple effectue
un ID d'exécution (faible) utilisant l'heure actuelle.
Ces quatre métadonnées sont nécessaires, mais d'autres peuvent être souhaitées. Ils peuvent être ajoutés
à l'enregistrement à l'aide de ns3 :: DataCollector :: AddMetadata () méthode.
Données DataCollector ;
data.DescribeRun (expérience, stratégie, entrée, runID);
data.AddMetadata("author", "tjkopena");
L'observation et le calcul réels sont effectués par ns3 :: DataCalculator objets, dont
plusieurs types différents existent. Ceux-ci sont créés par le programme de simulation, attaché à
code de déclaration ou d'échantillonnage, puis enregistré auprès du ns3 :: Collecteur de données alors ils vont
être interrogé plus tard pour leur sortie. Un mécanisme d'observation simple consiste à utiliser les
tracer des sources, par exemple pour instrumenter des objets dans le noyau ns-3 sans changer leur
code. Ici, un compteur est attaché directement à un signal de trace dans la couche WiFi MAC sur
le nœud cible.
Ptr totalRx = CréerObjet ();
totalRx->SetKey("wifi-rx-frames");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator ::FrameUpdate, totalRx));
data.AddDataCalculator(totalRx);
Les calculatrices peuvent également être manipulées directement. Dans cet exemple, un compteur est créé et
transmis à l'application de puits de trafic pour être mis à jour lorsque les paquets sont reçus.
Ptr > appRx = CréerObjet >();
appRx->SetKey("receiver-rx-packets");
récepteur-> SetCounter (appRx);
data.AddDataCalculator(appRx);
Pour incrémenter le compte, le code de traitement des paquets du puits appelle alors l'un des
méthodes de mise à jour de la calculatrice.
m_calc->Mise à jour();
Le programme comprend également plusieurs autres exemples, utilisant à la fois la primitive
calculatrices telles que ns3 :: CompteurCalculateur et ceux adaptés pour observer les paquets et
fois. Dans src/test/test02-apps.(cc|h) il crée également une simple balise personnalisée qu'il utilise
pour suivre le délai de bout en bout des paquets générés, en signalant les résultats à un
ns3 :: TimeMinMaxAvgTotalCalculator calculateur de données.
· Exécution de la simulation, qui est très simple une fois construite.
Simulateur ::Exécuter();
· Générer soit OMNet++ or SQLite sortie, en fonction des arguments de la ligne de commande. À
fais ça un ns3 :: DataOutputInterface objet est créé et configuré. Le genre spécifique
de cela déterminera le format de sortie. Cet objet reçoit alors la
ns3 :: Collecteur de données objet qu'il interroge pour produire la sortie.
Ptr production;
si (format == "OMNet++") {
NS_LOG_INFO("Création d'une sortie de données au format OMNet++.");
sortie = CréerObjet ();
} Else {
# ifdef STAT_USE_DB
NS_LOG_INFO("Création d'une sortie de données au format SQLite.");
sortie = CréerObjet ();
# fin si
}
sortie->Sortie(données);
· Libérer toute mémoire utilisée par la simulation. Cela devrait venir à la fin de la main
fonction pour l'exemple.
Simulateur :: Détruire ();
Journal
Pour voir ce que l'exemple de programme, les applications et le framework de statistiques font en détail, définissez
le NS_LOG variable de façon appropriée. Ce qui suit fournira une sortie abondante de tous
Trois.
$ export NS_LOG=WiFiDistanceExperiment :WiFiDistanceApps
Notez que cela ralentit considérablement la simulation.
Échantillon Sortie
Compiler et exécuter simplement le programme de test ajoutera OMNet++ sortie formatée telle que
la suite à données.sca.
courir courir-1212239121
attr expérience "wifi-distance-test"
stratégie attr "wifi-default"
entrée d'attribut "50"
attr description ""
attr "auteur" "tjkopena"
les trames wifi-tx scalaires comptent 30
les trames wifi-rx scalaires comptent 30
les paquets scalaires sender-tx comptent 30
les paquets receveurs scalaires comptent 30
scalaire tx-pkt-size count 30
scalaire tx-pkt-taille total 1920
scalaire tx-pkt-taille moyenne 64
scalaire tx-pkt-taille max 64
scalaire tx-pkt-taille min 64
compte de retard scalaire 30
retard scalaire total 5884980ns
délai scalaire moyen 196166ns
délai scalaire max 196166ns
délai scalaire min 196166ns
Contrôle scénario
Afin d'automatiser la collecte de données à une variété d'entrées (distances), un simple Bash
script est utilisé pour exécuter une série de simulations. Il peut être trouvé à
exemples/stats/wifi-example-db.sh. Le script est destiné à être exécuté à partir du exemples/statistiques/
répertoire.
Le script parcourt un ensemble de distances, rassemblant les résultats dans un SQLite
base de données. A chaque distance, cinq essais sont effectués pour donner une meilleure image de
performance. L'ensemble de l'expérience ne prend que quelques dizaines de secondes pour s'exécuter sur un bas de gamme
machine car il n'y a pas de sortie pendant la simulation et peu de trafic est généré.
#!/ Bin / sh
DISTANCES="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
ESSAIS="1 2 3 4 5"
echo WiFi Exemple d'expérience
si [ -e data.db ]
puis
echo Tuer data.db ?
lire ANS
si [ "$ANS" = "oui" -o "$ANS" = "y" ]
puis
echo Suppression de la base de données
rm data.db
fi
fi
pour essai dans $TRIALS
do
pour la distance en $DISTANCES
do
echo Essai $essai, distance $distance
./bin/test02 --format=db --distance=$distance --run=run-$distance-$essai
fait
fait
Analyse et Pour aller plus loin
Une fois tous les essais effectués, le script exécute une simple requête SQL sur le
base de données utilisant le SQLite programme en ligne de commande. La requête calcule la perte moyenne de paquets dans
chaque série d'essais associée à chaque distance. Il ne prend pas en compte les différents
stratégies, mais l'information est présente dans la base de données pour faire quelques extensions simples
et faites-le. Les données collectées sont ensuite transmises à GNUPlot pour être représentées graphiquement.
CMD="select exp.input,avg(100-((rx.value*100)/tx.value)) \
de Singletons rx, Singletons tx, Experiments exp \
où rx.run = tx.run ET \
rx.run = exp.run ET \
rx.name='récepteur-rx-paquets' ET \
tx.name='expéditeur-tx-paquets' \
grouper par exp.input \
trier par abs(exp.input) ASC ;"
sqlite3 -noheader data.db "$CMD" > wifi-default.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-exemple.gnuplot
Le script GNUPlot trouvé sur exemples/stats/wifi-example.gnuplot définit simplement la sortie
format et une mise en forme de base pour le graphique.
set terminal postscript portrait amélioré lw 2 "Helvetica" 14
définir la taille 1.0, 0.66
#-------------------------------------------------------------- ------
définir "wifi-default.eps"
#set title "Perte de paquets sur la distance"
set xlabel "Distance (m) --- moyenne de 5 essais par point"
définir xrange [0:200]
définir ylabel "% de perte de paquets"
définir la plage [0:110]
tracer "wifi-default.data" avec le titre des lignes "WiFi Defaults"
Fin Résultats
Le graphique résultant ne fournit aucune preuve que les performances du modèle Wi-Fi par défaut sont
nécessairement déraisonnable et donne une certaine confiance à une fidélité au moins symbolique à
réalité. Plus important encore, cette simple enquête a été menée tout au long
en utilisant le cadre statistique. Succès! [image]
Temps réél
ns-3 a été conçu pour être intégré dans des environnements de banc d'essai et de machines virtuelles. À
s'intégrer à de vraies piles de réseau et émettre/consommer des paquets, un ordonnanceur en temps réel est
nécessaire pour essayer de verrouiller l'horloge de simulation avec l'horloge matérielle. Nous décrivons ici une
composant de celui-ci : le planificateur RealTime.
Le but de l'ordonnanceur temps réel est de provoquer la progression de l'horloge de simulation
se produire de manière synchrone par rapport à une base de temps externe. Sans la présence de
une base de temps externe (horloge murale), le temps de simulation saute instantanément d'un temps simulé
temps à la prochaine.
Comportement
Lors de l'utilisation d'un ordonnanceur non temps réel (par défaut dans ns-3), le simulateur fait avancer le
temps de simulation jusqu'au prochain événement planifié. Pendant l'exécution de l'événement, le temps de simulation est
congelé. Avec le planificateur en temps réel, le comportement est similaire du point de vue de
modèles de simulation (c'est-à-dire que le temps de simulation est gelé pendant l'exécution de l'événement), mais entre
événements, le simulateur tentera de maintenir l'horloge de simulation alignée avec la machine
l'horloge.
Lorsqu'un événement est terminé et que le planificateur passe à l'événement suivant, le
le planificateur compare l'heure d'exécution de l'événement suivant avec l'horloge de la machine. Si la prochaine
événement est programmé pour une heure future, le simulateur dort jusqu'à ce que ce temps réel soit atteint
puis exécute l'événement suivant.
Il peut arriver que, du fait des traitements inhérents à l'exécution des événements de simulation,
que le simulateur ne peut pas suivre le temps réel. Dans ce cas, il appartient à l'utilisateur
configuration quoi faire. Il y en a deux ns-3 attributs qui régissent le comportement. La
le premier est ns3 :: RealTimeSimulatorImpl :: SynchronizationMode. Les deux entrées possibles pour
cet attribut sont Meilleur effort (la valeur par défaut) ou Limite dure. En mode "BestEffort", le
le simulateur essaiera simplement de rattraper le temps réel en exécutant des événements jusqu'à ce qu'il atteigne un
point où le prochain événement est dans le futur (en temps réel), sinon la simulation se termine. Dans
En mode BestEffort, il est alors possible que la simulation consomme plus de temps que le
l'heure de l'horloge murale. L'autre option "HardLimit" provoquera l'abandon de la simulation si le
seuil de tolérance est dépassé. Cet attribut est ns3 :: RealTimeSimulatorImpl :: HardLimit
et la valeur par défaut est de 0.1 seconde.
Un mode de fonctionnement différent est celui dans lequel le temps simulé est pas gelé lors d'un événement
exécution. Ce mode de simulation en temps réel a été implémenté mais retiré du ns-3 arbre
à cause des questions de savoir si cela serait utile. Si les utilisateurs sont intéressés par un temps réel
simulateur pour lequel le temps de simulation ne se fige pas pendant l'exécution de l'événement (c'est-à-dire
appeler pour Simulateur ::Maintenant() renvoie l'heure actuelle de l'horloge murale, et non l'heure à laquelle
l'événement a commencé à s'exécuter), veuillez contacter la liste de diffusion ns-developers.
Utilisation
L'utilisation du simulateur en temps réel est simple, du point de vue des scripts.
Les utilisateurs n'ont qu'à définir l'attribut Type d'implémentation du simulateur au temps réel
simulateur, comme suit :
GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl") );
Il y a un script dans exemples/realtime/realtime-udp-echo.cc qui a un exemple de comment
configurer le comportement en temps réel. Essayer:
$ ./waf --run temps réel-udp-echo
Le fonctionnement du simulateur dans le meilleur des cas ou dans le cadre d'une politique de limite stricte est régi
par les attributs expliqués dans la section précédente.
Implantation
L'implémentation est contenue dans les fichiers suivants :
· src/core/model/realtime-simulator-impl.{cc,h}
· src/core/model/wall-clock-synchronizer.{cc,h}
Afin de créer un ordonnanceur en temps réel, en première approximation, vous voulez simplement provoquer
le temps de simulation saute pour consommer du temps réel. Nous proposons de le faire en utilisant une combinaison de
sommeil- et occupé- attend. Sleep-waits fait que le processus appelant (thread) donne le
processeur pendant un certain temps. Même si ce laps de temps spécifié peut s'écouler
à une résolution nanoseconde, il est en fait converti en une granularité spécifique au système d'exploitation. Dans
Linux, la granularité s'appelle un Jiffy. Généralement, cette résolution est insuffisante pour
nos besoins (de l'ordre d'une dizaine de millisecondes), donc nous arrondissons et dormons pendant quelques
plus petit nombre de Jiffies. Le processus est alors réveillé après le nombre spécifié de
Jiffies est passé. En ce moment, nous avons un peu de temps résiduel à attendre. Cette fois est
généralement plus petit que le temps de sommeil minimum, nous attendons donc le reste de la
temps. Cela signifie que le thread se trouve juste dans une boucle for consommant des cycles jusqu'à ce que le
l'heure souhaitée arrive. Après la combinaison d'attentes de sommeil et d'occupation, le temps réel écoulé
L'horloge (murale) doit correspondre à l'heure de simulation du prochain événement et à la simulation
procède.
assistants
Les chapitres ci-dessus vous ont présenté divers ns-3 concepts de programmation tels que smart
des pointeurs pour la gestion de la mémoire comptée par référence, les attributs, les espaces de noms, les rappels, etc.
Les utilisateurs qui travaillent sur cette API de bas niveau peuvent s'interconnecter ns-3 objets à granularité fine.
Cependant, un programme de simulation écrit entièrement à l'aide de l'API de bas niveau serait assez long
et fastidieux à coder. Pour cette raison, une soi-disant "API d'assistance" distincte a été superposée
sur le noyau ns-3 API. Si vous avez lu le ns-3 tutoriel, vous serez déjà familier
avec l'API d'assistance, car c'est l'API à laquelle les nouveaux utilisateurs sont généralement présentés en premier.
Dans ce chapitre, nous introduisons la philosophie de conception de l'API d'assistance et la comparons à
l'API de bas niveau. Si vous devenez un gros consommateur de ns-3, vous ferez probablement des allers-retours
entre ces API même dans le même programme.
L'API d'assistance a plusieurs objectifs :
1. le reste de src / n'a aucune dépendance sur l'API d'assistance ; tout ce qu'on peut faire avec
l'API d'assistance peut également être codée à l'API de bas niveau
2. Conteneurs Souvent, les simulations devront faire un certain nombre d'actions identiques aux groupes
d'objets. L'API d'assistance fait un usage intensif de conteneurs d'objets similaires auxquels
des opérations similaires ou identiques peuvent être effectuées.
3. L'API d'assistance n'est pas générique ; il ne s'efforce pas de maximiser la réutilisation du code. Alors,
les constructions de programmation telles que le polymorphisme et les modèles qui permettent la réutilisation du code sont
pas aussi répandue. Par exemple, il existe des assistants CsmaNetDevice distincts et
Helpers PointToPointNetDevice mais ils ne dérivent pas d'une base NetDevice commune
classe.
4. L'API d'assistance fonctionne généralement avec des objets alloués par pile (vs. alloués par tas). Pour
certains programmes, ns-3 les utilisateurs peuvent ne pas avoir à se soucier de la création ou de la création d'objets de bas niveau.
Manipulation Ptr ; ils peuvent se contenter de conteneurs d'objets et d'assistants alloués par pile
qui opèrent sur eux.
L'API d'assistance consiste vraiment à créer ns-3 programmes plus faciles à écrire et à lire, sans
enlevant la puissance de l'interface de bas niveau. Le reste de ce chapitre fournit quelques
exemples de conventions de programmation de l'API d'assistance.
Fabrication Terrains en utilisant le GnuplotGenericName Classe
Il existe 2 méthodes courantes pour créer un tracé en utilisant ns-3 et gnuplot (‐
http://www.gnuplot.info):
1. Créez un fichier de contrôle gnuplot en utilisant ns-3est la classe Gnuplot.
2. Créez un fichier de données gnuplot en utilisant les valeurs générées par ns-3.
Cette section concerne la méthode 1, c'est-à-dire comment créer un tracé en utilisant ns-3de Gnuplot
classer. Si vous êtes intéressé par la méthode 2, consultez la sous-section "Un exemple réel" sous la rubrique
section "Traçage" dans le ns-3 Tutoriel.
La création Terrains En utilisant le GnuplotGenericName Classe
Les étapes suivantes doivent être suivies afin de créer un tracé à l'aide de ns-3La classe Gnuplot de :
1. Modifiez votre code afin qu'il utilise la classe Gnuplot et ses fonctions.
2. Exécutez votre code afin qu'il crée un fichier de contrôle gnuplot.
3. Appelez gnuplot avec le nom du fichier de contrôle gnuplot.
4. Affichez le fichier graphique qui a été produit dans votre visualiseur graphique préféré.
Voir le code des exemples de tracés qui sont discutés ci-dessous pour plus de détails sur l'étape 1.
An Exemple Programme qui Utilisations le GnuplotGenericName Classe
Un exemple de programme qui utilise ns-3La classe Gnuplot de se trouve ici :
src/stats/examples/gnuplot-example.cc
Pour exécuter cet exemple, procédez comme suit :
$ ./coque waf
$ cd build/debug/src/stats/exemples
$ ./gnuplot-exemple
Cela devrait produire les fichiers de contrôle gnuplot suivants dans le répertoire où l'exemple
est situé:
tracé-2d.plt
plot-2d-with-error-bars.plt
tracé-3d.plt
Pour traiter ces fichiers de contrôle gnuplot, procédez comme suit :
$ gnuplot trot-2d.plt
$ gnuplot plot-2d-with-error-bars.plt
$ gnuplot trot-3d.plt
Cela devrait produire les fichiers graphiques suivants dans le répertoire où se trouve l'exemple
situé:
plot-2d.png
plot-2d-with-error-bars.png
plot-3d.png
Vous pouvez afficher ces fichiers graphiques dans votre visualiseur graphique préféré. Si vous avez du gimp
installé sur votre machine, par exemple, vous pouvez faire ceci :
$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png
An Exemple 2 dimensions Terrain
Le tracé bidimensionnel suivant
[image]
a été créé en utilisant le code suivant de gnuplot-example.cc :
Utilisant l'espace de noms std;
string fileNameWithNoExtension = "plot-2d" ;
string graphicsFileName = fileNameWithNoExtension + ".png" ;
chaîne plotFileName = fileNameWithNoExtension + ".plt" ;
string plotTitle = "Graphique 2-D" ;
string dataTitle = "Données 2D" ;
// Instancie le tracé et définit son titre.
Tracé Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// Crée le fichier graphique, que le fichier de tracé créera lorsqu'il
// est utilisé avec Gnuplot, soit un fichier PNG.
plot.SetTerminal ("png");
// Définir les étiquettes pour chaque axe.
plot.SetLegend ("Valeurs X", "Valeurs Y");
// Définit la plage pour l'axe x.
plot.AppendExtra ("set xrange [-6:+6]");
// Instancie l'ensemble de données, définit son titre et fait en sorte que les points soient
// tracé avec les lignes de connexion.
Jeu de données Gnuplot2dDataset ;
ensemble de données.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset ::LINES_POINTS);
double X;
y double ;
// Crée le jeu de données 2-D.
pour (x = -5.0 ; x <= +5.0 ; x += 1.0)
{
// Calcule la courbe 2-D
//
// 2
// y = x .
//
y = x * x ;
// Ajoute ce point.
ensemble de données.Add (x, y);
}
// Ajoute le jeu de données au tracé.
plot.AddDataset (ensemble de données);
// Ouvre le fichier de tracé.
ofstream plotFile (plotFileName.c_str());
// Écrit le fichier de tracé.
plot.GenerateOutput (plotFile);
// Ferme le fichier de tracé.
plotFile.close ();
An Exemple 2 dimensions Terrain avec Erreur les bars
Le graphique bidimensionnel suivant avec des barres d'erreur dans les directions x et y
[image]
a été créé en utilisant le code suivant de gnuplot-example.cc :
Utilisant l'espace de noms std;
string fileNameWithNoExtension = "plot-2d-with-error-bars" ;
string graphicsFileName = fileNameWithNoExtension + ".png" ;
chaîne plotFileName = fileNameWithNoExtension + ".plt" ;
string plotTitle = "Graphique 2D avec barres d'erreur" ;
string dataTitle = "Données 2D avec barres d'erreur" ;
// Instancie le tracé et définit son titre.
Tracé Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// Crée le fichier graphique, que le fichier de tracé créera lorsqu'il
// est utilisé avec Gnuplot, soit un fichier PNG.
plot.SetTerminal ("png");
// Définir les étiquettes pour chaque axe.
plot.SetLegend ("Valeurs X", "Valeurs Y");
// Définit la plage pour l'axe x.
plot.AppendExtra ("set xrange [-6:+6]");
// Instancie l'ensemble de données, définit son titre et fait en sorte que les points soient
// tracé sans lignes de connexion.
Jeu de données Gnuplot2dDataset ;
ensemble de données.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset :: POINTS);
// Faire en sorte que l'ensemble de données ait des barres d'erreur dans les directions x et y.
dataset.SetErrorBars (Gnuplot2dDataset ::XY);
double X;
double xErrorDelta ;
y double ;
double yErrorDelta ;
// Crée le jeu de données 2-D.
pour (x = -5.0 ; x <= +5.0 ; x += 1.0)
{
// Calcule la courbe 2-D
//
// 2
// y = x .
//
y = x * x ;
// Rendre l'incertitude dans la direction x constante et faire
// l'incertitude dans la direction y soit une fraction constante de
// la valeur de y.
xErreurDelta = 0.25 ;
yErrorDelta = 0.1 * y ;
// Ajouter ce point avec des incertitudes à la fois sur le x et le y
// direction.
ensemble de données.Add (x, y, xErrorDelta, yErrorDelta);
}
// Ajoute le jeu de données au tracé.
plot.AddDataset (ensemble de données);
// Ouvre le fichier de tracé.
ofstream plotFile (plotFileName.c_str());
// Écrit le fichier de tracé.
plot.GenerateOutput (plotFile);
// Ferme le fichier de tracé.
plotFile.close ();
An Exemple 3 dimensions Terrain
Le tracé bidimensionnel suivant
[image]
a été créé en utilisant le code suivant de gnuplot-example.cc :
Utilisant l'espace de noms std;
string fileNameWithNoExtension = "plot-3d" ;
string graphicsFileName = fileNameWithNoExtension + ".png" ;
chaîne plotFileName = fileNameWithNoExtension + ".plt" ;
string plotTitle = "Graphique 3-D" ;
string dataTitle = "Données 3D" ;
// Instancie le tracé et définit son titre.
Tracé Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);
// Crée le fichier graphique, que le fichier de tracé créera lorsqu'il
// est utilisé avec Gnuplot, soit un fichier PNG.
plot.SetTerminal ("png");
// Faites pivoter le tracé de 30 degrés autour de l'axe x, puis faites pivoter le
// tracer 120 degrés autour du nouvel axe z.
plot.AppendExtra ("définir la vue 30, 120, 1.0, 1.0");
// Faire en sorte que le zéro de l'axe z soit dans le plan de l'axe x et de l'axe y.
plot.AppendExtra ("set ticslevel 0");
// Définir les étiquettes pour chaque axe.
plot.AppendExtra ("set xlabel 'X Values'");
plot.AppendExtra ("set ylabel 'Y Values'");
plot.AppendExtra ("set zlabel 'Z Values'");
// Définissez les plages pour les axes x et y.
plot.AppendExtra ("set xrange [-5:+5]");
plot.AppendExtra ("set yrange [-5:+5]");
// Instancie l'ensemble de données, définit son titre et fait en sorte que les points soient
// connecté par des lignes.
Jeu de données Gnuplot3dDataset ;
ensemble de données.SetTitle (dataTitle);
dataset.SetStyle ("avec des lignes");
double X;
y double ;
double z ;
// Crée le jeu de données 3-D.
pour (x = -5.0 ; x <= +5.0 ; x += 1.0)
{
pour (y = -5.0 ; y <= +5.0 ; y += 1.0)
{
// Calcule la surface 3D
//
// 2 2
// z = x * y .
//
z = X * X * y * y ;
// Ajoute ce point.
ensemble de données.Add (x, y, z);
}
// La ligne vide est nécessaire à la fin des données de chaque valeur x
// points pour que la grille de surface 3D fonctionne.
ensemble de données.AddEmptyLine ();
}
// Ajoute le jeu de données au tracé.
plot.AddDataset (ensemble de données);
// Ouvre le fichier de tracé.
ofstream plotFile (plotFileName.c_str());
// Écrit le fichier de tracé.
plot.GenerateOutput (plotFile);
// Ferme le fichier de tracé.
plotFile.close ();
En utilisant Python à Courir ns-3
Les liaisons Python autorisent le code C++ dans ns-3 être appelé depuis Python.
Ce chapitre vous montre comment créer un script Python qui peut s'exécuter ns-3 et aussi la
processus de création de liaisons Python pour un C++ ns-3 module.
Introduction
L'objectif des liaisons Python pour ns-3 sont de deux ordres :
1. Permettre au programmeur d'écrire des scripts de simulation complets en Python (‐
http://www.python.org);
2. Prototyper de nouveaux modèles (par exemple des protocoles de routage).
Pour le moment, l'objectif principal des fixations est le premier objectif, mais le second
l'objectif sera éventuellement soutenu également. Liaisons Python pour ns-3 sont en cours de développement
en utilisant un nouvel outil appelé PyBindGen (http://code.google.com/p/pybindgen).
An Exemple Python scénario qui Fonctionne ns-3
Voici un exemple de code écrit en Python et qui s'exécute ns-3, qui s'écrit
en C++. Cet exemple Python peut être trouvé dans exemples/tutoriel/first.py:
importer ns.applications
importer ns.core
importer ns.internet
importer ns.network
importer ns.point_to_point
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
nœuds = ns.network.NodeContainer()
nœuds.Créer(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Délai", ns.core.StringValue("2ms"))
appareils = pointToPoint.Install(nœuds)
pile = ns.internet.InternetStackHelper()
pile. Installer (nœuds)
adresse = ns.internet.Ipv4AddressHelper()
adresse.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
interfaces = address.Assign (dispositifs);
échoServeur = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(nœuds.Obtenir(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))
echoClient = ns.applications.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("TaillePaquet", ns.core.UintegerValue(1024))
clientApps = echoClient.Install(nœuds.Obtenir(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
Fonctionnement Python Scripts
waf contient des options qui mettent automatiquement à jour le chemin python pour trouver le ns3
module. Pour exécuter des exemples de programmes, il existe deux façons d'utiliser waf pour s'en occuper. Une
est d'exécuter un shell waf ; par exemple:
$ ./waf --shell
$ exemples python/sans fil/mixed-wireless.py
et l'autre consiste à utiliser l'option --pyrun pour waf :
$ ./waf --pyrun examples/wireless/mixed-wireless.py
Pour exécuter un script Python sous le débogueur C :
$ ./waf --shell
$ gdb --args exemples python/sans fil/mixed-wireless.py
Pour exécuter votre propre script Python qui appelle ns-3 et qui a ce chemin,
/chemin/vers/votre/exemple/mon-script.py, Procédez comme suit:
$ ./waf --shell
$ python /chemin/vers/votre/exemple/mon-script.py
Avertissements
Liaisons Python pour ns-3 sont un travail en cours, et certaines limites sont connues par
développeurs. Certaines de ces limitations (pas toutes) sont listées ici.
Couverture Couverture
Tout d'abord, gardez à l'esprit que 100 % de l'API n'est pas prise en charge en Python. Certains
les raisons sont :
1. certaines API impliquent des pointeurs, qui nécessitent de connaître le type de mémoire
sémantique de passage (qui possède quelle mémoire). Une telle connaissance ne fait pas partie de la fonction
signatures, et est documenté ou parfois même non documenté. Les annotations sont
nécessaire pour lier ces fonctions ;
2. Parfois, un type de données fondamental inhabituel ou une construction C++ est utilisé qui n'est pas encore
pris en charge par PyBindGen ;
3. GCC-XML ne signale pas les classes basées sur des modèles à moins qu'elles ne soient instanciées.
La plupart des API manquantes peuvent être encapsulées, avec suffisamment de temps, de patience et d'expertise, et
sera probablement encapsulé si des rapports de bogue sont soumis. Cependant, ne remplissez pas de rapport de bogue
disant "les reliures sont incomplètes", car nous n'avons pas la main d'oeuvre pour réaliser 100% des
reliures.
Conversion Constructeurs
Conversion constructeurs ne sont pas encore entièrement pris en charge par PyBindGen, et ils agissent toujours comme
constructeurs explicites lors de la traduction d'une API en Python. Par exemple, en C++, vous pouvez faire
ce:
Ipv4AddressHelper ipAddrs ;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
En Python, pour le moment il faut faire :
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign (backboneDevices)
Ligne de commande
LigneCommande::AjouterValeur() fonctionne différemment en Python qu'en ns-3. En Python, le
Le premier paramètre est une chaîne qui représente le nom de l'option de ligne de commande. Lorsque l'option
est défini, un attribut portant le même nom que le nom de l'option est défini sur le Ligne de commande()
objet. Exemple:
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.commandline ()
cmd.NumNodesSide = Aucun
cmd.AddValue("NumNodesSide", "Nombre de nœuds côté grille (le nombre total de nœuds sera ce nombre au carré)")
cmd.Parse(argv)
[...]
si cmd.NumNodesSide est Aucun :
num_nodes_side = NUM_NODES_SIDE_DEFAULT
autre:
num_nodes_side = int(cmd.NumNodesSide)
Traçant
Le traçage basé sur le rappel n'est pas encore correctement pris en charge pour Python, comme nouveau ns-3 L'API doit
être fourni pour que cela soit pris en charge.
L'écriture de fichiers Pcap est prise en charge via l'API normale.
Le traçage Ascii est pris en charge depuis ns-3.4 via l'API C++ normale traduite en Python.
Cependant, le traçage ascii nécessite la création d'un objet ostream pour passer dans l'ascii
méthodes de traçage. En Python, le C++ std :: ofstream a été encapsulé au minimum pour permettre
cette. Par exemple:
ascii = ns3.ofstream("wifi-ap.tr") # créer le fichier
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # ferme le fichier
Il y a une mise en garde : vous ne devez pas autoriser le ramasse-miettes de l'objet fichier pendant que ns-3
l'utilise encore. Cela signifie que la variable 'ascii' ci-dessus ne doit pas être autorisée à aller
hors de portée ou bien le programme plantera.
Cygwin limitation
Les liaisons Python ne fonctionnent pas sur Cygwin. Cela est dû à un bogue gccxml.
Vous pouvez vous en tirer en ré-analysant les définitions d'API depuis le cygwin
environnement (./waf --python-scan). Cependant, la solution la plus probable devra probablement
être que nous désactivons les liaisons python dans CygWin.
Si vous vous souciez vraiment des liaisons Python sous Windows, essayez de construire avec mingw et native
python à la place. Ou bien, pour construire sans liaisons python, désactivez les liaisons python dans le
étape de paramétrage :
$ ./waf configurer --disable-python
Working : un espace de travail commun avec Python fixations
Il existe actuellement deux types de liaisons Python dans ns-3:
1. Les liaisons monolithiques contiennent des définitions d'API pour tous les modules et peuvent être trouvées dans
un répertoire unique, fixations/python.
2. Les liaisons modulaires contiennent des définitions d'API pour un seul module et peuvent être trouvées dans chaque
modules fixations répertoire.
Python fixations Workflow
Le processus par lequel les liaisons Python sont gérées est le suivant :
1. Périodiquement, un développeur utilise un GCC-XML (http://www.gccxml.org) Analyse d'API basée sur
script, qui enregistre la définition d'API analysée sous liaisons/python/ns3_module_*.py fichiers
ou sous forme de fichiers Python dans chaque module' fixations annuaire. Ces fichiers sont conservés sous
contrôle de version dans l'ensemble ns-3 dépôt;
2. D'autres développeurs clonent le référentiel et utilisent les définitions d'API déjà analysées ;
3. Lors de la configuration ns-3, pybindgen sera automatiquement téléchargé si ce n'est déjà fait
installée. Publié ns-3 tarballs enverra une copie de pybindgen.
Si quelque chose ne va pas avec la compilation des liaisons Python et que vous voulez simplement les ignorer
et passez à C++, vous pouvez désactiver Python avec :
$ ./waf --disable-python
Instructions pour Maniabilité Équipement Fichiers or Changé Apis
Donc, vous avez changé existant ns-3 Les API et les liaisons Python ne se compilent plus ? Fais
ne désespérez pas, vous pouvez réanalyser les liaisons pour créer de nouvelles liaisons qui reflètent les modifications
à la ns-3 API.
Selon que vous utilisez des fixations monolithiques ou modulaires, consultez les discussions ci-dessous pour
apprenez à rescanner vos liaisons Python.
Monolithique Python fixations
Balayage le Monolithique Python fixations
Pour analyser les liaisons Python monolithiques, procédez comme suit :
$ ./waf --python-scan
Nom de l'entreprise of le Monolithique Python fixations
Les définitions monolithiques de l'API Python sont organisées comme suit. Pour chaque ns-3 module
, le fichier liaisons/python/ns3_module_ .py décrit son API. Chacun de ceux
les fichiers ont 3 fonctions de niveau supérieur :
1. def registre_types(module)(): cette fonction se charge d'enregistrer de nouveaux types (ex.
classes C++, énumérations) définies dans ce module ;
2. def registre_méthodes(module)(): cette fonction appelle, pour chaque classe , une autre
fonction register_methods_Ns3 (module). Ces dernières fonctions ajoutent la méthode
définitions pour chaque classe;
3. def registre_fonctions(module)(): cette fonction enregistre ns-3 fonctions appartenant à
ce module.
Modulaire Python fixations
Aperçu
Depuis ns 3.11, les fixations modulaires sont ajoutées, en parallèle à l'ancien monolithique
reliures.
Les nouvelles liaisons python sont générées dans un espace de noms 'ns', au lieu de 'ns3' pour l'ancien
reliures. Exemple:
à partir du nœud d'importation ns.network
n1 = Nœud()
Avec les liaisons Python modulaires :
1. Il existe un module d'extension Python distinct pour chaque ns-3 module;
2. L'analyse des définitions d'API (apidefs) est effectuée par ns-module ;
3. Les fichiers apidefs de chaque module sont stockés dans un sous-répertoire 'bindings' du module
annuaire;
Balayage le Modulaire Python fixations
Pour analyser les liaisons Python modulaires pour le module principal, par exemple, procédez comme suit :
$ ./waf --apiscan=core
Pour analyser les liaisons Python modulaires pour tous les modules, procédez comme suit :
$ ./waf --apiscan=tout
La création a Équipement Module
Si vous ajoutez un nouveau module, les liaisons Python continueront à se compiler mais pas
couvrir le nouveau module.
Pour couvrir un nouveau module, vous devez créer un liaisons/python/ns3_module_ .py fichier,
similaire à ce qui est décrit dans les sections précédentes, et enregistrez-le dans la variable
LOCAL_MODULES() in liaisons/python/ns3modulegen.py
L'ajout de Modulaire fixations À A Existant Module
Pour ajouter la prise en charge des liaisons modulaires à un ns-3 module, ajoutez simplement ce qui suit
ligne à sa fonction wscript build() :
bld.ns3_python_bindings()
Nom de l'entreprise of le Modulaire Python fixations
La src/ /fixations répertoire peut contenir les fichiers suivants, certains d'entre eux
optionnel:
· callbacks_list.py: ceci est un fichier scanné, NE PAS TOUCHER. Contient une liste de
Instances de modèle Callback<...> trouvées dans les en-têtes analysés ;
· modulegen__gcc_LP64.py: ceci est un fichier scanné, NE PAS TOUCHER. Définitions d'API analysées
pour l'architecture GCC, LP64 (64 bits)
· modulegen__gcc_ILP32.py: ceci est un fichier scanné, NE PAS TOUCHER. Définitions d'API analysées
pour GCC, architecture ILP32 (32 bits)
· modulegen_customizations.py: vous pouvez éventuellement ajouter ce fichier afin de personnaliser le
génération de code pybindgen
· scan-header.h: vous pouvez éventuellement ajouter ce fichier pour personnaliser le fichier d'en-tête analysé
pour le module. Fondamentalement, ce fichier est analysé au lieu de ns3/ -module.h.
Généralement, la première instruction est #include "ns3/ -module.h", plus quelques autres
trucs pour forcer les instanciations de modèles ;
· module_helpers.cc: vous pouvez ajouter des fichiers supplémentaires, comme celui-ci, à lier à python
module d'extension, mais ils doivent être enregistrés dans le wscript. Regarder
src/core/wscript pour un exemple de comment procéder ;
· .py: si ce fichier existe, il devient le module python "frontend" pour le ns3
module, et le module d'extension (fichier .so) devient _ .so au lieu de .alors.
La Le fichier .py doit importer tous les symboles du module _ (c'est plus
plus délicat qu'il n'y paraît, voir src/core/bindings/core.py pour un exemple), puis peut ajouter
quelques définitions supplémentaires en python pur.
Plus Info pour Développeurs
Si vous êtes un développeur et avez besoin de plus d'informations sur ns-3pour les liaisons Python, veuillez consulter le
Python fixations wiki page.
Tests
Aperçu
Ce document concerne les essais et la validation des ns-3 logiciel.
Ce document fournit
· arrière-plan sur la terminologie et les tests de logiciels (chapitre 2) ;
· une description du cadre de test ns-3 (chapitre 3);
· un guide pour les développeurs de modèles ou les nouveaux contributeurs de modèles sur la façon d'écrire des tests (chapitre
4);
En bref, les trois premiers chapitres devraient être lus par les développeurs et contributeurs de ns qui
besoin de comprendre comment contribuer au code de test et aux programmes validés, et le reste
du document fournit un espace permettant aux personnes de signaler les aspects des modèles sélectionnés
ont été validés.
Biographie
Cette chapitre Au cours de cette réunion, Matthew a obtenu de précieux conseils et Linda lui a demandé de la tenir au courant de ses progrès. be sauté by lecteurs familier avec le bases of software test.
Écrire un logiciel sans défaut est une proposition difficile. Il y a plusieurs dimensions à la
problème et il y a beaucoup de confusion quant à ce que l'on entend par différents termes dans
contextes différents. Nous avons trouvé utile de consacrer un peu de temps à l'examen de
sujet et définissant certains termes.
Le test logiciel peut être vaguement défini comme le processus d'exécution d'un programme avec le
l'intention de trouver des erreurs. Quand on entre dans une discussion concernant les tests de logiciels, il
devient rapidement évident qu'il existe de nombreux états d'esprit distincts avec lesquels on peut
aborder le sujet.
Par exemple, on pourrait diviser le processus en grandes catégories fonctionnelles comme
''test d'exactitude'' ''test de performance'' ''test de robustesse'' et ''test de sécurité
test.'' Une autre façon d'aborder le problème est par cycle de vie : ''test des exigences,''
''tests de conception'', ''tests d'acceptation'' et ''tests de maintenance''.
est par la portée du système testé. Dans ce cas on peut parler de ''tests unitaires'',
''test de composants'', ''test d'intégration'' et ''test de système''. Ces termes sont
également pas standardisé en aucune façon, et donc '' tests de maintenance '' et '' régression
testing'' peut être entendu de manière interchangeable. De plus, ces termes sont souvent mal utilisés.
Il existe également un certain nombre d'approches philosophiques différentes pour les tests de logiciels. Pour
Par exemple, certaines organisations préconisent d'écrire des programmes de test avant de mettre en œuvre
le logiciel souhaité, ce qui donne un "développement piloté par les tests". Certaines organisations préconisent
test du point de vue du client dès que possible, suivant un parallèle avec le
processus de développement agile : ''tester tôt et tester souvent.'' Ceci est parfois appelé
'' tests agiles ''. Il semble qu'il existe au moins une approche de test pour chaque
méthodologie de développement.
La ns-3 projet n'a pas pour but de défendre l'un de ces processus, mais
le projet dans son ensemble a des exigences qui aident à éclairer le processus de test.
Comme tous les principaux produits logiciels, ns-3 a un certain nombre de qualités qui doivent être présentes pour
le produit pour réussir. Du point de vue des tests, certaines de ces qualités qui doivent être
abordés sont que ns-3 doit être ''correct'' ''robuste'' ''performant'' et
''maintenable.'' Idéalement, il devrait y avoir des métriques pour chacune de ces dimensions qui sont
vérifié par les tests pour identifier quand le produit ne répond pas à ses attentes /
exigences.
Correction
Le but essentiel des tests est de déterminer qu'un logiciel se comporte
''correctement.'' Pour ns-3 cela signifie que si nous simulons quelque chose, la simulation devrait
représenter fidèlement une entité physique ou un processus avec une précision spécifiée et
précision.
Il s'avère qu'il existe deux perspectives à partir desquelles on peut voir la justesse.
Vérifier qu'un modèle particulier est implémenté conformément à sa spécification est
génériquement appelé vérification. Le processus de décision que le modèle est correct pour
son utilisation prévue est appelée génériquement validation.
Validation et Vérification
Un modèle informatique est une représentation mathématique ou logique de quelque chose. Ça peut
représentent un véhicule, un éléphant (voir David de Harel parler à propos modélisation an éléphant at
SIMUOutils 2009, ou une carte réseau. Les modèles peuvent également représenter des processus tels que
réchauffement climatique, flux de trafic autoroutier ou spécification d'un protocole de réseau. Les modèles peuvent être
représentations complètement fidèles d'une spécification de processus logique, mais elles
ne peut nécessairement jamais simuler complètement un objet ou un processus physique. Dans la plupart des cas, un
nombre de simplifications sont apportées au modèle pour effectuer la simulation par calcul
docile.
Chaque modèle a un l'objectif Système qu'il tente de simuler. La première étape dans
créer un modèle de simulation consiste à identifier ce système cible et le niveau de détail et
précision que la simulation est censée reproduire. Dans le cas d'un processus logique,
le système cible peut être identifié comme ''TCP tel que défini par la RFC 793.'' Dans ce cas, il
sera probablement souhaitable de créer un modèle reproduisant complètement et fidèlement RFC
793. Dans le cas d'un processus physique, cela ne sera pas possible. Si, par exemple, vous
voudriez simuler une carte réseau sans fil, vous pouvez déterminer que vous avez besoin, '' d'un
mise en œuvre précise au niveau MAC de la spécification 802.11 et [...] un pas si lent
Modèle de niveau PHY de la spécification 802.11a.''
Une fois cela fait, on peut développer un modèle abstrait du système cible. C'est
généralement un exercice de gestion des compromis entre la complexité, les besoins en ressources
et précision. Le processus de développement d'un modèle abstrait a été appelé modèle
qualification dans la littérature. Dans le cas d'un protocole TCP, ce processus se traduit par un
concevoir une collection d'objets, d'interactions et de comportements qui mettront pleinement en œuvre
RFC 793 dans ns-3. Dans le cas de la carte sans fil, ce processus entraîne un certain nombre de
compromis pour permettre la simulation de la couche physique et la conception d'un équipement réseau
et le canal pour ns-3, ainsi que les objets, interactions et comportements souhaités.
Ce modèle abstrait est ensuite développé en un ns-3 modèle qui implémente l'abstrait
modèle en tant que programme informatique. Le processus d'obtention de l'accord entre la mise en œuvre et le
le modèle abstrait est appelé modèle vérification dans la littérature.
Jusqu'à présent, le processus est en boucle ouverte. Il reste à déterminer qu'un ns-3 donné
modèle a un lien avec une réalité - qu'un modèle est une représentation précise de
un système réel, qu'il s'agisse d'un processus logique ou d'une entité physique.
Si l'on va utiliser un modèle de simulation pour essayer de prédire comment un système réel va
pour se comporter, il doit y avoir une raison de croire vos résultats - c'est-à-dire, peut-on croire que
une inférence faite à partir du modèle se traduit par une prédiction correcte pour le système réel.
Le processus d'obtention du comportement du modèle ns-3 pour s'accorder avec le système cible souhaité
comportement tel que défini par le processus de qualification du modèle est appelé modèle validation dans le
Littérature. Dans le cas d'une implémentation TCP, vous pouvez comparer le comportement de
votre modèle TCP ns-3 à une implémentation de référence afin de valider votre modèle. Dans
Dans le cas d'une simulation de couche physique sans fil, vous pouvez comparer le comportement de
votre modèle à celui d'un matériel réel dans un cadre maîtrisé,
La ns-3 environnement de test fournit des outils permettant à la fois la validation du modèle et
essais et encourage la publication des résultats de validation.
La solidité des mécanismes :
La robustesse est la qualité de pouvoir résister aux contraintes, ou aux changements d'environnements,
entrées ou calculs, etc. Un système ou une conception est « robuste » s'il peut
changements avec une perte minimale de fonctionnalité.
Ce type de test est généralement effectué avec une attention particulière. Par exemple, le système comme
un ensemble peut être exécuté sur de nombreuses configurations système différentes pour démontrer qu'il peut
fonctionner correctement dans un grand nombre d'environnements.
Le système peut également être stressé en fonctionnant à proximité ou au-delà de sa capacité en générant
ou simulant l'épuisement des ressources de divers types. Ce genre de test est appelé
''test de résistance.''
Le système et ses composants peuvent être exposés à ce que l'on appelle des « tests propres » qui démontrent
un résultat positif - c'est-à-dire que le système fonctionne correctement en réponse à une grande
variation des configurations attendues.
Le système et ses composants peuvent également être exposés à des ''tests sales'' qui fournissent des entrées
en dehors de la plage attendue. Par exemple, si un module attend une chaîne terminée par zéro
représentation d'un entier, un test sale peut fournir une chaîne non terminée de nombres aléatoires
caractères pour vérifier que le système ne plante pas à la suite de cette entrée inattendue.
Malheureusement, la détection de telles entrées « sales » et la prise de mesures préventives pour garantir la
système ne tombe pas en panne de manière catastrophique peut nécessiter une énorme quantité de frais généraux de développement.
Afin de réduire le temps de développement, il a été décidé très tôt dans le projet de
minimiser la quantité de validation des paramètres et de gestion des erreurs dans le ns-3 base de code. Pour
pour cette raison, nous ne consacrons pas beaucoup de temps aux tests sales - cela ne ferait que découvrir le
résultats de la décision de conception que nous savons avoir prise.
Nous voulons démontrer que ns-3 le logiciel fonctionne dans un certain ensemble de conditions. Nous
emprunter quelques définitions pour affiner un peu cela. La domaine of applicabilité is
un ensemble de conditions prescrites pour lesquelles le modèle a été testé, comparé à
réalité dans la mesure du possible, et jugées utilisables. La gamme of précision est un
accord entre le modèle informatisé et la réalité dans un domaine d'applicabilité.
La ns-3 environnement de test fournit des outils permettant de configurer et d'exécuter des tests
environnements sur plusieurs systèmes (buildbot) et fournit des classes pour encourager le nettoyage
tests pour vérifier le fonctionnement du système sur le ''domaine d'applicabilité'' attendu
et ''gamme de précision.''
performant
D'accord, '' performant '' n'est pas un vrai mot anglais. C'est pourtant un néologisme très concis
qui est assez souvent utilisé pour décrire ce que nous voulons ns-3 être : suffisamment puissant et rapide pour
finis le travail.
Il s'agit vraiment du vaste sujet des tests de performance des logiciels. Une des clés
ce qui se fait est de comparer deux systèmes pour trouver lequel est le plus performant (cf.
repères). Ceci est utilisé pour démontrer que, par exemple, ns-3 peut effectuer un type de base
de simulation au moins aussi rapide qu'un outil concurrent, ou peut être utilisé pour identifier des parties de
le système qui fonctionne mal.
Dans le ns-3 cadre de test, nous fournissons un support pour chronométrer différents types de tests.
Consommabilité
Un produit logiciel doit être maintenable. Il s'agit, encore une fois, d'une déclaration très large, mais
cadre de test peut aider à la tâche. Une fois qu'un modèle a été développé, validé et
vérifié, nous pouvons exécuter à plusieurs reprises la suite de tests pour l'ensemble du système afin de garantir
qu'il reste valide et vérifié tout au long de sa vie.
Lorsqu'une fonctionnalité cesse de fonctionner comme prévu après qu'un certain type de modification du système a été
intégré, il est appelé génériquement un régression. À l'origine, le terme régression
fait référence à un changement qui a fait réapparaître un bogue précédemment corrigé, mais le terme a
évolué pour décrire tout type de changement qui casse les fonctionnalités existantes. Il y a beaucoup de
types de régressions qui peuvent se produire dans la pratique.
A locales régression est celui dans lequel un changement affecte directement le composant modifié. Pour
exemple, si un composant est modifié pour allouer et libérer de la mémoire mais que des pointeurs obsolètes sont
utilisé, le composant lui-même tombe en panne.
A éloigné régression est celui dans lequel une modification apportée à un composant interrompt la fonctionnalité dans
un autre composant. Cela reflète la violation d'un engagement implicite mais peut-être non reconnu
contrat entre composants.
An démasqué régression est celui qui crée une situation où un bogue existant précédemment
qui n'avait aucun effet est soudainement exposé dans le système. Cela peut être aussi simple que de faire de l'exercice
un chemin de code pour la première fois.
A performant régression est celui qui fait que les exigences de performance du système
être violé. Par exemple, faire du travail dans une fonction de bas niveau qui peut être répétée
un grand nombre de fois peut soudainement rendre le système inutilisable sous certains angles.
La ns-3 framework de test fournit des outils pour automatiser le processus utilisé pour valider et
vérifier le code dans des suites de tests nocturnes pour aider à identifier rapidement les régressions possibles.
Contrôle de qualité cadre
ns-3 se compose d'un moteur central de simulation, d'un ensemble de modèles, d'exemples de programmes et de tests.
Au fil du temps, de nouveaux contributeurs apportent des modèles, des tests et des exemples. Un programme de test Python
test.py sert de gestionnaire d'exécution des tests ; test.py peut exécuter du code de test et des exemples pour
rechercher des régressions, peut afficher les résultats dans un certain nombre de formulaires et peut gérer le code
outils d'analyse de la couverture. En plus de cela, nous superposons Robots de construction qui sont construits automatiquement
des robots qui effectuent des tests de robustesse en exécutant le cadre de test sur différents systèmes
et avec différentes options de configuration.
Construire des robots
Au plus haut niveau de test ns-3 se trouvent les buildbots (robots de construction). Si vous êtes
je ne connais pas ce système regarde http://djmitche.github.com/buildbot/docs/0.7.11/.
Il s'agit d'un système automatisé open-source qui permet ns-3 à reconstruire et à tester chaque
fois que quelque chose a changé. En exécutant les buildbots sur un certain nombre de systèmes différents, nous
peut garantir que ns-3 se construit et s'exécute correctement sur tous ses systèmes pris en charge.
Les utilisateurs (et les développeurs) n'interagiront généralement pas avec le système buildbot autrement que pour
lire ses messages concernant les résultats des tests. Si une panne est détectée dans l'un des
tâches de construction et de test automatisées, le buildbot enverra un e-mail au ns-développeurs
liste de diffusion. Cet e-mail ressemblera à quelque chose comme
Dans l'URL complète des détails affichés dans l'e-mail, on peut rechercher le mot-clé manqué et
sélectionnez le stdio lien vers l'étape correspondante pour voir la raison de l'échec.
Le buildbot fera son travail tranquillement s'il n'y a pas d'erreurs, et le système subira
construisez et testez des cycles tous les jours pour vérifier que tout va bien.
Test.py
Les buildbots utilisent un programme Python, test.py, qui est responsable de l'exécution de tous les
tests et la collecte des rapports résultants sous une forme lisible par l'homme. Ce programme est
également disponible pour une utilisation par les utilisateurs et les développeurs.
test.py est très flexible en permettant à l'utilisateur de spécifier le nombre et le type de tests à
Cours; ainsi que la quantité et le type de sortie à générer.
Avant de courir test.py, assurez-vous que les exemples et les tests de ns3 ont été construits en faisant
le suivant
$ ./waf configurer --enable-examples --enable-tests
$ ./waf
Par défaut, test.py exécutera tous les tests disponibles et rendra compte de l'état de manière très concise
formulaire. Exécution de la commande
$ ./test.py
entraînera un certain nombre de PASS, FAIL, CRASH or SKIP indications suivies du type de
test qui a été exécuté et son nom d'affichage.
Waf : Entrer dans le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf : quitter le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' terminé avec succès (0.939s)
ÉCHEC : TestSuite ns3-wifi-propagation-loss-models
PASS : TestSuite nom-objet-service
PASS : TestSuite pcap-file-object
RÉUSSITE : TestSuite ns3-tcp-cwnd
PASS: TestSuite ns3-tcp-interopérabilité
PASS : Exemple de diffusion csma
PASS : Exemple csma-multidiffusion
Ce mode est destiné à être utilisé par les utilisateurs qui souhaitent déterminer si leur
distribution fonctionne correctement, et par les développeurs qui souhaitent déterminer si
les changements qu'ils ont apportés n'ont causé aucune régression.
Plusieurs options sont disponibles pour contrôler le comportement de test.py. si tu cours
test.py --Aidez-moi vous devriez voir un résumé de la commande comme :
Utilisation : test.py [options]
Options:
-h, --help affiche ce message d'aide et quitte
-b CHEMIN DE CONSTRUCTION, --buildpath=CHEMIN DE CONSTRUCTION
spécifiez le chemin où ns-3 a été construit (par défaut le
répertoire de construction pour la variante actuelle)
-c GENRE, --constrain=GENRE
contraindre le testeur par type de test
-e EXEMPLE, --exemple=EXEMPLE
spécifier un seul exemple à exécuter (aucun chemin relatif n'est
nécessaire)
-g, --grind exécuter les suites de tests et les exemples en utilisant valgrind
-k, --kinds affiche les types de tests disponibles
-l, --list affiche la liste des tests connus
-m, --multiple signale plusieurs échecs à partir des suites de tests et teste
cas
-n, --nowaf ne pas exécuter waf avant de commencer les tests
-p PYEXEMPLE, --pyexample=PYEXAMPLE
spécifier un seul exemple python à exécuter (avec relative
Chemin)
-r, --retain conserver tous les fichiers temporaires (qui sont normalement
supprimé)
-s SUITE-TEST, --suite=SUITE-TEST
spécifier une seule suite de tests à exécuter
-t FICHIER-TEXTE, --text=FICHIER-TEXTE
écrire des résultats de test détaillés dans TEXT-FILE.txt
-v, --verbose progression de l'impression et messages d'information
-w FICHIER-HTML, --web=FICHIER-HTML, --html=FICHIER-HTML
écrire des résultats de test détaillés dans HTML-FILE.html
-x FICHIER-XML, --xml=FICHIER-XML
écrire des résultats de test détaillés dans XML-FILE.xml
Si l'on spécifie un style de sortie facultatif, on peut générer des descriptions détaillées du
tests et état. Les styles disponibles sont texte et HTML. Les buildbots sélectionneront le HTML
option pour générer des rapports de test HTML pour les versions nocturnes à l'aide
$ ./test.py --html=nightly.html
Dans ce cas, un fichier HTML nommé ''nightly.html'' serait créé avec un joli résumé
des tests effectués. Un format ''lisible par l'homme'' est disponible pour les utilisateurs intéressés par le
détails.
$ ./test.py --text=resultats.txt
Dans l'exemple ci-dessus, la suite de tests vérifiant les ns-3 perte de propagation de l'appareil sans fil
les modèles ont échoué. Par défaut, aucune autre information n'est fournie.
Pour explorer davantage l'échec, test.py permet de spécifier une seule suite de tests.
Exécuter la commande
$ ./test.py --suite=ns3-wifi-propagation-loss-models
ou équivalent
$ ./test.py -s ns3-wifi-propagation-loss-models
entraîne l'exécution de cette seule suite de tests.
ÉCHEC : TestSuite ns3-wifi-propagation-loss-models
Pour trouver des informations détaillées sur la panne, il faut spécifier le type de sortie
voulu. Par exemple, la plupart des gens seront probablement intéressés par un fichier texte :
$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
Cela entraînera l'exécution de cette suite de tests unique avec l'état du test écrit dans le
fichier ''results.txt''.
Vous devriez trouver quelque chose de similaire à ce qui suit dans ce fichier
ÉCHEC : Test Suite ''ns3-wifi-propagation-loss-models'' (réel 0.02 utilisateur 0.01 système 0.00)
PASS: Test Case "Check ... Friis ... model ..." (réel 0.01 utilisateur 0.00 système 0.00)
FAIL: Test Case "Check ... Log Distance ... model" (réel 0.01 utilisateur 0.01 système 0.00)
Détails:
Message : Vous avez obtenu une valeur SNR inattendue
Condition : [longue description de ce qui a réellement échoué]
Courant: 176.395
Limite : 176.407 +- 0.0005
Fichier : ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Ligne: 360
Notez que la suite de tests est composée de deux cas de test. Le premier cas de test a vérifié la
Modèle de perte de propagation de Friis et passé. Le deuxième cas de test n'a pas vérifié le journal
Modèle de propagation à distance. Dans ce cas, un SNR de 176.395 a été trouvé, et le test
attendu une valeur de 176.407 correcte à trois décimales. Le dossier qui a mis en place
le test défaillant est répertorié ainsi que la ligne de code qui a déclenché l'échec.
Si vous le désirez, vous pourriez tout aussi bien avoir écrit un fichier HTML en utilisant le --html option
comme décrit ci-dessus.
Généralement, un utilisateur exécutera tous les tests au moins une fois après le téléchargement ns-3 pour être sur de
son environnement a été construit correctement et génère des résultats corrects
selon les suites de tests. Les développeurs exécuteront généralement les suites de tests avant et
après avoir effectué une modification pour s'assurer qu'ils n'ont pas introduit de régression avec leur
changements. Dans ce cas, les développeurs peuvent ne pas vouloir exécuter tous les tests, mais seulement un sous-ensemble. Pour
Par exemple, le développeur peut ne vouloir exécuter les tests unitaires que périodiquement lors de la création
modifications apportées à un référentiel. Dans ce cas, test.py on peut dire de contraindre les types de
tests exécutés dans une classe particulière de tests. La commande suivante n'entraînera que
les tests unitaires en cours d'exécution :
$ ./test.py --constrain=unité
De même, la commande suivante entraînera uniquement l'exécution des exemples de tests de fumée :
$ ./test.py --constrain=unité
Pour voir une liste rapide des types de contraintes légales, vous pouvez demander qu'elles soient répertoriées.
La commande suivante
$ ./test.py --kinds
entraînera l'affichage de la liste suivante :
Waf : Entrer dans le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf : quitter le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' s'est terminé avec succès (0.939s)Waf : Entrer dans le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt : Tests de vérification de la construction (pour voir si la construction s'est terminée avec succès)
core : exécuter tous les tests basés sur TestSuite (exclure les exemples)
exemple : Exemples (pour voir si les exemples de programmes s'exécutent correctement)
performances : tests de performances (vérifiez si le système est aussi rapide que prévu)
système : Tests du système (s'étend sur les modules pour vérifier l'intégration des modules)
unité : tests unitaires (dans les modules pour vérifier les fonctionnalités de base)
N'importe lequel de ces types de test peut être fourni comme une contrainte en utilisant le --contrainte option.
Pour voir une liste rapide de toutes les suites de tests disponibles, vous pouvez demander qu'elles soient
listé. La commande suivante,
$ ./test.py --list
entraînera l'affichage d'une liste de la suite de tests, similaire à
Waf : Entrer dans le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf : quitter le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' terminé avec succès (0.939s)
histogramme
ns3-wifi-ingérence
ns3-tcp-cwnd
ns3-tcp-interopérabilité
échantillon
dispositifs-maille-flamme
appareils-mesh-dot11s
dispositifs-maille
service-nom-objet
rappeler
attributs
config
valeur globale
de ligne de commande
nombre-aléatoire-de-base
objet
N'importe laquelle de ces suites répertoriées peut être sélectionnée pour être exécutée par elle-même à l'aide de la --suite option comme
montré ci-dessus.
Comme pour les suites de tests, on peut exécuter un seul exemple de programme C++ en utilisant le --Exemple
option. Notez que le chemin relatif de l'exemple n'a pas besoin d'être inclus et que
les exécutables construits pour les exemples C++ n'ont pas d'extensions. Entrer
$ ./test.py --example=udp-echo
entraîne l'exécution de cet exemple unique.
PASS : Exemples d'exemples/udp/udp-echo
Vous pouvez spécifier le répertoire où ns-3 a été construit en utilisant le --chemin de construction option comme
suit.
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
On peut exécuter un seul exemple de programme Python en utilisant le --pyexemple option. Notez que le
chemin relatif pour l'exemple doit être inclus et que les exemples Python ont besoin de leur
extensions. Entrer
$ ./test.py --pyexample=exemples/tutoriel/first.py
entraîne l'exécution de cet exemple unique.
PASS : Exemples/tutoriel/first.py
Étant donné que les exemples Python ne sont pas construits, vous n'avez pas besoin de spécifier le répertoire où ns-3
a été construit pour les faire fonctionner.
Normalement, lorsque des exemples de programmes sont exécutés, ils écrivent une grande quantité de données de fichier de trace.
Ceci est normalement enregistré dans le répertoire de base de la distribution (par exemple,
/home/user/ns-3-dev). Lorsque test.py exécute un exemple, il est vraiment complètement indifférent
avec les fichiers de trace. Il veut juste déterminer si l'exemple peut être construit et exécuté
sans erreur. Comme c'est le cas, les fichiers de trace sont écrits dans un
/tmp/traces-unchecked annuaire. Si vous exécutez l'exemple ci-dessus, vous devriez pouvoir trouver
l'associé udp-echo.tr et udp-echo-n-1.pcap fichiers là-bas.
La liste des exemples disponibles est définie par le contenu du répertoire ''examples'' dans
la distribution. Si vous sélectionnez un exemple à exécuter à l'aide de la --Exemple option,
test.py n'essaiera pas de décider si l'exemple a été configuré ou non, il
va simplement essayer de l'exécuter et signaler le résultat de la tentative.
Lorsque test.py s'exécute, par défaut, il s'assurera d'abord que le système a été complètement
construit. Cela peut être vaincu en sélectionnant le --nowaf option.
$ ./test.py --list --nowaf
entraînera l'affichage d'une liste des suites de tests actuellement construites, semblable à :
ns3-wifi-propagation-loss-models
ns3-tcp-cwnd
ns3-tcp-interopérabilité
pcap-fichier-objet
service-nom-objet
générateurs de nombres aléatoires
A noter l'absence de Waf construire des messages.
test.py prend également en charge l'exécution des suites de tests et des exemples sous valgrind. Valgrind est un
programme flexible pour le débogage et le profilage des exécutables Linux. Par défaut, valgrind s'exécute
un outil appelé memcheck, qui exécute une gamme de fonctions de vérification de la mémoire, y compris
détection d'accès à la mémoire non initialisée, de mauvaise utilisation de la mémoire allouée (doubles libérations,
accès après libération, etc.) et détection des fuites de mémoire. Ceci peut être sélectionné en utilisant le
--moudre option.
$ ./test.py --grind
Pendant qu'il tourne, test.py et les programmes qu'il exécute indirectement, génèrent un grand nombre de
fichiers temporaires. Habituellement, le contenu de ces fichiers n'est pas intéressant, cependant dans certains
Dans certains cas, il peut être utile (à des fins de débogage) d'afficher ces fichiers. test.py fournit une
--conserver option qui entraînera la conservation de ces fichiers temporaires après l'exécution
complété. Les fichiers sont enregistrés dans un répertoire nommé testpy-sortie sous un sous-répertoire
nommé selon le temps universel coordonné actuel (également connu sous le nom de Greenwich Mean
Temps).
$ ./test.py --retain
Enfin, test.py fournit une --verbeux option qui imprimera de grandes quantités d'informations
sur son avancement. On ne s'attend pas à ce que cela soit terriblement utile à moins qu'il n'y ait
une erreur. Dans ce cas, vous pouvez accéder à la sortie standard et à l'erreur standard
signalés par l'exécution de suites de tests et d'exemples. Sélectionnez verbeux de la manière suivante :
$ ./test.py --verbose
Toutes ces options peuvent être mélangées et assorties. Par exemple, pour exécuter tout le noyau ns-3
suites de tests sous valgrind, en mode verbeux, tout en générant un fichier de sortie HTML, un
ferait:
$ ./test.py --verbose --grind --constrain=core --html=results.html
TestTaxonomie
Comme mentionné ci-dessus, les tests sont regroupés en un certain nombre de classifications largement définies pour
permettre aux utilisateurs d'exécuter des tests de manière sélective pour répondre aux différents types de tests qui nécessitent
être fait.
· Tests de vérification de construction
· Tests unitaires
· Essais du système
· Exemples
· Des tests de performance
Construire des tests de vérification
Ce sont des tests relativement simples qui sont construits avec la distribution et sont utilisés
pour s'assurer que la construction fonctionne à peu près. Nos tests unitaires actuels vivent dans le
les fichiers sources du code qu'ils testent et sont intégrés aux modules ns-3 ; et ainsi adapter le
Description des BVT. Les BVT vivent dans le même code source que celui intégré au code ns-3.
Nos tests actuels sont des exemples de ce type de test.
Unité Tests
Les tests unitaires sont des tests plus complexes qui vont dans les détails pour s'assurer qu'un morceau de code
fonctionne comme annoncé isolément. Il n'y a vraiment aucune raison pour que ce genre de test soit
intégré dans un module ns-3. Il s'avère, par exemple, que l'unité teste l'objet
service de nom ont à peu près la même taille que le code du service de nom d'objet lui-même. Tests unitaires
sont des tests qui vérifient un seul bit de fonctionnalité qui n'est pas intégré au code ns-3,
mais vivent dans le même répertoire que le code qu'il teste. Il est possible que ces tests
vérifiez également l'intégration de plusieurs fichiers d'implémentation dans un module. Le fichier
src/core/test/names-test-suite.cc est un exemple de ce type de test. Le fichier
src/network/test/pcap-file-test-suite.cc est un autre exemple qui utilise un bon pcap connu
fichier en tant que fichier vectoriel de test. Ce fichier est stocké localement dans le répertoire src/network.
Système Tests
Les tests système sont ceux qui impliquent plus d'un module dans le système. Nous avons beaucoup de
ce type de test s'exécute dans notre cadre de régression actuel, mais ils sont généralement
exemples surchargés. Nous fournissons une nouvelle place pour ce genre de test dans l'annuaire
source/test. Le fichier src/test/ns3tcp/ns3-interop-test-suite.cc est un exemple de ce genre
d'essai. Il utilise NSC TCP pour tester l'implémentation TCP ns-3. Il y aura souvent des tests
vecteurs nécessaires à ce type de test, et ils sont stockés dans le répertoire où
tester des vies. Par exemple, ns3tcp-interop-response-vectors.pcap est un fichier composé d'un
nombre d'en-têtes TCP utilisés comme réponses attendues du TCP ns-3 testé
à un stimulus généré par le NSC TCP qui est utilisé comme une implémentation '' bien connue ''.
Exemples
Les exemples sont testés par le framework pour s'assurer qu'ils sont construits et s'exécuteront. Rien n'est
vérifié, et actuellement les fichiers pcap sont simplement écrits dans / Tmp à jeter. Si
les exemples s'exécutent (ne plantent pas), ils réussissent ce test de fumée.
Performance Tests
Les tests de performance sont ceux qui exercent une partie particulière du système et déterminent
si les tests ont été exécutés jusqu'à la fin dans un délai raisonnable.
Fonctionnement Tests
Les tests sont généralement exécutés en utilisant le haut niveau test.py programme. Pour obtenir une liste des
options de ligne de commande disponibles, exécutez test.py --Aidez-moi
Le programme d'essai test.py exécutera à la fois les tests et les exemples qui ont été ajoutés à
la liste à vérifier. La différence entre les tests et les exemples est la suivante. Essais
vérifient généralement que la sortie ou les événements de simulation spécifiques sont conformes au comportement attendu.
En revanche, la sortie des exemples n'est pas vérifiée et le programme de test vérifie simplement la
l'état de sortie du programme d'exemple pour vous assurer qu'il s'exécute sans erreur.
En bref, pour exécuter tous les tests, il faut d'abord configurer les tests lors de la phase de configuration, et
également (facultativement) des exemples si des exemples doivent être vérifiés :
$ ./waf --configure --enable-examples --enable-tests
Ensuite, construisez ns-3, et après qu'il soit construit, lancez simplement test.py. test.py -h affichera un nombre
d'options de configuration qui modifient le comportement de test.py.
Le programme test.py appelle, pour les tests et les exemples C++, un programme C++ de niveau inférieur appelé
testeur Pour exécuter réellement les tests. Comme discuté ci-dessous, ceci testeur peut être un
moyen utile pour déboguer les tests.
Débogage Tests
Le débogage des programmes de test est mieux exécuté en exécutant le testeur de bas niveau
programme. Le testeur est le pont entre le code Python générique et ns-3 code. Il est
écrit en C++ et utilise le processus de découverte automatique des tests dans le ns-3 code pour trouver et
permettre l'exécution de tous les différents tests.
La principale raison pour laquelle test.py n'est pas adapté au débogage, c'est qu'il n'est pas autorisé pour
la journalisation doit être activée à l'aide de NS_LOG variable d'environnement lors de l'exécution de test.py. Cette
la limitation ne s'applique pas à l'exécutable test-runner. Par conséquent, si vous voulez voir la journalisation
sortie de vos tests, vous devez les exécuter directement à l'aide du test-runner.
Pour exécuter le test-runner, vous l'exécutez comme n'importe quel autre exécutable ns-3 -- en utilisant
waf. Pour obtenir une liste des options disponibles, vous pouvez taper :
$ ./waf --run "test-runner --help"
Vous devriez voir quelque chose comme ce qui suit
Waf : Entrer dans le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf : quitter le répertoire `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' terminé avec succès (0.353s)
--assert : Dire aux tests de segfault (comme assert) si une erreur est détectée
--basedir=dir : définit le répertoire de base (où trouver src) sur ''dir''
--tempdir=dir : définit le répertoire temporaire (où trouver les fichiers de données) sur ''dir''
--constrain=test-type : Contraint les vérifications aux suites de tests de type ''test-type''
- help: imprimez ce message
--kinds : répertorie tous les types de tests disponibles
--list : répertorie toutes les suites de tests (éventuellement limitées par le type de test)
--out=nom-fichier : définit le fichier de sortie de l'état du test sur ''nom-fichier''
--suite=nom-de-la-suite : Exécute la suite de tests nommée ''nom-de-la-suite''
--verbose : Activer les messages dans les suites de tests d'exécution
Il y a un certain nombre de choses à votre disposition qui vous seront familières si vous avez
regardé test.py. Cela devrait être attendu puisque le testeur n'est qu'une interface
jusqu'à XNUMX fois test.py et ns-3. Vous remarquerez peut-être que les commandes liées à l'exemple sont manquantes ici.
C'est parce que les exemples ne sont vraiment pas ns-3 Des tests. test.py les dirige comme s'ils étaient
pour présenter un environnement de test unifié, mais ils sont vraiment complètement différents et non
être trouvé ici.
La première nouvelle option qui apparaît ici, mais pas dans test.py est la --affirmer option. Ce
L'option est utile lors du débogage d'un cas de test lors de l'exécution sous un débogueur comme gdb. Quand
sélectionnée, cette option indique au cas de test sous-jacent de provoquer une violation de segmentation si
une erreur est détectée. Cela a l'effet secondaire agréable de provoquer l'arrêt de l'exécution du programme
(entrer dans le débogueur) lorsqu'une erreur est détectée. Si vous utilisez gdb, vous pouvez utiliser
cette option quelque chose comme,
$ ./coque waf
$ cd build/débogage/utils
Coureur de test $ gdb
$ run --suite=valeur-globale --assert
Si une erreur est alors trouvée dans la suite de tests de valeur globale, une erreur de segmentation serait générée
et le débogueur (niveau source) s'arrêterait à la NS_TEST_ASSERT_MSG qui a détecté le
Erreur.
Une autre nouvelle option qui apparaît ici est la --basedir option. Il s'avère que certains
les tests peuvent avoir besoin de référencer le répertoire source du ns-3 distribution pour trouver local
data, donc un répertoire de base est toujours requis pour exécuter un test.
Si vous exécutez un test à partir de test.py, le programme Python fournira l'option basedir pour
tu. Pour exécuter l'un des tests directement depuis le testeur en utilisant waf, tu devras
spécifiez la suite de tests à exécuter avec le répertoire de base. Vous pouvez donc utiliser le shell
et fait:
$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"
Notez les ''guillemets arrière'' sur le pwd commander.
Si vous exécutez la suite de tests à partir d'un débogueur, il peut être assez pénible de se souvenir
et tapez constamment le chemin absolu du répertoire de base de la distribution. À cause de
ceci, si vous omettez le basedir, le testeur essaiera d'en trouver un pour vous. Ce
commence dans le répertoire de travail courant et parcourt l'arborescence des répertoires à la recherche d'un
fichier de répertoire avec des fichiers nommés VERSION et LICENCE. S'il en trouve un, il suppose que
doit être le basedir et le fournit pour vous.
Le test sortie
De nombreuses suites de tests ont besoin d'écrire des fichiers temporaires (tels que des fichiers pcap) lors du processus de
exécuter les tests. Les tests ont alors besoin d'un répertoire temporaire pour écrire. Le Python
l'utilitaire de test (test.py) fournira automatiquement un fichier temporaire, mais s'il est exécuté de manière autonome
ce répertoire temporaire doit être fourni. Tout comme dans le cas basedir, il peut être
ennuyeux de devoir continuellement fournir un --répertoire temp, donc le testeur en déduira un
pour vous si vous n'en fournissez pas. Il recherche d'abord les variables d'environnement nommées TMP
et TEMP et les utilise. Si ni l'un ni l'autre TMP ni TEMP sont définis il sélectionne / Tmp. Le code
puis pointe un identifiant indiquant ce qui a créé le répertoire (ns-3) puis l'heure
(hh.mm.ss) suivi d'un grand nombre aléatoire. Le testeur crée un répertoire de ce
nom à utiliser comme répertoire temporaire. Les fichiers temporaires vont ensuite dans un répertoire qui
sera nommé quelque chose comme
/tmp/ns-3.10.25.37.61537845
Le temps est fourni à titre indicatif afin que vous puissiez reconstituer relativement facilement ce que
répertoire a été utilisé si vous avez besoin de revenir en arrière et de regarder les fichiers qui ont été placés dans ce
répertoire.
Une autre classe de sortie est la sortie de test comme les traces pcap qui sont générées pour être comparées à
sortie de référence. Le programme de test les supprimera généralement après que les suites de tests auront toutes
Cours. Pour désactiver la suppression de la sortie de test, exécutez test.py avec l'option "conserver":
$ ./test.py -r
et la sortie de test peut être trouvée dans le testpy-sortie/ répertoire.
Reporting of tester échecs
Lorsque vous exécutez une suite de tests à l'aide du lanceur de tests, il exécutera le test silencieusement par défaut.
La seule indication que vous obtiendrez que le test a réussi est le absence d'un message
de waf indiquant que le programme a renvoyé autre chose qu'un code de sortie nul. Pour obtenir
certaines sorties du test, vous devez spécifier un fichier de sortie auquel les tests seront
écrire leur statut XML en utilisant le --dehors option. Vous devez être prudent en interprétant
résultats parce que les suites de tests seront ajouter résultats sur ce fichier. Essayer,
$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=monfichier.xml"
Si vous regardez le fichier monfichier.xml vous devriez voir quelque chose comme,
pcap-fichier-objet
Vérifiez que PcapFile::Open with mode ''w'' fonctionne
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Vérifiez que PcapFile::Open with mode ''r'' fonctionne
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Vérifiez que PcapFile::Open with mode ''a'' fonctionne
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Vérifier que PcapFileHeader est correctement géré
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Vérifier que PcapRecordHeader est correctement géré
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Vérifiez que PcapFile peut lire un bon fichier pcap connu
PASSER
réel 0.00 utilisateur 0.00 système 0.00
PASSER
réel 0.00 utilisateur 0.00 système 0.00
Si vous êtes familier avec XML, cela devrait être assez explicite. Ce n'est pas non plus un
fichier XML complet puisque les suites de tests sont conçues pour que leur sortie soit ajoutée à un maître
Fichier d'état XML comme décrit dans le test.py .
Débogage tester suite échecs
Pour déboguer les plantages de test, tels que
CRASH : TestSuite ns3-wifi-ingérence
Vous pouvez accéder au programme test-runner sous-jacent via gdb comme suit, puis passer le
Argument "--basedir=`pwd`" à exécuter (vous pouvez également passer d'autres arguments si nécessaire, mais
basedir est le minimum requis):
$ ./waf --command-template="gdb %s" --run "test-runner"
Waf : Entrer dans le répertoire `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf : Départ du répertoire `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
'build' terminé avec succès (0.380s)
GNU gdb 6.8-debian
Droit d'auteur (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+ : GNU GPL version 3 ou ultérieurehttp://gnu.org/licenses/gpl.html>
C'est un logiciel libre : vous êtes libre de le modifier et de le redistribuer.
Il n'y a AUCUNE GARANTIE, dans la mesure permise par la loi. Tapez "afficher la copie"
et "afficher la garantie" pour plus de détails.
Ce GDB a été configuré en tant que "x86_64-linux-gnu"...
(gdb) r --basedir=`pwd`
Programme de démarrage : <..>/build/debug/utils/test-runner --basedir=`pwd`
[Débogage de thread à l'aide de libthread_db activé]
l'affirmation a échoué. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
Voici un autre exemple d'utilisation de valgrind pour déboguer un problème de mémoire tel que :
VALGR : TestSuite devices-mesh-dot11s-regression
$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --run test-runner
Classe Exécuteur de test
Les exécutables qui exécutent des programmes de test dédiés utilisent une classe TestRunner. Cette classe
fournit l'enregistrement et la liste automatiques des tests, ainsi qu'un moyen d'exécuter le
épreuves individuelles. Les suites de tests individuelles utilisent des constructeurs globaux C++ pour s'ajouter à
une collection de suites de tests gérées par le lanceur de tests. Le testeur est utilisé pour répertorier
tous les tests disponibles et de sélectionner un test à exécuter. C'est une classe assez simple
qui fournit trois méthodes statiques pour fournir ou ajouter et obtenir des suites de tests à un
recueil d'épreuves. Voir le doxygen pour la classe ns3 ::TestRunner pour en savoir plus.
Le test Suite
Tous ns-3 les tests sont classés en suites de tests et cas de test. Une suite de tests est un
collection de cas de test qui exécutent complètement un type de fonctionnalité donné. Comme
décrites ci-dessus, les suites de tests peuvent être classées comme suit :
· Tests de vérification de construction
· Tests unitaires
· Essais du système
· Exemples
· Des tests de performance
Cette classification est exportée depuis la classe TestSuite. Cette classe est assez simple,
n'existant que comme lieu d'exportation de ce type et d'accumulation de cas de test. D'un utilisateur
perspective, pour créer une nouvelle TestSuite dans le système, il suffit de définir une nouvelle
classe qui hérite de la classe Suite de tests et remplir ces deux fonctions.
Le code suivant définira une nouvelle classe qui peut être exécutée par test.py comme test ''unitaire''
avec le nom d'affichage, nom-de-ma-suite-de-tests.
classe MaSuite : public TestSuite
{
publique:
MaSuiteTest ();
};
MaSuiteTests ::MaSuiteTests ()
: TestSuite ("nom-de-ma-suite-de-tests", UNIT)
{
AddTestCase (nouveau MyTestCase);
}
MaSuiteTest maSuiteTest ;
La classe de base s'occupe de tous les enregistrements et rapports requis pour être un bon
citoyen dans le cadre du test.
Le test Témoignage client
Les tests individuels sont créés à l'aide d'une classe TestCase. Modèles communs pour l'utilisation d'un test
cas incluent "un cas de test par fonctionnalité" et "un cas de test par méthode". Mélanges de
ces modèles peuvent être utilisés.
Pour créer un nouveau cas de test dans le système, il suffit d'hériter du
Cas de test classe de base, remplacez le constructeur pour donner un nom au cas de test et remplacez
le Exécuter méthode pour exécuter le test.
classe MyTestCase : public TestCase
{
MonCasTest ();
vide virtuel DoRun (vide);
};
MyTestcase :: myTestcase ()
: TestCase ("Vérifiez quelques fonctionnalités")
{
}
annuler
MyTestCase::DoRun (vide)
{
NS_TEST_ASSERT_MSG_EQ (vrai, vrai, "Un message d'échec");
}
Utilitaires
Il existe un certain nombre d'utilitaires de différents types qui font également partie des tests
cadre. Les exemples incluent un fichier pcap généralisé utile pour stocker des vecteurs de test ; un
conteneur générique utile pour le stockage transitoire des vecteurs de test pendant l'exécution du test ; et
des outils pour générer des présentations basées sur les résultats des tests de validation et de vérification.
Ces utilitaires ne sont pas documentés ici, mais par exemple, veuillez voir comment les tests TCP
trouvé dans src/test/ns3tcp/ utilisez les fichiers pcap et la sortie de référence.
Comment à écrire tests
L'un des principaux objectifs du projet ns-3 est d'aider les utilisateurs à améliorer la validité et
crédibilité de leurs résultats. Il existe de nombreux éléments pour obtenir des modèles valides et
les simulations et les tests sont une composante majeure. Si vous apportez des modèles ou des exemples à
ns-3, vous pouvez être invité à contribuer au code de test. Les modèles que vous contribuez seront utilisés
pendant de nombreuses années par d'autres personnes, qui n'ont probablement aucune idée à première vue si le
le modèle est correct. Le code de test que vous écrivez pour votre modèle vous aidera à éviter de futures
régressions dans la sortie et aidera les futurs utilisateurs à comprendre la vérification et
limites d'applicabilité de vos modèles.
Il existe de nombreuses façons de vérifier l'exactitude de l'implémentation d'un modèle. Dans ce
section, nous espérons couvrir certains cas courants qui peuvent être utilisés comme guide pour écrire de nouveaux
Des tests.
Échantillon Suite de tests squelette
Lorsque vous partez de zéro (c'est-à-dire sans ajouter de TestCase à une TestSuite existante), ces
les choses doivent être décidées à l'avance:
· Comment s'appellera la suite de tests
· De quel type de test il s'agira (Build Verification Test, Unit Test, System Test ou
Test de performance)
· Où le code de test vivra (soit dans un module ns-3 existant ou séparément dans
répertoire src/test/). Vous devrez éditer le fichier wscript dans ce répertoire pour
compilez votre nouveau code, s'il s'agit d'un nouveau fichier.
Un programme appelé src/create-module.py est un bon point de départ. Ce programme peut être
invoqué comme créer-module.py toupie pour un nouveau module hypothétique appelé toupie. Une fois que
vous faites cela, vous verrez un toupie répertoire et un test/routeur-test-suite.cc suite de tests.
Ce fichier peut être un point de départ pour votre test initial. Ceci est une suite de tests de travail,
bien que les tests réels effectués soient triviaux. Copiez-le dans le test de votre module
répertoire, et faire une substitution globale de "Router" dans ce fichier pour quelque chose se rapportant
au modèle que vous souhaitez tester. Vous pouvez également modifier des éléments tels qu'une description plus détaillée
nom du cas de test.
Vous devez également ajouter un bloc dans votre wscript pour obtenir ce test à compiler :
module_test.source = [
'test/routeur-test-suite.cc',
]
Avant de commencer à lui faire faire des choses utiles, il peut être utile d'essayer d'exécuter le
squelette. Assurez-vous que ns-3 a été configuré avec l'option "--enable-tests".
Supposons que votre nouvelle suite de tests s'appelle "routeur" comme ici :
RouteurTestSuite :: RouteurTestSuite ()
: TestSuite ("routeur", UNITÉ)
Essayez cette commande :
$ ./test.py -s routeur
Une sortie telle que ci-dessous doit être produite :
RÉUSSI : Routeur TestSuite
1 des 1 tests réussis (1 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Voir src/lte/test/test-lte-antenna.cc pour un exemple concret.
Le test macros
Il existe un certain nombre de macros disponibles pour vérifier la sortie du programme de test avec les résultats attendus.
production. Ces macros sont définies dans src/core/model/test.h.
L'ensemble principal de macros utilisées comprend les éléments suivants :
NS_TEST_ASSERT_MSG_EQ(réel, limite, msg)
NS_TEST_ASSERT_MSG_NE(réel, limite, msg)
NS_TEST_ASSERT_MSG_LT(réel, limite, msg)
NS_TEST_ASSERT_MSG_GT(réel, limite, msg)
NS_TEST_ASSERT_MSG_EQ_TOL(réel, limite, tol, msg)
Le premier argument présenter est la valeur testée, la deuxième valeur limite est l'attendu
value (ou la valeur à tester) et le dernier argument msg est le message d'erreur à
imprimer si le test échoue.
Les quatre premières macros ci-dessus testent l'égalité, l'inégalité, inférieur ou supérieur à,
respectivement. La cinquième macro ci-dessus teste l'égalité, mais dans une certaine tolérance.
Cette variante est utile pour tester l'égalité des nombres à virgule flottante par rapport à une limite,
où vous voulez éviter un échec de test dû à des erreurs d'arrondi.
Enfin, il existe des variantes de ce qui précède où le mot-clé ASSERT est remplacé par ATTENDRE.
Ces variantes sont spécialement conçues pour être utilisées dans les méthodes (en particulier les rappels) renvoyant
annuler. Réservez leur utilisation aux callbacks que vous utilisez dans vos programmes de test ; sinon, utilisez
le ASSERT variantes.
Comment à ajouter an (ici) Programme à le tester suite
On peut "smoke test" que les exemples se compilent et s'exécutent avec succès jusqu'à la fin (sans
fuites de mémoire) en utilisant le exemples-à-exécuter.py script situé dans le répertoire de test de votre module.
En bref, en incluant une instance de ce fichier dans votre répertoire de test, vous pouvez provoquer le
test runner pour exécuter les exemples listés. Il est généralement préférable de s'assurer que vous
sélectionnez des exemples qui ont des durées d'exécution raisonnablement courtes afin de ne pas enliser les tests. Voir
l'exemple dans src/lte/test/ répertoire.
Contrôle de qualité pour booléen les résultats
Contrôle de qualité les résultats quand aléatoire is impliqué
Contrôle de qualité sortie données, à opposer à a connu pour la distribution
Fournir non trivial contribution vecteurs of données,
Stockage et référencement non trivial sortie données,
En présentant votre sortie tester données,
Assistance
La création a nouvelle ns-3 modèle
Ce chapitre décrit le processus de conception d'un ns-3 maquette. Dans de nombreux cas de recherche,
les utilisateurs ne se contenteront pas d'adapter des modèles existants, mais voudront peut-être étendre
noyau du simulateur d'une manière nouvelle. Nous utiliserons l'exemple de l'ajout d'un ErrorModel à un
simple ns-3 lien comme un exemple motivant de la façon dont on pourrait aborder ce problème et
procéder à une conception et à une mise en œuvre.
NOTE:
Documentation
Ici, nous nous concentrons sur le processus de création de nouveaux modèles et de nouveaux modules, et certains des
choix de conception impliqués. Par souci de clarté, nous reportons la discussion sur mécanique
de documenter les modèles et le code source au Documentation chapitre.
Design approche
Réfléchissez à la façon dont vous voulez que cela fonctionne ; que doit-il faire. Pensez à ces choses :
· Fonctionnalité: Quelle fonctionnalité devrait-il avoir? Quels sont les attributs ou la configuration
exposé à l'utilisateur ?
· réutilisabilité : Dans quelle mesure les autres devraient-ils pouvoir réutiliser mon design ? Puis-je réutiliser le code de
ns-2 pour commencer? Comment un utilisateur intègre-t-il le modèle avec le reste d'un autre
simulation?
· dépendances : Comment puis-je réduire l'introduction de dépendances extérieures sur mon nouveau code
autant que possible (pour le rendre plus modulable) ? Par exemple, dois-je éviter tout
dépendance à IPv4 si je veux qu'il soit également utilisé par IPv6 ? Dois-je éviter toute dépendance
sur IP du tout?
N'hésitez pas à contacter le ns-3-utilisateurs or ns-développeurs liste si vous avez des questions.
En particulier, il est important de penser à l'API publique de votre nouveau modèle et de demander
retour d'information. Cela aide également à faire connaître votre travail aux autres au cas où vous seriez intéressé par
collaborateurs.
Mise en situation : Modèle d'erreur
Un modèle d'erreur existe dans ns-2. Il permet de transmettre des paquets à un objet avec état qui
détermine, sur la base d'une variable aléatoire, si le paquet est corrompu. L'appelant peut
puis décidez quoi faire avec le paquet (lâchez-le, etc.).
L'API principale du modèle d'erreur est une fonction à laquelle transmettre un paquet, et la valeur de retour de
cette fonction est un booléen qui indique à l'appelant si une corruption s'est produite. Noter
selon le modèle d'erreur, le tampon de données de paquets peut ou non être corrompu.
Appelons cette fonction "IsCorrupt()".
Jusqu'à présent, dans notre conception, nous avons :
classe ErrorModel
{
publique:
/ **
* \retourne vrai si le paquet doit être considéré comme erroné/corrompu
* \param pkt Paquet auquel appliquer le modèle d'erreur
*/
bool IsCorrupt (Ptr paquet);
};
Notez que nous ne passons pas de pointeur const, permettant ainsi à la fonction de modifier le
paquet si IsCorrupt() renvoie true. Tous les modèles d'erreur ne modifieront pas réellement le paquet ;
si oui ou non le tampon de données de paquets est corrompu doit être documenté.
Nous pouvons également vouloir des versions spécialisées de cela, comme dans ns-2, donc même si ce n'est pas le
seul choix de conception pour le polymorphisme, nous supposons que nous allons sous-classer une classe de base
ErrorModel pour les classes spécialisées, telles que RateErrorModel, ListErrorModel, etc., telles que
se fait dans ns-2.
Vous pensez peut-être à ce stade, "Pourquoi ne pas faire de IsCorrupt() une méthode virtuelle ?". C'est-à-dire
une approche; l'autre est de rendre indirecte la fonction publique non virtuelle par un
fonction virtuelle privée (cela en C++ est connu sous le nom d'idiome d'interface non virtuelle et est
adopté dans le ns-3 classe ErrorModel).
Ensuite, cet appareil doit-il dépendre d'IP ou d'autres protocoles ? Nous ne voulons pas
créer des dépendances aux protocoles Internet (le modèle d'erreur doit être applicable aux
protocoles non-Internet aussi), donc nous garderons cela à l'esprit plus tard.
Une autre considération est la manière dont les objets incluront ce modèle d'erreur. Nous envisageons de mettre
un setter explicite dans certaines implémentations de NetDevice, par exemple :
/ **
* Attachez un ErrorModel de réception au PointToPointNetDevice.
*
* Le PointToPointNetDevice peut éventuellement inclure un ErrorModel dans
* la chaîne de réception des paquets.
*
* @voir modèle d'erreur
* @param em Ptr au ErrorModel.
*/
void PointToPointNetDevice ::SetReceiveErrorModel(Ptr em);
Encore une fois, ce n'est pas le seul choix que nous avons (les modèles d'erreur peuvent être agrégés à de nombreux
d'autres objets), mais il satisfait notre cas d'utilisation principal, qui est de permettre à un utilisateur de forcer
erreurs sur des transmissions de paquets par ailleurs réussies, au niveau NetDevice.
Après avoir réfléchi et examiné l'existant ns-2 code, voici un exemple d'API d'une base
classe et première sous-classe qui pourraient être publiées pour examen initial :
classe ErrorModel
{
publique:
Modèle d'erreur ();
virtuel ~ErrorModel ();
bool IsCorrupt (Ptr paquet);
annuler la réinitialisation (annuler);
annuler Activer (annuler);
void Désactiver (annuler);
bool IsEnabled (vide) const ;
privé:
bool virtuel DoCorrupt (Ptr pkt) = 0 ;
vide virtuel DoReset (vide) = 0 ;
};
enum ErreurUnité
{
EU_BIT,
EU_BYTE,
EU_PKT
};
// Détermine quels paquets sont erronés correspondant à un sous-jacent
// distribution de variables aléatoires, un taux d'erreur et une unité pour le taux.
classe RateErrorModel : public ErrorModel
{
publique:
Modèle d'erreur de taux ();
virtuel ~RateErrorModel ();
enum ErrorUnit GetUnit (vide) const ;
void SetUnit (énumération ErrorUnit error_unit);
double GetRate (vide) const ;
annuler SetRate (taux double);
void SetRandomVariable (const RandomVariable &ranvar);
privé:
bool virtuel DoCorrupt (Ptr paquet);
vide virtuel DoReset (vide);
};
Échafaudage
Disons que vous êtes prêt à commencer la mise en œuvre ; vous avez une image assez claire de
ce que vous voulez construire, et vous avez peut-être sollicité un examen initial ou des suggestions de
la liste. Une façon d'aborder l'étape suivante (mise en œuvre) consiste à créer un échafaudage et
remplissez les détails au fur et à mesure que la conception mûrit.
Cette section passe en revue de nombreuses étapes à prendre en compte pour définir un échafaudage, ou
un squelette non fonctionnel de ce que votre modèle implémentera éventuellement. C'est généralement bon
pratique de ne pas attendre pour intégrer ces détails à la fin, mais plutôt de sonder un
squelette de votre modèle dans le système au début, puis ajoutez des fonctions plus tard une fois l'API et
l'intégration semble correcte.
Notez que vous voudrez modifier quelques éléments dans la présentation ci-dessous pour votre modèle
car si vous suivez le modèle d'erreur textuellement, le code que vous produisez entrera en collision avec le
modèle d'erreur existant. Ce qui suit n'est qu'un aperçu de la façon dont ErrorModel a été construit que vous
peut s'adapter à d'autres modèles.
Évaluation le ns-3 Codage Style Documents
À ce stade, vous voudrez peut-être faire une pause et lire le ns-3 document de style de codage, en particulier
si vous envisagez de contribuer votre code au projet. Le style de codage
document est lié à la page principale du projet : ns-3 Coding Catégorie.
Décide Où in le Source Arbre le Modèle Si Résider
Toutes les ns-3 le code source du modèle est dans le répertoire src /. Vous devrez choisir lequel
sous-répertoire dans lequel il réside. S'il s'agit d'un nouveau code modèle quelconque, il est logique de le mettre
into the src / répertoire quelque part, en particulier pour faciliter l'intégration avec la construction
système.
Dans le cas du modèle d'erreur, il est très lié à la classe de paquets, il est donc logique
pour mettre cela en œuvre dans le src/réseau/ module où ns-3 paquets sont implémentés.
waf et wscript
ns-3 utilise l' Waf système de construction. Vous voudrez intégrer votre nouveau ns-3 utilise le Waf
système de construction. Vous voudrez intégrer vos nouveaux fichiers source dans ce système. Cette
nécessite que vous ajoutiez vos fichiers au wscript fichier présent dans chaque répertoire.
Commençons par les fichiers vides error-model.h et error-model.cc, et ajoutons ceci à
src/réseau/wscript. Il s'agit simplement d'ajouter le fichier .cc au reste du
fichiers source et le fichier .h à la liste des fichiers d'en-tête.
Maintenant, ouvrez le répertoire de niveau supérieur et tapez "./test.py". Tu n'aurais pas dû casser
quoi que ce soit par cette opération.
Inclure Gardes
Ensuite, ajoutons quelques de Swanson comprennent gardes dans notre fichier d'en-tête :
#ifndef ERROR_MODEL_H
#définir ERROR_MODEL_H
#endif
namespace ns3
ns-3 utilise l' ns-3 namespace pour isoler ses symboles des autres espaces de noms. Typiquement, un
l'utilisateur mettra ensuite un ns-3 bloc d'espace de noms dans les fichiers cc et h. :
espace de noms ns3 {
}
À ce stade, nous avons quelques fichiers squelettiques dans lesquels nous pouvons commencer à définir nos nouvelles classes.
Le fichier d'en-tête ressemble à ceci :
#ifndef ERROR_MODEL_H
#définir ERROR_MODEL_H
espace de noms ns3 {
} // espace de noms ns3
#endif
tandis que le modèle-d'erreur.cc le fichier ressemble simplement à ceci :
#include "error-model.h"
espace de noms ns3 {
} // espace de noms ns3
Ces fichiers doivent être compilés car ils n'ont pas vraiment de contenu. Nous sommes maintenant prêts à
commencer à ajouter des classes.
Initiales Implantation
À ce stade, nous travaillons encore sur des échafaudages, mais nous pouvons commencer à définir notre
classes, avec la fonctionnalité à ajouter ultérieurement.
Hériter de le Exlcusion Classer?
Il s'agit d'une étape de conception importante ; s'il faut utiliser la classe Exlcusion comme classe de base pour votre nouveau
classes.
Comme décrit dans le chapitre sur la ns-3 Modèle d'objet, classes qui héritent de la classe
Exlcusion obtenir des propriétés spéciales :
· les ns-3 type et système d'attributs (voir Attributs)
· un système d'agrégation d'objets
· un système de comptage de références à pointeur intelligent (classe Ptr)
Les classes qui dérivent de la classe Base d'objets} obtenir les deux premières propriétés ci-dessus, mais ne pas
obtenir des pointeurs intelligents. Les classes qui dérivent de la classe RefCountBase obtenir uniquement le pointeur intelligent
système de comptage de référence.
En pratique, la classe Exlcusion est la variante des trois ci-dessus que le ns-3 le développeur va
rencontre le plus souvent.
Dans notre cas, nous voulons utiliser le système d'attributs, et nous passerons des instances
de cet objet à travers le ns-3 API publique, donc classe Exlcusion nous convient.
Initiales Cours
Une façon de procéder est de commencer par définir les fonctions minimales et de voir si elles
compiler. Passons en revue ce que tout est nécessaire pour implémenter lorsque nous dérivons de la classe Object. :
#ifndef ERROR_MODEL_H
#définir ERROR_MODEL_H
#include "ns3/objet.h"
espace de noms ns3 {
classe ErrorModel : objet public
{
publique:
statique TypeId GetTypeId (void);
Modèle d'erreur ();
virtuel ~ErrorModel ();
};
classe RateErrorModel : public ErrorModel
{
publique:
statique TypeId GetTypeId (void);
Modèle d'erreur de taux ();
virtuel ~RateErrorModel ();
};
#endif
Quelques éléments à noter ici. Nous devons inclure objet.h. La convention en ns-3 est-ce que si
le fichier d'en-tête est co-localisé dans le même répertoire, il peut être inclus sans aucun chemin
préfixe. Par conséquent, si nous implémentions ErrorModel dans src/noyau/modèle répertoire, nous
aurait pu juste dire "#comprendre "objet.h"". Mais nous sommes dans src/réseau/modèle, nous devons donc
l'inclure comme "#comprendre "ns3/objet.h"". Notez également que cela va en dehors de l'espace de noms
déclaration.
Deuxièmement, chaque classe doit implémenter une fonction membre publique statique appelée ObtenirTypeId (vide).
Troisièmement, c'est une bonne idée d'implémenter des constructeurs et des destructeurs plutôt que de laisser le
le compilateur les génère et rend le destructeur virtuel. En C++, notez également que copier
l'opérateur d'affectation et les constructeurs de copie sont générés automatiquement s'ils ne sont pas définis, donc
si vous ne les voulez pas, vous devez les implémenter en tant que membres privés. Cet aspect de
Le C++ est abordé dans le livre Effective C++ de Scott Meyers. article 45.
Examinons maintenant un code d'implémentation squelettique correspondant dans le fichier .cc :
#include "error-model.h"
espace de noms ns3 {
NS_OBJECT_ENSURE_REGISTERED (ErreurModèle);
TypeId ErrorModel :: GetTypeId (vide)
{
statique TypeId tid = TypeId ("ns3::ErrorModel")
.SetParent ()
;
retour tid;
}
ModèleErreur::ModèleErreur ()
{
}
ModèleErreur ::~ModèleErreur ()
{
}
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
TypeId RateErrorModel :: GetTypeId (vide)
{
statique TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
;
retour tid;
}
RateErrorModel :: RateErrorModel ()
{
}
RateErrorModel ::~RateErrorModel ()
{
}
Quelle est le ObtenirTypeId (vide) fonction? Cette fonction fait quelques choses. Il enregistre une
chaîne unique dans le système TypeId. Il établit la hiérarchie des objets dans le
système d'attributs (via Setparent). Il déclare également que certains objets peuvent être créés via
le framework de création d'objets (Ajouter un constructeur).
La macro NS_OBJECT_ENSURE_REGISTERED (nom du cours) est également nécessaire une fois pour chaque classe qui
définit une nouvelle méthode GetTypeId et effectue l'enregistrement réel de la classe dans le
système. Le chapitre sur le modèle objet en parle plus en détail.
Y compris Externe Fichiers
Journal Assistance
Ici, écrire a Bits à propos ajoutant |ns3| enregistrement macro. Notez qui LOG_COMPONENT_DEFINE is
fait au contrôle le namespace ns3
Constructeur, Vide Fonction Prototypes
ACTIVITES Variables (Défaut Valeurs, Les attributs)
Le test Programme 1
Exlcusion Cadre
L'ajout de a Échantillon scénario
À ce stade, on peut vouloir essayer de prendre l'échafaudage de base défini ci-dessus et l'ajouter
dans le système. L'exécution de cette étape permet désormais d'utiliser un modèle plus simple lors de la plomberie
dans le système et peut également révéler si des modifications de conception ou d'API doivent être
fabriqué. Une fois cela fait, nous reviendrons à la construction de la fonctionnalité du
ErrorModels eux-mêmes.
Ajouter Basic Assistance in le Classe
/* périphérique-réseau-point-à-point.h */
classe ErrorModel ;
/ **
* Modèle d'erreur pour recevoir des événements de paquets
*/
Ptr m_receiveErrorModel ;
Ajouter Accesseur
annuler
PointToPointNetDevice ::SetReceiveErrorModel (Ptr em)
{
NS_LOG_FUNCTION (ce <<em);
m_receiveErrorModel = em ;
}
.AddAttribute ("ReceiveErrorModel",
"Le modèle d'erreur du récepteur utilisé pour simuler la perte de paquets",
PointeurValeur (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker ())
Sonder Dans le Système
void PointToPointNetDevice ::Receive (Ptr paquet)
{
NS_LOG_FUNCTION (ce << paquet);
protocole uint16_t = 0 ;
si (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (paquet) )
{
//
// Si nous avons un modèle d'erreur et qu'il indique qu'il est temps de perdre un
// paquet corrompu, ne transférez pas ce paquet, laissez-le partir.
//
m_dropTrace (paquet);
}
d'autre
{
//
// Appuyez sur le crochet de suivi de réception, supprimez l'en-tête de protocole point à point
// et transfère ce paquet vers le haut de la pile de protocoles.
//
m_rxTrace (paquet);
ProcessHeader(paquet, protocole);
m_rxCallback (ceci, paquet, protocole, GetRemote ());
si (!m_promiscCallback.IsNull ())
{ m_promiscCallback (ce, paquet, protocole, GetRemote (),
GetAddress(), NetDevice::PACKET_HOST);
}
}
}
Créer Null Fonctionnel scénario
/* modèle-erreur-simple.cc */
// Modèle d'erreur
// Nous voulons ajouter un modèle d'erreur au NetDevice du nœud 3
// Nous pouvons obtenir un handle vers le NetDevice via le canal et le nœud
// pointeurs
Ptr nd3 = PointToPointTopology::GetNetDevice
(n3, canal2);
Ptr em = Créer ();
nd3->SetReceiveErrorModel (em);
bool
ErrorModel::DoCorrupt (Packet& p)
{
NS_LOG_FUNCTION ;
NS_LOG_UNCOND("Corrupt!");
return false;
}
À ce stade, nous pouvons exécuter le programme avec notre trivial ErrorModel inséré dans le récepteur
chemin du PointToPointNetDevice. Il imprime la chaîne "Corrupt!" pour chaque paquet
reçu au noeud n3. Ensuite, nous retournons au modèle d'erreur pour ajouter une sous-classe qui effectue
modélisation d'erreur plus intéressante.
Ajouter a Sous-classe
La classe de base triviale ErrorModel ne fait rien d'intéressant, mais elle fournit une
interface de classe de base utile (Corrupt () et Reset ()), transmise aux fonctions virtuelles qui
peut être sous-classé. Considérons ensuite ce que nous appelons un BasicErrorModel qui est basé sur
le ns-2 Classe ErrorModel (dans ns-2/queue/errmodel.{cc,h}).
Quelles propriétés voulons-nous que cela ait, du point de vue de l'interface utilisateur ? Nous voudrions
pour que l'utilisateur puisse échanger trivialement le type d'ErrorModel utilisé dans le
NetDevice. Nous aimerions également avoir la possibilité de définir des paramètres configurables.
Voici quelques exigences simples que nous considérerons :
· Possibilité de définir la variable aléatoire qui régit les pertes (la valeur par défaut est UniformVariable)
· Possibilité de définir l'unité (bit, octet, paquet, temps) de granularité sur laquelle les erreurs sont
appliqué.
· Possibilité de définir le taux d'erreurs (par exemple 10^-3) correspondant à l'unité de
granularité.
· Possibilité d'activer/désactiver (la valeur par défaut est activée)
Comment à Sous-classe
Nous déclarons BasicErrorModel comme étant une sous-classe de ErrorModel comme suit :
classe BasicErrorModel : public ErrorModel
{
publique:
statique TypeId GetTypeId (void);
privé:
// Implémenter les fonctions virtuelles pures de la classe de base
bool virtuel DoCorrupt (Ptr p);
bool virtuel DoReset (vide);
}
et configurez la fonction de sous-classe GetTypeId en définissant une chaîne TypeId unique et
définir le parent sur ErrorModel :
TypeId RateErrorModel :: GetTypeId (vide)
{
statique TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
Silhouette Core Les fonctions et Unité Tests
Affirmer Macros
Écriture Unité Tests
L'ajout de a Équipement Module à ns-3
Lorsque vous avez créé un groupe de classes, d'exemples et de tests associés, ils peuvent être
combinés ensemble dans un ns-3 module afin qu'ils puissent être utilisés avec les ns-3 modules
et par d'autres chercheurs.
Ce chapitre vous guide à travers les étapes nécessaires pour ajouter un nouveau module à ns-3.
étape 0 - Module Mise En Page
Tous les modules se trouvent dans le src annuaire. Chaque module se trouve dans un répertoire
qui porte le même nom que le module. Par exemple, le spectre module peut être trouvé ici:
src/spectre. Nous citerons le spectre modules à titre d'illustration.
Un module prototype a la structure de répertoires et les fichiers requis suivants :
src /
nom-module/
reliures/
doc/
exemples/
wscript
assistant/
maquette/
test /
exemples-à-exécuter.py
wscript
Tous les répertoires ne seront pas présents dans chaque module.
étape 1 - Créer a Module Squelette
Un programme python est fourni dans le répertoire source qui créera un squelette pour un nouveau
module. Pour les besoins de cette discussion, nous supposerons que votre nouveau module s'appelle
nouveau-module. De l' src répertoire, procédez comme suit pour créer le nouveau module :
$ ./create-module.py nouveau-module
Ensuite, cd développement nouveau-module; vous trouverez cette disposition de répertoire :
$ cd nouveau-module
$ls
doc exemples modèle d'aide test wscript
Plus en détail, le créer-module.py le script créera les répertoires ainsi que les initiales
squelette wscript, .h, . Cc et .première des dossiers. Le module complet avec des fichiers squelette ressemble
comme ça:
src /
nouveau-module/
doc/
nouveau-module.rst
exemples/
nouveau-module-exemple.cc
wscript
assistant/
nouveau-module-helper.cc
nouveau-module-helper.h
maquette/
nouveau-module.cc
nouveau-module.h
test /
nouveau-module-test-suite.cc
wscript
(Si nécessaire, le reliures/ répertoire répertorié dans Step-0 sera créé automatiquement pendant
la construction.)
Nous verrons ensuite comment personnaliser ce module. Délation waf sur les fichiers qui
composer votre module se fait en éditant les deux wscript des dossiers. Nous traverserons le
principales étapes de ce chapitre.
Tous ns-3 les modules dépendent de la core module et généralement sur d'autres modules. Cette dépendance
est spécifié dans le wscript fichier (au niveau supérieur du module, pas le fichier séparé wscript
déposer dans le exemples annuaire!). Dans le squelette wscript l'appel qui déclarera votre
nouveau module pour waf ressemblera à ceci (avant édition):
construction def(bld):
module = bld.create_ns3_module('nouveau-module', ['core'])
Supposons que nouveau-module Depend de Internet, mobilité et aodv modules. Après
le modifier le wscript le fichier doit ressembler à:
construction def(bld):
module = bld.create_ns3_module('nouveau-module', ['internet', 'mobilité', 'aodv'])
Notez que seules les dépendances de module de premier niveau doivent être répertoriées, c'est pourquoi nous avons supprimé
core; la Internet module dépend à son tour de core.
Votre module aura très probablement des fichiers source de modèle. Squelettes initiaux (qui seront
compiler avec succès) sont créés dans modèle/nouveau-module.cc et modèle/nouveau-module.h.
Si votre module aura des fichiers source d'assistance, ils iront dans le assistant/
annuaire; encore une fois, les squelettes initiaux sont créés dans ce répertoire.
Enfin, il est de bonne pratique d'écrire des tests et des exemples. Ceux-ci seront presque certainement
requis pour que de nouveaux modules soient acceptés dans le programme officiel ns-3 arbre source. Un squelette
la suite de tests et le cas de test sont créés dans le test / annuaire. La suite de tests squelette sera
contiennent le constructeur ci-dessous, qui déclare un nouveau test unitaire nommé nouveau-module, avec une
cas de test unique composé de la classe NouveauModuleTestCase1:
NouveauModuleTestSuite::NouveauModuleTestSuite ()
: TestSuite ("nouveau-module", UNITÉ)
{
AddTestCase (nouveau NouveauModuleTestCase1);
}
étape 3 - Déclarer Source Fichiers
L'en-tête public et les fichiers de code source de votre nouveau module doivent être spécifiés dans le
wscript fichier en le modifiant avec votre éditeur de texte.
Par exemple, après avoir déclaré le spectre module, le src/spectre/wscript précise le
fichiers de code source avec la liste suivante :
construction def(bld):
module = bld.create_ns3_module('spectre', ['internet', 'propagation', 'antenne', 'applications'])
module.source = [
'modèle/spectre-modèle.cc',
'modèle/valeur-spectre.cc',
.
.
.
'modèle/micro-ondes-four-spectre-valeur-helper.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
'helper/waveform-generator-helper.cc',
'helper/spectrum-analyzer-helper.cc',
]
Les objets issus de la compilation de ces sources seront assemblés dans une bibliothèque de liens,
qui sera lié à tous les programmes s'appuyant sur ce module.
Mais comment ces programmes apprennent-ils l'API publique de notre nouveau module ? Continuer à lire!
étape 4 - Déclarer Public En-tête Fichiers
Les fichiers d'en-tête définissant l'API publique de votre modèle et les assistants doivent également être
spécifié dans le wscript fichier.
Poursuivre avec le spectre illustration du modèle, les fichiers d'en-tête publics sont spécifiés
avec la strophe suivante. (Notez que l'argument de la bld la fonction indique waf à
installer les en-têtes de ce module avec les autres ns-3 en-têtes) :
en-têtes = bld(features='ns3header')
headers.module = 'spectre'
en-têtes.source = [
'modèle/spectre-modèle.h',
'modèle/valeur-spectre.h',
.
.
.
'modèle/micro-ondes-four-spectre-valeur-helper.h',
'helper/spectre-helper.h',
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
'helper/waveform-generator-helper.h',
'helper/spectrum-analyzer-helper.h',
]
Les en-têtes ainsi rendus publics seront accessibles aux utilisateurs de votre modèle avec include
des déclarations comme
#include "ns3/spectrum-model.h"
Les en-têtes utilisés strictement en interne dans votre implémentation ne doivent pas être inclus ici. Ils
sont toujours accessibles à votre implémentation en incluant des déclarations telles que
#include "my-module-implementation.h"
étape 5 - Déclarer Tests
Si votre nouveau module comporte des tests, ils doivent être spécifiés dans votre wscript classer par
en le modifiant avec votre éditeur de texte.
La spectre les tests de modèle sont spécifiés avec la strophe suivante :
module_test = bld.create_ns3_module_test_library('spectre')
module_test.source = [
'test/spectre-interférence-test.cc',
'test/spectre-valeur-test.cc',
]
See Tests pour plus d'informations sur la façon d'écrire des cas de test.
étape 6 - Déclarer Exemples
Si votre nouveau module a des exemples, alors ils doivent être spécifiés dans votre exemples/wscript
dossier. (Le squelette de niveau supérieur wscript inclura récursivement exemples/wscript seulement si
les exemples ont été activés au moment de la configuration.)
La spectre modèle définit son premier exemple dans src/spectre/exemples/wscript avec
construction def(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
['spectre', 'mobilité'])
obj.source = 'adhoc-aloha-ideal-phy.cc'
Notez que le deuxième argument de la fonction create_ns3_program() est la liste des modules
dont dépend le programme en cours de création ; encore une fois, n'oubliez pas d'inclure nouveau-module in
la liste. Il est préférable de ne répertorier que les dépendances directes du module et de laisser waf
en déduire l'arbre de dépendance complet.
Parfois, pour plus de clarté, vous pouvez diviser l'implémentation de votre exemple entre
plusieurs fichiers sources. Dans ce cas, incluez simplement ces fichiers en tant que fichiers explicites supplémentaires
sources de l'exemple :
obj = bld.create_ns3_program('nouveau-module-exemple', [nouveau-module])
obj.source = ['nouveau-module-exemple.cc', 'nouveau-module-exemple-partie.cc']
Les exemples Python sont spécifiés à l'aide de l'appel de fonction suivant. A noter que la deuxième
argument de la fonction registre_ns3_script() est la liste des modules que Python
exemple dépend de :
bld.register_ns3_script('nouveau-module-exemple.py', ['nouveau-module'])
étape 7 - Exemples Courir as Tests
En plus d'exécuter du code de test explicite, le framework de test peut également être instrumenté pour
exécutez des programmes d'exemple complets pour essayer d'attraper les régressions dans les exemples. Cependant, tous
les exemples conviennent aux tests de régression. Le fichier test/exemples-à-exécuter.py contrôle la
invocation des exemples lors de l'exécution du framework de test.
La spectre exemples de modèles exécutés par test.py sont spécifiés dans
src/spectre/test/exemples-à-exécuter.py en utilisant les deux listes suivantes de C++ et Python
exemples:
# Une liste d'exemples C++ à exécuter afin de s'assurer qu'ils restent
# constructible et exécutable dans le temps. Chaque tuple de la liste contient
#
# (nom_exemple, do_run, do_valgrind_run).
#
# Voir test.py pour plus d'informations.
exemples_cpp = [
("adhoc-aloha-ideal-phy", "Vrai", "Vrai"),
("adhoc-aloha-ideal-phy-with-microwave-oven", "True", "True"),
("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "Vrai", "Vrai"),
]
# Une liste d'exemples Python à exécuter afin de s'assurer qu'ils restent
# exécutable dans le temps. Chaque tuple de la liste contient
#
# (exemple_nom, do_run).
#
# Voir test.py pour plus d'informations.
python_exemples = [
("exemple-simulateur.py", "Vrai"),
]
Comme indiqué dans le commentaire, chaque entrée de la liste C++ d'exemples à exécuter contient le
tuple (exemple_nom, faire_exécuter, do_valgrind_run), Où
· exemple_nom est l'exécutable à exécuter,
· faire_exécuter est une condition sous laquelle exécuter l'exemple, et
· do_valgrind_run est une condition sous laquelle exécuter l'exemple sous valgrind. (Cette
est nécessaire car NSC provoque des plantages d'instructions illégales avec certains tests lorsqu'ils
sont exécutés sous valgrind.)
Notez que les deux conditions sont des instructions Python qui peuvent dépendre de waf paramétrage
variables. Par exemple,
("tcp-nsc-lfn", "NSC_ENABLED == Vrai", "NSC_ENABLED == Faux"),
Chaque entrée de la liste Python des exemples à exécuter contient le tuple (exemple_nom,
faire_run), où, comme pour les exemples C++,
· exemple_nom est le script Python à exécuter, et
· faire_exécuter est une condition sous laquelle exécuter l'exemple.
Encore une fois, la condition est une instruction Python qui peut dépendre de waf variables de configuration.
Par exemple,
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
étape 8 - Configurez et Silhouette
Vous pouvez maintenant configurer, compiler et tester votre module normalement. Vous devez reconfigurer le
projet comme première étape afin que waf met en cache les nouvelles informations dans votre wscript des fichiers, ou
sinon votre nouveau module ne sera pas inclus dans la construction.
$ ./waf configurer --enable-examples --enable-tests
$ ./waf construit
$ ./test.py
Cherchez la suite de tests de votre nouveau module (et des exemples de programmes, si votre module a des
activé) dans la sortie de test.
étape 9 - Python fixations
L'ajout de liaisons Python à votre module est facultatif et l'étape est commentée par
par défaut dans le créer-module.py scripts.
# bld.ns3_python_bindings()
Si vous souhaitez inclure des liaisons Python (nécessaires uniquement si vous souhaitez écrire Python ns-3
programmes au lieu des programmes C++ ns-3), vous devez décommenter ce qui précède et installer le
Système d'analyse de l'API Python (traité ailleurs dans ce manuel) et analysez votre module pour
générer de nouvelles liaisons.
La création Documentation
ns-3 fournit deux types de documentation : des chapitres explicatifs de style "guide de l'utilisateur", et
documentation de l'API du code source.
Les chapitres "guide d'utilisation" sont écrits à la main Texterestructuré format (.première), lequel est
traité par le système de documentation Python Sphinx pour générer des pages web et des fichiers pdf.
La documentation de l'API est générée à partir du code source lui-même, en utilisant doxygen, générer
pages Web croisées. Ces deux éléments sont importants : les chapitres sur le Sphinx expliquent why
et aperçu de l'utilisation d'un modèle ; la documentation de l'API explique how détails.
Ce chapitre donne un aperçu rapide de ces outils, en mettant l'accent sur l'utilisation préférée et
personnalisations pour ns-3.
Pour construire toute la documentation standard :
$ ./documents waf
Pour des options plus spécialisées, lisez la suite.
Documenter avec Sphinx
Nous utilisons Sphinx générer des chapitres explicatifs décrivant la conception et l'utilisation de chaque
module. En ce moment, vous lisez le Documentation Chapitre. La Afficher Source maillon de la
la barre latérale vous montrera la source reStructuredText pour ce chapitre.
L'ajout de Équipement Chapitres
L'ajout d'un nouveau chapitre s'effectue en trois étapes (décrites plus en détail ci-dessous) :
1. Choisir Où? le ou les fichiers de documentation vivront.
2. Lien d'une page existante vers la nouvelle documentation.
3. Ajoutez le nouveau fichier au Makefile.
Où?
Documentation pour un module spécifique, foo, devrait normalement entrer src/foo/doc/. Par exemple
src/foo/doc/foo.rst serait le document de niveau supérieur pour le module. La
src/create-module.py script créera ce fichier pour vous.
Certains modèles nécessitent plusieurs .première fichiers et chiffres ; ceux-ci devraient tous aller dans le
src/foo/doc/ annuaire. Les docs sont en fait construits par un Sphinx Makefile. Pour surtout
documentation impliquée, il peut être utile d'avoir un local Makefile dans le src/foo/doc/
répertoire pour simplifier la construction de la documentation de ce module (Antenna est un exemple).
La configuration n'est pas particulièrement difficile, mais dépasse le cadre de ce chapitre.
Dans certains cas, la documentation couvre plusieurs modèles ; la Réseau chapitre en est un exemple. Dans
ces cas en ajoutant le .première fichiers directement sur doc/modèles/source/ pourrait être approprié.
Lien
Sphinx doit savoir où votre nouveau chapitre devrait apparaître. Dans la plupart des cas, un nouveau modèle
chapitre doit apparaître dans Modèles photo livre. Pour y ajouter votre chapitre, modifiez
doc/models/source/index.rst
.. toctree ::
: profondeur max : 1
organisation
animation
antenne
aodv
applications
Ajoutez le nom de votre document (sans le .première extension) à cette liste. Veuillez conserver le
Modélisez les chapitres par ordre alphabétique, pour faciliter la recherche visuelle de chapitres spécifiques.
Makefile
Vous devez également ajouter votre document au bon Makefile, De sorte a prendre une sait le vérifier
pour les mises à jour. Le Makefile du livre de modèles est doc/modèles/Makefile, le livre manuel Makefile est
doc/manuel/Makefile.
# répertorie tous les fichiers .rst de la bibliothèque de modèles qui doivent être copiés dans $SOURCETEMP
SOURCES = \
source/conf.py \
source/_statique \
source/index.rst \
source/remplacer.txt \
source/organisation.rst \
$(SRC)/antenne/doc/source/antenne.rst \
Vous ajoutez votre .première fichiers vers le SOURCES variable. Pour ajouter des chiffres, lisez les commentaires dans le
Makefile pour voir quelle variable doit contenir vos fichiers image. Encore une fois, veuillez conserver ces
par ordre alphabétique.
Développement Sphinx Docs
Construire la documentation Sphinx est assez simple. Pour construire tout le Sphinx
Documentation:
$ ./waf sphinx
Pour créer uniquement la documentation des modèles :
$ make -C doc/modèles
Pour voir la documentation générée, pointez votre navigateur sur doc/modèles/build/html.
Comme vous pouvez le voir, Sphinx utilise Make pour guider le processus. La cible par défaut construit tous
activé les formulaires de sortie, qui dans ns-3 sont les multi-pages html, une seule page html unique et
pdf (latex). Pour créer uniquement le code HTML multipage, vous ajoutez le html Cible:
$ make -C doc/modèles html
Cela peut être utile pour réduire le temps de construction (et la taille du bavardage de construction) lorsque vous
rédigez votre chapitre.
Avant de valider votre documentation dans le dépôt, veuillez vérifier qu'elle se construit sans
erreurs ou avertissements. Le processus de construction génère beaucoup de sortie (principalement des bavardages normaux
de LaTeX), ce qui peut rendre difficile de voir s'il y a des avertissements Sphinx ou
les erreurs. Pour rechercher des avertissements et des erreurs importants, créez uniquement le html version, puis recherchez
le journal de construction pour avertissement or erreur.
ns-3 Spécifiques
Le Sphinx Documentation et tutoriel sont plutôt bons. Nous ne dupliquerons pas les bases
ici, en se concentrant plutôt sur l'utilisation préférée pour ns-3.
· Commencez les documents par ces deux lignes :
.. inclure :: remplacer.txt
.. surbrillance :: cpp
La première ligne permet quelques remplacements simples. Par exemple, en tapant |ns3| rend comme
ns-3. La seconde définit explicitement le langage de mise en évidence du code source par défaut pour le
fichier, car l'estimation de l'analyseur n'est pas toujours précise. (Il est également possible de régler le
langage explicitement pour un seul bloc de code, voir ci-dessous.)
· Sections:
Sphinx est assez libéral sur le marquage des en-têtes de section. Par convention, nous préférons ceci
hiérarchie:
.. hiérarchie des en-têtes :
-------------- Chapitre
************* Section (#.#)
============= Sous-section (#.#.#)
############# Sous-sous-section
· Mise en évidence de la syntaxe :
Pour utiliser le surligneur de syntaxe par défaut, démarrez simplement un bloc de code source :
┌─ase ───────────────────────────────┐
│Source Sphinx │ Sortie rendue │
├─ase ───────────────────────────────┤
│ │ Le Frobnitz est accessible par : │
│ Le ``Frobnitz`` est accessible par :: │ │
│ │ Foo::Frobnitz frob ; │
│ Foo :: Frobnitz frob ; │ frob.Set (...) ; │
│ frob.Set (...) ; │ │
└─ase ───────────────────────────────┘
Pour utiliser un surligneur de syntaxe spécifique, par exemple, bash commandes shell :
┌─ase ───┐
│Source Sphinx │ Sortie rendue │
├─ase ───┤
│ │
│ .. code source :: bash │ $ ls │
│ │
│ $ ls │ │
└─ase ───┘
· Notations abrégées :
Ces raccourcis sont définis :
-
│Source Sphinx │ Sortie rendue │
-
│ ns-3 │
│ |ns3| │ │
-
│ ns-2 │
│ |ns2| │ │
-
│ │
│ |vérifier| │ │
-
│ RFC 6282 │
│ :rfc:`6282` │ │
-
Documenter avec doxygen
Nous utilisons doxygen générer navigable Documentation API. Doxygen fournit un certain nombre de
fonctionnalités utiles :
· Tableau récapitulatif de tous les membres de la classe.
· Graphes d'héritage et de collaboration pour toutes les classes.
· Liens vers le code source implémentant chaque fonction.
· Liens vers chaque endroit où un membre est utilisé.
· Liens vers chaque objet utilisé dans la mise en œuvre d'une fonction.
· Regroupement de classes liées, telles que toutes les classes liées à un protocole spécifique.
De plus, nous utilisons le ID de type système à ajouter à la documentation pour chaque classe
· Le Config chemins par lesquels ces objets peuvent être atteints.
· Documentation pour tout Attributs, dont Attributs définis dans les classes parentes.
· Documentation pour tout Tracer sources définies par la classe.
Doxygen fonctionne en scannant le code source, à la recherche de commentaires spécialement marqués. Ce
crée également une référence croisée, indiquant où chaque fichier, classe, méthode et variable est
utilisé.
Favoris Style
Le style préféré pour les commentaires Doxygen est le style JavaDoc :
/ **
* Brève description de cette classe ou méthode.
* Les lignes adjacentes deviennent un seul paragraphe.
*
* Description plus longue, avec beaucoup de détails.
*
* Les lignes blanches séparent les paragraphes.
*
* Expliquez ce que fait la classe ou la méthode, en utilisant quel algorithme.
* Expliquer les unités d'arguments et les valeurs de retour.
*
* \note Notez toutes les limitations ou pièges.
*
* (Pour les fonctions avec arguments ou valeur de retour :)
* \param foo Phrase nominale brève décrivant cet argument.
* \param bar Remarque Casse de la phrase et période de fin.
* \return Brève phrase nominale décrivant la valeur.
*
* \interne
*
* Vous pouvez également discuter des détails de mise en œuvre interne.
* Comprendre ce matériel ne devrait pas être nécessaire pour utiliser
* la classe ou la méthode.
*/
classe Exemple
Dans ce style, le bloc de commentaires Doxygen commence par deux caractères `*' : / **, et précède
l'élément documenté.
Pour les articles ne nécessitant qu'une brève description, l'une ou l'autre de ces formes courtes est appropriée :
/** Implémentation du destructeur. */
void DoDispose ();
int m_count ; //!< Compte de ...
Notez la forme spéciale du commentaire de fin de ligne, // !, indiquant qu'il fait référence à la
précédant article.
Quelques éléments à noter :
· Utilisez la casse des phrases, y compris la majuscule initiale.
· Utilisez la ponctuation, en particulier les `.' à la fin des phrases ou des expressions.
· Le \bref la balise n'est pas nécessaire ; la première phrase sera utilisée comme brève
la description.
Chaque classe, méthode, typedef, variable membre, argument de fonction et valeur de retour doit
être documenté dans tous les fichiers de code source qui forment l'API formelle et l'implémentation pour
ns-3 tels que src/ /maquette/*, src/ /assistant/* et src/ /utils/*.
Documentation pour les articles dans src/ /test/* et src/ /exemples/* est préféré,
mais pas obligatoire.
Information Caractéristiques
· Les membres hérités hériteront automatiquement des documents du parent (mais peuvent être remplacés
par la documentation locale).
1. Documentez la classe de base.
2. Dans la sous-classe, marquez les fonctions héritées avec un commentaire ordinaire :
// Méthodes héritées
vide virtuel FooBar (vide);
int virtuel BarFoo (double baz);
Notez que les signatures doivent correspondre exactement, alors incluez l'argument formel (vide)
Cela ne fonctionne pas pour les fonctions statiques ; voir ObtenirTypeId, ci-dessous, pour un exemple.
Développement doxygen Docs
Construire la documentation Doxygen est assez simple :
$ ./waf doxygène
Cela se construit en utilisant la configuration par défaut, qui génère des sections de documentation pour
tous éléments, même s'ils n'ont pas de blocs de documentation de commentaires explicites. Cela a le
effet de supprimer les avertissements pour les éléments non documentés, mais s'assure que tout apparaît
dans la sortie générée.
Lors de la rédaction d'une documentation, il est souvent plus utile de voir quels éléments génèrent
des avertissements, généralement concernant des documents manquants. Pour voir la liste complète des avertissements, utilisez le
doc/doxygen.warnings.report.sh script:
$ doc/doxygen.warnings.report.sh
Waf : Entrer dans le répertoire `build'
Waf : sortie du répertoire `build'
'construction' terminée avec succès (3m24.094s)
Reconstruire les documents doxygen avec des erreurs complètes... Fait.
Rapport des avertissements Doxygen
----------------------------------------
(Tous les nombres sont des limites inférieures.)
Avertissements par module/répertoire :
Compter le répertoire
---------- ----------------------------------
3844 src/lte/modèle
1718 src/wimax/modèle
1423 code source/noyau/modèle
....
138 paramètres supplémentaires non documentés.
----------------------------------------
15765 avertissements au total
126 répertoires avec avertissements
Avertissements par fichier (alphabétique)
Fichier de comptage
---------- ----------------------------------
17 doc/introspected-doxygen.h
15 exemples/routing/manet-routing-compare.cc
26 exemples/statistiques/wifi-example-apps.h
....
----------------------------------------
967 fichiers avec avertissements
Avertissements par fichier (numérique)
Fichier de comptage
---------- ----------------------------------
374 src/lte/model/lte-asn1-header.h
280 src/lte/model/lte-rrc-sap.h
262 src/lte/model/lte-rrc-header.h
....
----------------------------------------
967 fichiers avec avertissements
Résumé des avertissements Doxygen
----------------------------------------
126 répertoires
Fichiers 967
Avertissements 15765
Le script modifie la configuration pour afficher tous les avertissements et pour raccourcir le temps d'exécution.
Comme vous pouvez le voir, au moment où nous écrivons, nous avons a lot d'objets sans papiers. Le rapport
récapitule les avertissements par module source/*/*, et par dossier, par ordre alphabétique et numérique.
Le script a quelques options pour réduire les choses et rendre cela plus gérable. Pour aider,
utiliser le -h option. Après l'avoir exécuté une fois pour faire la construction Doxygen et générer le plein
journal des avertissements, vous pouvez retraiter le fichier journal avec divers "filtres", sans avoir à faire
la version complète de Doxygen, en utilisant à nouveau le -s option. Vous pouvez exclure les avertissements de
*/exemples/* fichiers (-e option), et/ou */test/* fichiers (-t).
L'option la plus utile lors de la rédaction de commentaires de documentation est peut-être -m , Qui
limitera le rapport aux seuls fichiers correspondant src/ /*, et suivre le rapport avec
les lignes d'avertissement réelles. Combiner avec eth et vous pouvez vous concentrer sur les avertissements qui sont
les plus urgents dans un seul module :
$ doc/doxygen.warnings.report.sh -m mesh/helper
Résumé des avertissements Doxygen
----------------------------------------
1 répertoires
Fichiers 3
Avertissements 149
Avertissements filtrés
==================================
src/mesh/helper/dot11s/dot11s-installer.h:72 : avertissement : le membre m_root (variable) de la classe ns3::Dot11sStack n'est pas documenté.
src/mesh/helper/dot11s/dot11s-installer.h:35 : avertissement : le type de retour du membre ns3 ::Dot11sStack ::GetTypeId n'est pas documenté
src/mesh/helper/dot11s/dot11s-installer.h:56 : avertissement : le type de retour du membre ns3::Dot11sStack::InstallStack n'est pas documenté
src/mesh/helper/flame/lfame-installer.h:40 : avertissement : le membre GetTypeId() (fonction) de la classe ns3::FlameStack n'est pas documenté.
src/mesh/helper/flame/flame-installer.h:60 : avertissement : le type de retour du membre ns3::FlameStack::InstallStack n'est pas documenté
src/mesh/helper/mesh-helper.h:213 : avertissement : le membre m_nInterfaces (variable) de la classe ns3::MeshHelper n'est pas documenté.
src/mesh/helper/mesh-helper.h:214 : avertissement : le membre m_spreadChannelPolicy (variable) de la classe ns3::MeshHelper n'est pas documenté.
src/mesh/helper/mesh-helper.h:215 : avertissement : le membre m_stack (variable) de la classe ns3::MeshHelper n'est pas documenté.
src/mesh/helper/mesh-helper.h:216 : avertissement : le membre m_stackFactory (variable) de la classe ns3::MeshHelper n'est pas documenté.
src/mesh/helper/mesh-helper.h:209 : avertissement : les paramètres du membre ns3::MeshHelper::CreateInterface ne sont pas (tous) documentés
src/mesh/helper/mesh-helper.h:119 : avertissement : les paramètres du membre ns3::MeshHelper::SetStandard ne sont pas (tous) documentés
Maintenant, c'est juste une question de comprendre le code, et d'écrire quelques docs !
ns-3 Spécifiques
Quant au Sphinx, le Doxygen docs et référence sont plutôt bons. Nous ne dupliquerons pas le
bases ici, en se concentrant plutôt sur l'utilisation préférée pour ns-3.
· Utiliser Doxygen formation vidéo pour regrouper les éléments associés.
Dans l'en-tête principal d'un module, créez un groupe Doxgyen :
/ **
* \defgroup foo Protocole Foo.
*/
Marquez chaque classe associée comme appartenant au groupe :
/ **
* \ingroup foo
*
* Type de paquet Foo.
*/
classe foo
· Le saviez-vous définitions de type peut avoir des arguments formels? Cela permet de documenter la fonction
signatures de pointeur :
/ **
* Signature de la fonction de rappel de la barre.
*
* \param ale La taille d'une pinte de bière, en onces impériales.
*/
typedef void (* BarCallback)(const in ale);
· Copiez le Attribut chaînes d'aide de la ObtenirTypeId méthode à utiliser comme brief
descriptions des membres associés.
· \bugid{298} créera un lien vers le bogue 298 dans notre Bugzilla.
· \pnom{foo} dans une description formatera foo en tant que \param foo paramètre, en précisant
que vous faites référence à un argument réel.
· \RFC{301} créera un lien vers la RFC 301.
· \interne doit être utilisé uniquement pour lancer une discussion sur les détails de la mise en œuvre, et non pour
marque Privé fonctions (elles sont déjà marquées, comme Privé!)
· Ne créez pas de classes avec des noms triviaux, tels que classe A, même dans les suites de tests. Ces
provoque le rendu de toutes les instances du littéral de nom de classe "A" sous forme de liens.
Comme indiqué ci-dessus, les fonctions statiques n'héritent pas de la documentation des mêmes fonctions dans
la classe mère. ns-3 utilise quelques fonctions statiques de manière omniprésente ; le suggéré
bloc de documentation pour ces cas est :
· Constructeur/destructeur par défaut :
Ma classe (); //!< Constructeur par défaut
~MaClasse (); //!< Destructeur
· Destructeur factice et DoDispose :
/** Destructeur factice, voir DoDispose. */
~MaClasse ();
/** Implémentation du destructeur */
vide virtuel DoDispose ();
· GetTypeID :
/ **
* Enregistrez ce type.
* \return L'objet TypeId.
*/
statique TypeId GetTypeId (void);
Activation Sous-ensembles of ns-3 formation vidéo
Comme pour la plupart des projets logiciels, ns-3 est de plus en plus grand en termes de nombre de modules,
lignes de code et empreinte mémoire. Les utilisateurs, cependant, ne peuvent utiliser que quelques-uns de ces modules
à la fois. Pour cette raison, les utilisateurs peuvent souhaiter n'activer explicitement que le sous-ensemble de
possible ns-3 modules dont ils ont réellement besoin pour leurs recherches.
Ce chapitre explique comment activer uniquement ns-3 les modules qui vous intéressent
utilisant.
Comment à permettre a sous-ensemble of ns-3's modules
Si des bibliothèques partagées sont en cours de construction, l'activation d'un module entraînera au moins un
bibliothèque à construire :
libns3-nommodule.so
Si le module a une bibliothèque de tests et que des bibliothèques de tests sont en cours de construction, alors
libns3-nommodule-test.so
sera également construit. Autres modules dont dépend le module et leurs bibliothèques de test
seront également construits.
Par défaut, tous les modules sont intégrés ns-3. Il existe deux façons d'activer un sous-ensemble de ces
modules:
1. Utilisation de l'option --enable-modules de waf
2. Utilisation de l' ns-3 fichier de configuration
Permettre modules en utilisant waf's --enable-modules option
Pour activer uniquement le module principal avec exemple et tests, par exemple, essayez ces commandes :
$ ./waf propre
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf construit
$ construction cd/débogage/
$ls
et les bibliothèques suivantes doivent être présentes :
liaisons libns3-core.so ns3 scratch utils
exemples libns3-core-test.so exemples src
Noter la ./waff espace extérieur plus propre, l'étape est faite ici uniquement pour rendre plus évident quelles bibliothèques de modules
ont été construits. Tu n'as pas à faire ./waff espace extérieur plus propre, afin d'activer des sous-ensembles de modules.
L'exécution de test.py entraînera uniquement l'exécution des tests qui dépendent du noyau du module :
24 des 24 tests réussis (24 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Répétez les étapes ci-dessus pour le module "réseau" au lieu du module "core", et le
suivant sera construit, puisque le réseau dépend du noyau :
liaisons libns3-core.so libns3-network.so ns3 scratch utils
exemples libns3-core-test.so libns3-network-test.so exemples src
L'exécution de test.py entraînera l'exécution des tests qui dépendent uniquement des modules principaux et réseau.
Être exécuté:
31 des 31 tests réussis (31 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Permettre modules en utilisant le ns-3 paramétrage filet
Un fichier de configuration, .ns3rc, a été ajouté à ns-3 qui permet aux utilisateurs de spécifier
les modules doivent être inclus dans la construction.
Lors de l'activation d'un sous-ensemble de ns-3 modules, les règles de priorité sont les suivantes :
1. la chaîne de configuration --enable-modules remplace tout fichier .ns3rc
2. le fichier .ns3rc au niveau supérieur ns-3 répertoire est ensuite consulté, s'il est présent
3. le système recherche ~/.ns3rc si les deux ci-dessus ne sont pas spécifiés
Si rien de ce qui précède ne limite les modules à construire, tous les modules connus de waf seront
être construit.
La version maintenue du fichier .ns3rc dans le ns-3 le référentiel de code source réside dans
le utils annuaire. La raison en est que s'il se trouvait dans le répertoire de niveau supérieur du
référentiel, il serait sujet à des enregistrements accidentels de la part des responsables qui permettent le
modules qu'ils souhaitent utiliser. Par conséquent, les utilisateurs doivent copier manuellement le .ns3rc à partir du
utils répertoire à leur emplacement préféré (répertoire de niveau supérieur ou répertoire personnel) pour
activer la configuration de construction modulaire persistante.
En supposant que vous êtes au niveau supérieur ns-3 répertoire, vous pouvez obtenir une copie du fichier .ns3rc
fichier qui se trouve dans le utils répertoire comme suit :
$ cp utils/.ns3rc .
Le fichier .ns3rc devrait maintenant être dans votre niveau supérieur ns-3 répertoire, et il contient le
Suivante à la suite:
#! / usr / bin / env python
# Une liste des modules qui seront activés lors de l'exécution de ns-3.
# Les modules qui dépendent des modules répertoriés seront également activés.
#
# Tous les modules peuvent être activés en choisissant 'all_modules'.
modules_enabled = ['tous_les modules']
# Définissez ceci égal à true si vous voulez que les exemples soient exécutés.
exemples_enabled = Faux
# Définissez ceci égal à true si vous voulez que les tests soient exécutés.
tests_enabled = Faux
Utilisez votre éditeur préféré pour modifier le fichier .ns3rc afin d'activer uniquement le module principal avec
exemples et tests comme celui-ci:
#! / usr / bin / env python
# Une liste des modules qui seront activés lors de l'exécution de ns-3.
# Les modules qui dépendent des modules répertoriés seront également activés.
#
# Tous les modules peuvent être activés en choisissant 'all_modules'.
modules_enabled = ['core']
# Définissez ceci égal à true si vous voulez que les exemples soient exécutés.
exemples_enabled = Vrai
# Définissez ceci égal à true si vous voulez que les tests soient exécutés.
tests_enabled = Vrai
Seul le module principal sera activé maintenant si vous essayez ces commandes :
$ ./waf propre
$ ./waf configurer
$ ./waf construit
$ construction cd/débogage/
$ls
et les bibliothèques suivantes doivent être présentes :
liaisons libns3-core.so ns3 scratch utils
exemples libns3-core-test.so exemples src
Noter la ./waff espace extérieur plus propre, l'étape est faite ici uniquement pour rendre plus évident quelles bibliothèques de modules
ont été construits. Tu n'as pas à faire ./waff espace extérieur plus propre, afin d'activer des sous-ensembles de modules.
L'exécution de test.py entraînera uniquement l'exécution des tests qui dépendent du noyau du module :
24 des 24 tests réussis (24 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Répétez les étapes ci-dessus pour le module "réseau" au lieu du module "core", et le
suivant sera construit, puisque le réseau dépend du noyau :
liaisons libns3-core.so libns3-network.so ns3 scratch utils
exemples libns3-core-test.so libns3-network-test.so exemples src
L'exécution de test.py entraînera l'exécution des tests qui dépendent uniquement des modules principaux et réseau.
Être exécuté:
31 des 31 tests réussis (31 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Activation / désactivation ns-3 Tests et Exemples
La ns-3 distribution comprend de nombreux exemples et tests qui sont utilisés pour valider la ns-3
système. Cependant, les utilisateurs ne souhaitent pas toujours que ces exemples et tests soient exécutés pour leur
installation de ns-3.
Ce chapitre explique comment construire ns-3 avec ou sans ses exemples et tests.
Comment à activer désactiver exemples et tests in ns-3
Il existe 3 façons d'activer/désactiver les exemples et les tests dans ns-3:
1. Utiliser build.py quand ns-3 est construit pour la première fois
2. Utiliser waf une fois ns-3 a été construit
3. Utilisation de l' ns-3 fichier de configuration une fois ns-3 a été construit
Activer désactiver exemples et tests en utilisant build.py
Vous pouvez utiliser build.py pour activer/désactiver les exemples et les tests lorsque ns-3 est construit pour la première
le temps.
Par défaut, les exemples et les tests ne sont pas intégrés ns-3.
À partir du répertoire ns-3-allinone, vous pouvez créer ns-3 sans exemples ni tests simplement
en faisant:
$ ./build.py
Exécution de test.py au niveau supérieur ns-3 répertoire maintenant ne causera aucun exemple ou test à être
courir:
0 des 0 tests réussis (0 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Si vous souhaitez construire ns-3 avec des exemples et des tests, puis procédez comme suit à partir du
Répertoire ns-3-allinone :
$ ./build.py --enable-examples --enable-tests
Exécution de test.py au niveau supérieur ns-3 répertoire entraînera tous les exemples et tests
à exécuter :
170 des 170 tests réussis (170 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Activer désactiver exemples et tests en utilisant waf
Vous pouvez utiliser waf pour activer/désactiver les exemples et les tests une fois ns-3 a été construit.
Par défaut, les exemples et les tests ne sont pas intégrés ns-3.
Du plus haut niveau ns-3 répertoire, vous pouvez construire ns-3 sans exemples ni tests simplement
en faisant:
$ ./waf configurer
$ ./waf construit
L'exécution de test.py maintenant n'entraînera l'exécution d'aucun exemple ou test :
0 des 0 tests réussis (0 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Si vous souhaitez construire ns-3 avec des exemples et des tests, puis procédez comme suit à partir du haut
niveau ns-3 annuaire:
$ ./waf configurer --enable-examples --enable-tests
$ ./waf construit
L'exécution de test.py entraînera l'exécution de tous les exemples et tests :
170 des 170 tests réussis (170 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Activer désactiver exemples et tests en utilisant le ns-3 paramétrage filet
Un fichier de configuration, .ns3rc, a été ajouté à ns-3 qui permet aux utilisateurs de spécifier si
des exemples et des tests doivent être construits ou non. Vous pouvez utiliser ce fichier pour activer/désactiver
exemples et tests une fois ns-3 a été construit.
Lors de l'activation de la désactivation des exemples et des tests, les règles de priorité sont les suivantes :
1. les chaînes de configuration --enable-examples/--disable-examples remplacent tout fichier .ns3rc
2. les chaînes de configuration --enable-tests/--disable-tests remplacent tout fichier .ns3rc
3. le fichier .ns3rc au niveau supérieur ns-3 répertoire est ensuite consulté, s'il est présent
4. le système recherche ~/.ns3rc si le fichier .ns3rc n'a pas été trouvé à l'étape précédente
Si aucun des éléments ci-dessus n'existe, les exemples et les tests ne seront pas créés.
La version maintenue du fichier .ns3rc dans le ns-3 le référentiel de code source réside dans
le utils annuaire. La raison en est que s'il se trouvait dans le répertoire de niveau supérieur du
référentiel, il serait sujet à des enregistrements accidentels de la part des responsables qui permettent le
modules qu'ils souhaitent utiliser. Par conséquent, les utilisateurs doivent copier manuellement le .ns3rc à partir du
utils répertoire à leur emplacement préféré (répertoire de niveau supérieur ou répertoire personnel) pour
activer l'activation persistante des exemples et des tests.
En supposant que vous êtes au niveau supérieur ns-3 répertoire, vous pouvez obtenir une copie du fichier .ns3rc
fichier qui se trouve dans le utils répertoire comme suit :
$ cp utils/.ns3rc .
Le fichier .ns3rc devrait maintenant être dans votre niveau supérieur ns-3 répertoire, et il contient le
Suivante à la suite:
#! / usr / bin / env python
# Une liste des modules qui seront activés lors de l'exécution de ns-3.
# Les modules qui dépendent des modules répertoriés seront également activés.
#
# Tous les modules peuvent être activés en choisissant 'all_modules'.
modules_enabled = ['tous_les modules']
# Définissez ceci égal à true si vous voulez que les exemples soient exécutés.
exemples_enabled = Faux
# Définissez ceci égal à true si vous voulez que les tests soient exécutés.
tests_enabled = Faux
Du plus haut niveau ns-3 répertoire, vous pouvez construire ns-3 sans exemples ni tests simplement
en faisant:
$ ./waf configurer
$ ./waf construit
L'exécution de test.py maintenant n'entraînera l'exécution d'aucun exemple ou test :
0 des 0 tests réussis (0 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Si vous souhaitez construire ns-3 avec des exemples et des tests, utilisez votre éditeur préféré pour modifier
les valeurs dans le fichier .ns3rc pour les fichiers examples_enabled et tests_enabled doivent être True :
#! / usr / bin / env python
# Une liste des modules qui seront activés lors de l'exécution de ns-3.
# Les modules qui dépendent des modules répertoriés seront également activés.
#
# Tous les modules peuvent être activés en choisissant 'all_modules'.
modules_enabled = ['tous_les modules']
# Définissez ceci égal à true si vous voulez que les exemples soient exécutés.
exemples_enabled = Vrai
# Définissez ceci égal à true si vous voulez que les tests soient exécutés.
tests_enabled = Vrai
Du plus haut niveau ns-3 répertoire, vous pouvez construire ns-3 avec des exemples et des tests simplement en
Faire:
$ ./waf configurer
$ ./waf construit
L'exécution de test.py entraînera l'exécution de tous les exemples et tests :
170 des 170 tests réussis (170 réussi, 0 sauté, 0 échoué, 0 planté, 0 erreurs valgrind)
Dépannage
Ce chapitre publie des informations sur les erreurs éventuellement courantes lors de la construction ou de l'exécution
ns-3 programmes.
Veuillez noter que le wiki (http://www.nsnam.org/wiki/Troubleshooting) peut avoir contribué
articles.
Silhouette erreurs
Run-time erreurs
Parfois, des erreurs peuvent se produire avec un programme après une génération réussie. Ce sont des temps d'exécution
erreurs, et peuvent généralement se produire lorsque la mémoire est corrompue ou que les valeurs du pointeur sont inattendues
zéro.
Voici un exemple de ce qui pourrait arriver :
$ ./waf --run tcp-point à point
Entrer dans le répertoire '/home/tomh/ns-3-nsc/build'
Compilation terminée avec succès
Commande ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] terminée avec le code -11
Le message d'erreur indique que le programme s'est terminé sans succès, mais ce n'est pas clair
à partir de ces informations, ce qui pourrait être faux. Pour examiner de plus près, essayez de l'exécuter sous
le gdb débogueur:
$ ./waf --run tcp-point-to-point --command-template="gdb %s"
Entrer dans le répertoire '/home/tomh/ns-3-nsc/build'
Compilation terminée avec succès
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Copyright 2004 Fondation du logiciel libre, Inc.
GDB est un logiciel libre, couvert par la licence publique générale GNU, et vous êtes
bienvenue pour le modifier et/ou en distribuer des copies sous certaines conditions.
Tapez "afficher la copie" pour voir les conditions.
Il n'y a absolument aucune garantie pour GDB. Tapez "montrer la garantie" pour plus de détails.
Ce GDB a été configuré en tant que "i386-redhat-linux-gnu"... Utilisation de l'hôte libthread_db
bibliothèque "/lib/libthread_db.so.1".
(gdb) exécuter
Programme de démarrage : /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Lecture de symboles à partir d'un objet partagé lu à partir de la mémoire cible... terminé.
Système chargé fourni DSO à 0xf5c000
Le programme a reçu le signal SIGSEGV, Défaut de segmentation.
0x0804aa12 dans principal (argc=1, argv=0xbfdfefa4)
à ../examples/tcp-point-to-point.cc:136
136 points localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
1 $ = {m_ptr = 0x3c5d65}
(gdb) p socketFactory
2 $ = {m_ptr = 0x0}
(gdb) quitter
Le programme est en cours d'exécution. Sortir quand même ? (o ou n) o
Notez d'abord la façon dont le programme a été appelé - passez la commande à exécuter en tant qu'argument au
modèle de commande "gdb %s".
Cela nous indique qu'il y a eu une tentative de déréférencement d'un pointeur null socketFactory.
Regardons autour de la ligne 136 de tcp-point-à-point, comme le suggère gdb :
Ptr socketFactory = n2->GetObject (Tcp ::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Lier ();
Le coupable ici est que la valeur de retour de GetObject n'est pas vérifiée et peut être
zéro.
Parfois, vous devrez peut-être utiliser le valgrind Mémoire vérificateur pour des erreurs plus subtiles. Encore,
vous invoquez l'utilisation de valgrind de la même manière :
$ ./waf --run tcp-point-to-point --command-template="valgrind %s"
SOURCE
Ce document est rédigé en Texterestructuré pour Sphinx et est maintenu dans le
doc/manuel répertoire du code source de ns-3.
Utilisez ns-3-manual en ligne en utilisant les services onworks.net