EnglishFranceseCorsi

Favicon di OnWorks

ns-3-manual - Online nel cloud

Esegui ns-3-manual nel provider di hosting gratuito OnWorks su Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

Questo è il comando ns-3-manual che può essere eseguito nel provider di hosting gratuito OnWorks utilizzando una delle nostre molteplici workstation online gratuite come Ubuntu Online, Fedora Online, emulatore online Windows o emulatore online MAC OS

PROGRAMMA:

NOME


ns-3-manuale - ns-3 manuale

Questa è la NS-3 Manuale. La documentazione principale per il progetto ns-3 è disponibile in cinque
le forme:

· NS-3 Doxygen: Documentazione delle API pubbliche del simulatore

· Tutorial, Manuale (Questo documento)e la libreria di modelli per il con i più recenti rilasciare e
sviluppo albero

· NS-3 wiki

INDICE


Organizzazione
Questo capitolo descrive l'insieme NS-3 organizzazione del software e il corrispondente
organizzazione di questo manuale.

NS-3 è un simulatore di rete a eventi discreti in cui il nucleo e i modelli di simulazione sono
implementato in C++. NS-3 è costruito come una libreria che può essere statica o dinamica
collegato a un programma principale C++ che definisce la topologia della simulazione e avvia il
simulatore. NS-3 esporta anche quasi tutte le sue API in Python, consentendo ai programmi Python di
importare un modulo "ns3" più o meno allo stesso modo del NS-3 la libreria è collegata da eseguibili
in C ++.
[immagine] Organizzazione software di NS-3.INDENTATO

Il codice sorgente per NS-3 è per lo più organizzato nel src directory e può essere descritto
dal diagramma in Software organizzazione of NS-3. Lavoreremo a modo nostro dal basso
su; in generale, i moduli dipendono solo dai moduli sottostanti nella figura.

Per prima cosa descriviamo il nucleo del simulatore; quei componenti che sono comuni a tutti
protocollo, hardware e modelli ambientali. Il nucleo di simulazione è implementato in
sorgente/nucleo. I pacchetti sono oggetti fondamentali in un simulatore di rete e sono implementati in
src/rete. Questi due moduli di simulazione da soli sono destinati a comprendere a
nucleo di simulazione generico che può essere utilizzato da diversi tipi di reti, non solo
Reti basate su Internet. I moduli di cui sopra di NS-3 sono indipendenti dalla rete specifica
e modelli di dispositivi, che sono trattati nelle parti successive di questo manuale.

In aggiunta a quanto sopra NS-3 core, introduciamo, anche nella porzione iniziale del
manuale, altri due moduli che integrano l'API principale basata su C++. NS-3 i programmi possono
accedere direttamente a tutte le API o utilizzare un cosiddetto aiutante API quello fornisce
wrapper convenienti o incapsulamento di chiamate API di basso livello. Il fatto che NS-3 programmi
può essere scritto su due API (o una loro combinazione) è un aspetto fondamentale del
simulatore. Descriviamo anche come Python è supportato in NS-3 prima di passare allo specifico
modelli rilevanti per la simulazione di rete.

Il resto del manuale è incentrato sulla documentazione dei modelli e sul supporto
capacità. La parte successiva si concentra su due oggetti fondamentali in NS-3: il Nodo e
NetDevice. Due tipi speciali di NetDevice sono progettati per supportare l'uso dell'emulazione di rete
casi, e l'emulazione è descritta di seguito. Il capitolo seguente è dedicato a
Modelli relativi a Internet, inclusa l'API socket utilizzata dalle applicazioni Internet. Il
il capitolo successivo tratta le applicazioni e il capitolo successivo descrive il supporto aggiuntivo
per la simulazione, come animatori e statistiche.

Il progetto mantiene un manuale separato dedicato al test e alla convalida di NS-3 codice
(Vedi NS-3 Testing e Convalida Manuale).

Random Variabili
NS-3 contiene un generatore di numeri pseudo-casuali (PRNG) integrato. È importante per
utenti seri del simulatore per comprenderne la funzionalità, la configurazione e l'utilizzo
di questo PRNG, e per decidere se è sufficiente per il suo uso di ricerca.

Presto Panoramica
NS-3 i numeri casuali sono forniti tramite istanze di ns3::RandomVariableStream.

· per impostazione predefinita, NS-3 le simulazioni utilizzano un seme fisso; se c'è qualche casualità nel
simulazione, ogni esecuzione del programma produrrà risultati identici a meno che il seme e/o
il numero di manche è cambiato.

· In NS-3.3 e prima, NS-3 le simulazioni hanno utilizzato un seme casuale per impostazione predefinita; questo segna un
cambiamento di politica a partire da NS-3.4.

· In NS-3.14 e prima, NS-3 le simulazioni usavano una classe wrapper diversa chiamata
ns3::Variabile casuale. Come di NS-3.15, questa classe è stata sostituita da
ns3::RandomVariableStream; il generatore di numeri pseudo-casuali sottostante non ha
cambiato.

· per ottenere casualità su più esecuzioni di simulazione, è necessario impostare il seme
in modo diverso o impostare il numero di manche in modo diverso. Per impostare un seme, chiama
ns3::RngSeedManager::SetSeed() all'inizio del programma; per impostare un numero di manche con
lo stesso seme, chiama ns3::RngSeedManager::SetRun() all'inizio del programma; vedere
Creazione con variabili.

· ogni RandomVariableStream utilizzato in NS-3 ha un generatore di numeri casuali virtuale associato
con esso; tutte le variabili casuali utilizzano un seme fisso o casuale basato sull'uso del
seme globale (punto elenco precedente);

· se si intende eseguire più run dello stesso scenario, con random differenti
numeri, assicurati di leggere la sezione su come eseguire repliche indipendenti:
Creazione con variabili.

Leggi oltre per ulteriori spiegazioni sulla struttura del numero casuale per NS-3.

sfondo
Le simulazioni utilizzano molti numeri casuali; uno studio ha rilevato che la maggior parte delle simulazioni di rete
spendi fino al 50% della CPU generando numeri casuali. Gli utenti della simulazione devono essere
interessato alla qualità dei numeri (pseudo) casuali e all'indipendenza tra
diversi flussi di numeri casuali.

Gli utenti devono preoccuparsi di alcuni problemi, come ad esempio:

· il seeding del generatore di numeri casuali e se un risultato della simulazione è
deterministico o no,

· come acquisire diversi flussi di numeri casuali indipendenti da uno
un altro, e

· quanto tempo impiegano i flussi per il ciclo

Introdurremo qui alcuni termini: un RNG fornisce una lunga sequenza di (pseudo) casuali
numeri. La lunghezza di questa sequenza è chiamata ciclo lunghezza or periodo, dopo il quale
l'RNG si ripeterà. Questa sequenza può essere partizionata in disgiunti flussi. UN
flusso di un RNG è un sottoinsieme contiguo o blocco della sequenza RNG. Ad esempio, se il
Il periodo RNG è di lunghezza N e da questo RNG vengono forniti due flussi, quindi il primo
stream potrebbe utilizzare i primi N/2 valori e il secondo stream potrebbe produrre il secondo N/2
valori. Una proprietà importante qui è che i due flussi non sono correlati. Allo stesso modo,
ogni flusso può essere partizionato in modo disgiunto in un numero di non correlati flussi secondari.
si spera che l'RNG sottostante produca una sequenza pseudo-casuale di numeri con una lunghezza molto lunga
lunghezza del ciclo e la suddivide in flussi e flussi secondari in modo efficiente.

NS-3 usa lo stesso generatore di numeri casuali sottostante come fa NS-2: il MRG32k3a
generatore di Pierre L'Ecuyer. Una descrizione dettagliata può essere trovata in
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. Il generatore MRG32k3a
fornisce 1.8x10^{19} flussi indipendenti di numeri casuali, ognuno dei quali consiste di
2.3x10^{15} flussi secondari. Ogni substream ha un punto (vale a dire, il numero di numeri casuali
prima della sovrapposizione) di 7.6x10^{22}. Il periodo dell'intero generatore è 3.1x10^{57}.

Classe ns3::RandomVariableStream è l'interfaccia pubblica a questo numero casuale sottostante
Generatore. Quando gli utenti creano nuove variabili casuali (come ns3::UniformRandomVariable,
ns3::Variabile casuale esponenziale, ecc.), creano un oggetto che utilizza uno dei
flussi distinti e indipendenti del generatore di numeri casuali. Pertanto, ogni oggetto di
Digitare ns3::RandomVariableStream ha, concettualmente, un proprio RNG "virtuale". Per di più,
ogni ns3::RandomVariableStream può essere configurato per utilizzare uno dei set di flussi secondari disegnati
dal flusso principale.

Un'implementazione alternativa sarebbe quella di consentire a ciascuna variabile casuale di avere la propria
(diversamente seminato) RNG. Tuttavia, non possiamo garantire così fortemente che il diverso
le sequenze sarebbero incorrelate in tal caso; quindi, preferiamo usare un singolo RNG e
flussi e substream da esso.

Creazione con variabili
NS-3 supporta un numero di oggetti variabili casuali dalla classe base
Flusso variabile casuale. Questi oggetti derivano da ns3::Oggetto e sono gestiti da smart
puntatori.

Il modo corretto per creare questi oggetti è usare il templated CreaOggetto<> Metodo,
per esempio:

Ptr x = CreaOggetto ();

quindi puoi accedere ai valori chiamando metodi sull'oggetto come:

myRandomNo = x->GetInteger ();

Se invece provi a fare qualcosa del genere:

myRandomNo = UniformRandomVariable().GetInteger ();

il tuo programma incontrerà un errore di segmentazione, perché l'implementazione si basa su
una costruzione di attributi che si verifica solo quando CreateObject è chiamato.

Gran parte del resto di questo capitolo discute ora le proprietà del flusso di
numeri pseudo-casuali generati da tali oggetti e come controllare la semina di tali
oggetti.

semina e studente indipendente repliche
NS-3 le simulazioni possono essere configurate per produrre risultati deterministici o casuali. Se la
NS-3 la simulazione è configurata per utilizzare un seme fisso e deterministico con lo stesso numero di esecuzione,
dovrebbe dare lo stesso output ogni volta che viene eseguito.

Per impostazione predefinita, NS-3 le simulazioni utilizzano un seme fisso e un numero di esecuzione. Questi valori sono memorizzati in
Tutto ns3::Valore Globale istanze: g_rngSeed e g_rngEsegui.

Un tipico caso d'uso consiste nell'eseguire una simulazione come una sequenza di prove indipendenti, in modo da
calcolare statistiche su un gran numero di esecuzioni indipendenti. L'utente può modificare il
seed globale e rieseguire la simulazione, o può far avanzare lo stato substream dell'RNG, che
è indicato come incremento del numero di manche.

Una classe ns3::RngSeedManager fornisce un'API per controllare il seeding e il numero di esecuzione
comportamento. Questa impostazione dello stato di seeding e substream deve essere chiamata prima di qualsiasi random
le variabili vengono create; per esempio:

RngSeedManager::SetSeed (3); // Cambia il seme dal valore predefinito di 1 a 3
RngSeedManager::SetRun (7); // Modifica il numero di esecuzione dal valore predefinito 1 a 7
// Ora, crea variabili casuali
Ptr x = CreaOggetto ();
Ptr y = CreaOggetto ();
...

Che cosa è meglio, impostare un nuovo seme o far avanzare lo stato substream? Non c'è
garantire che i flussi prodotti da due semi casuali non si sovrappongano. L'unico modo per
garantire che due flussi non si sovrappongano è utilizzare la capacità del flusso secondario fornita da
l'implementazione del RNG. Perciò, uso , il flusso secondario capacità a produrre multiplo
studente indipendente corre of , il stesso simulazione. In altre parole, quanto più statisticamente rigoroso
il modo per configurare più repliche indipendenti è utilizzare un seme fisso e avanzare
il numero della corsa. Questa implementazione consente un massimo di 2.3x10^{15} indipendenti
repliche utilizzando i substream.

Per facilità d'uso, non è necessario controllare il seme e il numero di corse dall'interno del
programma; l'utente può impostare il NS_GLOBAL_VALUE variabile d'ambiente come segue:

$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run nome-programma

Un altro modo per controllarlo è passare un argomento della riga di comando; poiché questo è un NS-3
Istanza GlobalValue, viene eseguita in modo equivalente come segue:

$ ./waf --command-template="%s --RngRun=3" --run nome-programma

oppure, se stai eseguendo programmi direttamente al di fuori di waf:

$ ./build/ottimizzato/scratch/nome-programma --RngRun=3

Le varianti della riga di comando di cui sopra semplificano l'esecuzione di molte esecuzioni diverse da una shell
script semplicemente passando un indice RngRun diverso.

Classe Flusso variabile casuale
Tutte le variabili casuali dovrebbero derivare dalla classe Variabile casuale. Questa classe base fornisce a
pochi metodi per configurare globalmente il comportamento del generatore di numeri casuali. derivato
le classi forniscono API per disegnare variazioni casuali dalla particolare distribuzione che è
supportato.

A ogni RandomVariableStream creato nella simulazione viene assegnato un generatore che è un nuovo
RNGStream dal PRNG sottostante. Usato in questo modo, l'implementazione di L'Ecuyer
consente un massimo di 1.8x10^19 variabili casuali. Ogni variabile casuale in un singolo
la replica può produrre fino a 7.6x10^22 numeri casuali prima di sovrapporsi.

Tavola XY classe la percezione API
Di seguito sono estratti alcuni metodi pubblici di classe Flusso variabile casuale che accedono al
valore successivo nel substream.

/ **
* \breve Restituisce un doppio casuale dalla distribuzione sottostante
* \return Un valore casuale in virgola mobile
*/
doppio GetValue (vuoto) const;

/ **
* \brief Restituisce un numero intero casuale dalla distribuzione sottostante
* \return cast intero di ::GetValue()
*/
uint32_t GetInteger (vuoto) const;

Abbiamo già descritto la configurazione di seeding sopra. Variabile casuale diversa
le sottoclassi possono avere API aggiuntive.

Tipi of Variabili casuali
I seguenti tipi di variabili casuali sono forniti e sono documentati nel NS-3
Doxygen o leggendo src/core/modello/flusso-variabile-casuale.h. Gli utenti possono anche creare
le proprie variabili casuali personalizzate derivanti dalla classe Flusso variabile casuale.

· classe Variabile casuale uniforme

· classe CostanteVariabileCasuale

· classe Sequenziale Variabile Casuale

· classe EsponenzialeVariabileCasuale

· classe Variabile ParetoCasuale

· classe Variabile casuale di Weibull

· classe Normale Variabile Casuale

· classe LogNormaleVariabileCasuale

· classe Variabile GammaRandom

· classe ErlangVariabile casuale

· classe Variabile TriangolareCasuale

· classe ZipfVariabile casuale

· classe ZetaVariabile casuale

· classe Variabile casuale deterministica

· classe Variabile casuale empirica

Semantica of Flusso variabile casuale oggetti
Gli oggetti RandomVariableStream derivano da ns3::Oggetto e sono gestiti da puntatori intelligenti.

Le istanze RandomVariableStream possono essere utilizzate anche in NS-3 attributi, il che significa che
i valori possono essere impostati per loro tramite il NS-3 sistema di attributi. Un esempio è in
modelli di propagazione per WifiNetDevice:

ID tipo
RandomPropagationDelayModel::GetTypeId (vuoto)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent ()
.Aggiungi costruttore ()
.AddAttribute ("Variabile",
"La variabile casuale che genera ritardi casuali (s).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker ())
;
ritorno a mare;
}

Qui, il NS-3 l'utente può modificare la variabile casuale predefinita per questo modello di ritardo (che è
un UniformRandomVariable compreso tra 0 e 1) attraverso il sistema di attributi.

utilizzando Altro PRNG
Al momento non esiste alcun supporto per la sostituzione di un numero casuale sottostante diverso
generatore (ad esempio, la GNU Scientific Library o il pacchetto Akaroa). Le patch sono benvenute.

Configurazione , il ruscello numero
Il generatore MRG32k3a sottostante fornisce 2^64 flussi indipendenti. In ns-3, questi sono
assegnato in sequenza a partire dal primo flusso come nuove istanze RandomVariableStream
fanno la loro prima chiamata a GetValue().

Come risultato di come questi oggetti RandomVariableStream sono assegnati ai flussi sottostanti,
l'assegnazione è sensibile alle perturbazioni della configurazione della simulazione. Il
conseguenza è che se un qualsiasi aspetto della configurazione della simulazione viene modificato, la mappatura
di RandomVariables ai flussi può (o non può) cambiare.

Per fare un esempio concreto, un utente che esegue uno studio comparativo tra protocolli di routing può
scoprire che l'atto di cambiare un protocollo di routing per un altro noterà che il
anche il modello di mobilità sottostante è cambiato.

A partire da ns-3.15, è stato fornito un controllo agli utenti per consentire agli utenti di
facoltativamente correggi l'assegnazione degli oggetti RandomVariableStream selezionati al sottostante
flussi. Questo è il Stream attributo, parte della classe base RandomVariableStream.

Partizionando la sequenza esistente di flussi precedenti:

<-------------------------------------------------- ----------->
flusso 0 flusso (2^64 - 1)

in due serie uguali:

<-------------------------------------------------- ----------->
^^^ ^
| || |
flusso 0 flusso (2^63 - 1) flusso 2^63 flusso (2^64 - 1)
<- assegnato automaticamente -----------><- assegnato dall'utente ------------------>

I primi 2^63 stream continuano ad essere assegnati automaticamente, mentre gli ultimi 2^63 lo sono
dati gli indici di flusso che iniziano con zero fino a 2^63-1.

L'assegnazione degli stream a un numero fisso di stream è facoltativa; istanze di
RandomVariableStream a cui non è stato assegnato un valore di flusso verrà assegnato il prossimo
uno dal pool di flussi automatici.

Per fissare un RandomVariableStream a un particolare flusso sottostante, assegnarlo Stream
attribuire a un numero intero non negativo (il valore predefinito di -1 significa che un valore sarà
assegnato automaticamente).

editoriale il tuo sul risultato
Quando pubblichi i risultati della simulazione, una parte fondamentale delle informazioni di configurazione che
dovrebbe sempre indicare come hai usato il generatore di numeri casuali.

· che semi hai usato,

· quale RNG hai usato se non quello predefinito,

· come sono state eseguite le corse indipendenti,

· per grandi simulazioni, come hai verificato di non aver pedalato.

Spetta al ricercatore che pubblica i risultati includere informazioni sufficienti per
consentire ad altri di riprodurre i suoi risultati. Spetta inoltre al ricercatore
convincersi che i numeri casuali utilizzati fossero statisticamente validi e affermare in
la carta perché si presume tale fiducia.

Sommario
Esaminiamo le operazioni da eseguire durante la creazione di una simulazione.

· Decidi se stai correndo con un seme fisso o un seme casuale; un seme fisso è il
predefinito

· Decidere come gestire le repliche indipendenti, se applicabile,

· Convinciti che non stai estraendo valori più casuali della lunghezza del ciclo, se
stai eseguendo una simulazione molto lunga e

· Quando pubblichi, segui le linee guida di cui sopra per documentare l'uso del random
generatore di numeri

Hash funzioni
NS-3 fornisce un'interfaccia generica per funzioni di hash di uso generale. Nel più semplice
utilizzo, la funzione hash restituisce l'hash a 32 o 64 bit di un buffer di dati o di una stringa.
La funzione hash sottostante predefinita è mormorio, scelto perché ha una buona funzione di hash
proprietà e offre una versione a 64 bit. Il venerabile FNV1a hash è disponibile anche.

Esiste un meccanismo semplice per aggiungere (o fornire in fase di esecuzione) hash alternativo
implementazioni di funzioni.

Basic Impiego
Il modo più semplice per ottenere un valore hash di un buffer di dati o di una stringa è semplicemente:

#include "ns3/hash.h"

usando lo spazio dei nomi ns3;

carattere * buffer = ...
dimensione_t dimensione_tampone = ...

uint32_t buffer_hash = Hash32 (buffer, buffer_size);

std::stringa s;
uint32_t string_hash = Hash32 (s);

Le funzioni equivalenti sono definite per i valori hash a 64 bit.

Incrementale hashing
In alcune situazioni è utile calcolare l'hash di più buffer, come se lo avessero
stati uniti insieme. (Ad esempio, potresti volere l'hash di un flusso di pacchetti, ma non
desidera assemblare un singolo buffer con il contenuto combinato di tutti i pacchetti.)

Questo è quasi semplice come il primo esempio:

#include "ns3/hash.h"

usando lo spazio dei nomi ns3;

carattere * buffer;
dimensione_t dimensione_tampone;

Hasher hash; // Usa la funzione hash predefinita

per ( )
{
buffer = get_next_buffer ();
hasher (buffer, buffer_size);
}
uint32_t combinato_hash = hasher.GetHash32 ();

Di default hasher conserva lo stato interno per abilitare l'hashing incrementale. Se lo desidera
riutilizzare hasher oggetto (ad esempio perché è configurato con un hash non predefinito
funzione), ma non vuoi aggiungere all'hash calcolato in precedenza, devi chiaro()
primo:

hasher.clear().GetHash32 (buffer, buffer_size);

Questo reinizializza lo stato interno prima di eseguire l'hashing del buffer.

utilizzando an Alternative Hash Funzione
La funzione hash predefinita è mormorio. FNV1a è anche disponibile. Per specificare l'hash
funzione in modo esplicito, utilizzare questo costruttore:

Hasher hasher = Hasher ( Crea ());

Aggiunta Nuovo Hash Funzione implementazioni
Per aggiungere la funzione hash foo, Segui il hash-mormorio3.h/. Cc modello:

· Creare una dichiarazione di classe (.h) e definizione (. Cc) ereditando da
Hash::Implementazione.

· includere la dichiarazione in hash.h (nel punto in cui hash-mormorio3.h è incluso.

· Nel tuo codice, istanzia a hasher oggetto tramite il costruttore hasher
(Ptr ())

Se la tua funzione hash è una singola funzione, ad es hash, non è nemmeno necessario creare un
nuova classe derivata da HashImplementation:

hasher hash =
Hasher ( Crea (&hashf) );

Per questo da compilare, il tuo hash deve corrispondere a una delle firme del puntatore alla funzione:

typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);

fonti per Hash funzioni
Le fonti per altre implementazioni di funzioni hash includono:

· Peter Kankowski: http://www.strchr.com

· Alash 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

Eventi e Simulatore
NS-3 è un simulatore di rete a eventi discreti. Concettualmente, il simulatore tiene traccia di a
numero di eventi pianificati per l'esecuzione in un momento di simulazione specificato. Il lavoro di
il simulatore deve eseguire gli eventi in ordine temporale sequenziale. Una volta che il completamento di
si verifica un evento, il simulatore si sposterà all'evento successivo (o uscirà se non ce ne sono
più eventi nella coda eventi). Se, ad esempio, un evento programmato per il tempo di simulazione
Viene eseguito "100 secondi" e l'evento successivo non è programmato fino a "200 secondi", il
simulatore passerà immediatamente da 100 secondi a 200 secondi (di tempo di simulazione) a
eseguire il prossimo evento. Questo è ciò che si intende per simulatore di "eventi discreti".

Per fare in modo che tutto ciò accada, il simulatore ha bisogno di alcune cose:

1. un oggetto simulatore che può accedere a una coda di eventi in cui sono memorizzati gli eventi e che può
gestire lo svolgimento degli eventi

2. uno scheduler responsabile dell'inserimento e della rimozione degli eventi dalla coda

3. un modo per rappresentare il tempo di simulazione

4. gli eventi stessi

Questo capitolo del manuale descrive questi oggetti fondamentali (simulatore, schedulatore,
tempo, evento) e come vengono utilizzati.

Evento
A be completato

Simulatore
La classe Simulator è il punto di ingresso pubblico per accedere alle strutture di pianificazione degli eventi. Una volta
sono stati programmati un paio di eventi per avviare la simulazione, l'utente può iniziare a
eseguirli entrando nel ciclo principale del simulatore (call Simulatore::Esegui). Una volta che il ciclo principale
inizia a funzionare, eseguirà in sequenza tutti gli eventi programmati in ordine dal più vecchio al
più recente fino a quando non ci sono più eventi rimasti nella coda degli eventi o
Simulatore::Stop è stato chiamato.

Per pianificare gli eventi per l'esecuzione da parte del ciclo principale del simulatore, la classe Simulator fornisce
la famiglia di funzioni Simulator::Schedule*.

1. Gestione di gestori di eventi con firme diverse

Queste funzioni sono dichiarate e implementate come modelli C++ per gestire automaticamente i
un'ampia varietà di firme di gestori di eventi C++ utilizzate in natura. Ad esempio, per programmare un
evento da eseguire 10 secondi in futuro e invocare un metodo o una funzione C++ con
argomenti specifici, potresti scrivere questo:

gestore void (int arg0, int arg1)
{
std::cout << "gestore chiamato con argomento arg0=" << arg0 << " e
arg1=" << arg1 << std::endl;
}

Simulatore::Programma(secondi(10), &gestore, 10, 5);

Che produrrà:

gestore chiamato con argomento arg0=10 e arg1=5

Naturalmente, questi modelli C++ possono anche gestire metodi membro in modo trasparente su C++
oggetti:

A be completato: membro metodo esempio

Note:

· i metodi ns-3 Schedule riconoscono automaticamente funzioni e metodi solo se essi
accetta meno di 5 argomenti. Se ne hai bisogno per supportare più argomenti, per favore, invia a
riportare un errore.

· I lettori che hanno familiarità con il termine "funtori completamente legati" riconosceranno il
Simulator::Schedule metodi come un modo per costruire automaticamente tali oggetti.

2. Operazioni di programmazione comuni

L'API Simulator è stata progettata per semplificare la pianificazione della maggior parte degli eventi. Esso
fornisce tre varianti per farlo (ordinate dal più comunemente usato al meno comunemente usato):

· Metodi di pianificazione che consentono di pianificare un evento in futuro fornendo il
ritardo tra l'ora di simulazione corrente e la data di scadenza dell'evento target.

· Metodi ScheduleNow che consentono di programmare un evento per la simulazione corrente
time: verranno eseguiti _dopo_ l'esecuzione dell'evento corrente è terminata ma _prima_ del
l'ora della simulazione viene modificata per l'evento successivo.

· Metodi ScheduleDestroy che ti permettono di agganciarti al processo di spegnimento del Simulator
per ripulire le risorse di simulazione: ogni evento 'distruggi' viene eseguito quando l'utente chiama
il metodo Simulator::Distruggi.

3. Mantenimento del contesto di simulazione

Esistono due modi di base per programmare eventi, con e senza contesto. Cosa fa questo
significa?

Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);

vs.

Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);

Lettori che investono tempo e fatica nello sviluppo o nell'utilizzo di un modello di simulazione non banale
conoscerà il valore del framework di registrazione ns-3 per eseguire il debug di simulazioni semplici e complesse
nello stesso modo. Una delle caratteristiche importanti fornite da questo framework di registrazione è il
visualizzazione automatica dell'id del nodo di rete associato all'evento 'attualmente' in esecuzione.

L'id del nodo di rete attualmente in esecuzione è infatti tracciato dal Simulatore
classe. È possibile accedervi con il metodo Simulator::GetContext che restituisce il
'contesto' (un intero a 32 bit) associato e memorizzato nell'evento attualmente in esecuzione. In
alcuni rari casi, quando un evento non è associato a un nodo di rete specifico, la sua
'contesto' è impostato su 0xffffffff.

Per associare automaticamente un contesto a ciascun evento, i metodi Schedule e ScheduleNow
riutilizzare il contesto dell'evento attualmente in esecuzione come contesto dell'evento programmato
per l'esecuzione in seguito.

In alcuni casi, in particolare quando si simula la trasmissione di un pacchetto da un nodo a
un altro, questo comportamento è indesiderabile poiché il contesto previsto dell'evento di ricezione è
quello del nodo ricevente, non quello mittente. Per evitare questo problema, il Simulatore
fornisce un metodo di pianificazione specifico: ScheduleWithContext che consente di fornire
esplicitamente l'id del nodo ricevente associato all'evento di ricezione.

Xxx: codice esempio

In alcuni casi molto rari, gli sviluppatori potrebbero aver bisogno di modificare o capire come il contesto
(id nodo) del primo evento è impostato su quello del nodo associato. Questo è compiuto
dalla classe NodeList: ogni volta che viene creato un nuovo nodo, la classe NodeList usa
ScheduleWithContext per pianificare un evento di "inizializzazione" per questo nodo. L'evento "inizializza"
quindi viene eseguito con un contesto impostato su quello dell'id del nodo e può utilizzare la normale varietà di
Metodi di pianificazione. Invoca il metodo Node::Initialize che propaga l'"initialize"
evento chiamando il metodo DoInitialize per ogni oggetto associato al nodo. Il
Metodo DoInitialize sovrascritto in alcuni di questi oggetti (in particolare nell'applicazione
classe base) pianificherà alcuni eventi (in particolare Application::StartApplication) che
a sua volta pianificherà gli eventi di generazione del traffico che a loro volta pianificheranno
eventi a livello di rete.

Note:

· Gli utenti devono fare attenzione a propagare i metodi DoInitialize tra gli oggetti chiamando
Inizializzare esplicitamente sui loro oggetti membri

· L'ID contesto associato a ciascun metodo ScheduleWithContext ha altri usi oltre
logging: è utilizzato da un ramo sperimentale di ns-3 per eseguire simulazioni parallele su
sistemi multicore che utilizzano il multithreading.

Le funzioni Simulator::* non sanno quale sia il contesto: si assicurano semplicemente che
qualunque contesto tu specifichi con ScheduleWithContext è disponibile quando il corrispondente
l'evento viene eseguito con ::GetContext.

Spetta ai modelli implementati su Simulator::* interpretare il valore del contesto.
In ns-3, i modelli di rete interpretano il contesto come il node id del nodo che
generato un evento. Ecco perché è importante chiamare ScheduleWithContext in
ns3::Sottoclassi di canale perché stiamo generando un evento dal nodo i al nodo j e we
vogliono assicurarsi che l'evento che verrà eseguito sul nodo j abbia il giusto contesto.

Ora
A be completato

Scheduler
A be completato

callback
Alcuni nuovi utenti per NS-3 non hanno familiarità con un linguaggio di programmazione ampiamente utilizzato utilizzato
in tutto il codice: the NS-3 richiama. Questo capitolo fornisce alcune motivazioni sul
callback, indicazioni su come utilizzarlo e dettagli sulla sua implementazione.

callback Motivazione
Considera che hai due modelli di simulazione A e B e desideri che passino
informazioni tra di loro durante la simulazione. Un modo in cui puoi farlo è che tu
possono rendere A e B esplicitamente consapevoli dell'altro, in modo che possano invocare
metodi l'uno sull'altro:

classe A {
pubblico:
void ReceiveInput ( // parametri );
...
}

(in un altro file sorgente :)

classe B {
pubblico:
void Fai Qualcosa (void);
...

privato:
A* a_istanza; // puntatore a un A
}

nulla
B::Fai qualcosa()
{
// Dì a un_istanza che è successo qualcosa
a_instance->ReceiveInput ( // parametri);
...
}

Questo funziona certamente, ma ha lo svantaggio di introdurre una dipendenza da A e B
conoscere l'altro in fase di compilazione (questo rende più difficile avere indipendenti
unità di compilazione nel simulatore) e non è generalizzato; se in uno scenario di utilizzo successivo,
B ha bisogno di parlare con un oggetto C completamente diverso, il codice sorgente di B deve essere
cambiato per aggiungere a c_istanza e così via. È facile vedere che questa è una forza bruta
meccanismo di comunicazione che può portare alla programmazione cruft nei modelli.

Questo non vuol dire che gli oggetti non dovrebbero sapere l'uno dell'altro se c'è un duro
dipendenza tra loro, ma che spesso il modello può essere reso più flessibile se è
le interazioni sono meno vincolate in fase di compilazione.

Questo non è un problema astratto per la ricerca sulla simulazione di rete, ma piuttosto è stato un
fonte di problemi nei simulatori precedenti, quando i ricercatori vogliono estendere o modificare il
sistema per fare cose diverse (come tendono a fare nella ricerca). Si consideri, ad esempio,
un utente che desidera aggiungere un sottolivello del protocollo di sicurezza IPsec tra TCP e IP:

------------ -----------
| TCP | | TCP |
------------ -----------
| diventa -> |
----------- -----------
| PI | | IPSec |
----------- -----------
|
-----------
| PI |
-----------

Se il simulatore ha fatto delle ipotesi e l'ha codificato nel codice, quell'IP parla sempre
a un protocollo di trasporto di cui sopra, l'utente potrebbe essere costretto ad hackerare il sistema per ottenere il
interconnessioni desiderate. Questo non è chiaramente un modo ottimale per progettare un generico
simulatore.

callback sfondo
NOTA:
I lettori che hanno familiarità con i callback di programmazione possono saltare questa sezione del tutorial.

Il meccanismo di base che consente di affrontare il problema di cui sopra è noto come a richiama.
L'obiettivo finale è consentire a un pezzo di codice di chiamare una funzione (o metodo in C++)
senza alcuna dipendenza specifica tra moduli.

Questo in definitiva significa che hai bisogno di una sorta di indiretto: tratti l'indirizzo del
chiamata funzione come variabile. Questa variabile è chiamata variabile puntatore a funzione.
La relazione tra la funzione e il puntatore da puntatore a funzione non è davvero diversa
quella di oggetto e puntatore ad oggetto.

In C l'esempio canonico di puntatore a funzione è a
pointer-to-function-returning-integer (PFI). Per un PFI che accetta un parametro int, questo
potrebbe essere dichiarato come:

int(*pfi)(int arg) = 0;

Quello che ottieni da questo è una variabile chiamata semplicemente PFI che viene inizializzato al valore 0.
Se vuoi inizializzare questo puntatore a qualcosa di significativo, devi avere un
funzione con una firma corrispondente. In questo caso:

int MiaFunzione (int arg) {}

Se hai questo obiettivo, puoi inizializzare la variabile in modo che punti alla tua funzione come:

pfi = MiaFunzione;

Puoi quindi chiamare MyFunction indirettamente utilizzando la forma più suggestiva della chiamata:

int risultato = (*pfi) (1234);

Questo è suggestivo poiché sembra che tu stia dereferenziando solo il puntatore alla funzione
come dereferenziare qualsiasi puntatore. In genere, tuttavia, le persone approfittano del
fatto che il compilatore sa cosa sta succedendo e utilizzerà solo una forma più breve:

int risultato = pfi (1234);

Nota che il puntatore a funzione obbedisce alla semantica del valore, quindi puoi passarlo come qualsiasi altro
altro valore. In genere, quando usi un'interfaccia asincrona passerai alcune entità
in questo modo a una funzione che eseguirà un'azione e chiamata precedente per fartelo sapere
completato. Richiama seguendo l'indiretto ed eseguendo la funzione fornita.

In C++ hai la complessità aggiuntiva degli oggetti. L'analogia con il PFI sopra significa che tu
avere un puntatore a una funzione membro che restituisce un int (PMI) invece del puntatore a
funzione che restituisce un int (PFI).

La dichiarazione della variabile che fornisce l'indiretto sembra solo leggermente diversa:

int (MiaClasse::*pmi) (int arg) = 0;

Questo dichiara una variabile denominata pmi proprio come l'esempio precedente ha dichiarato una variabile denominata
PFI. Poiché sarà chiamare un metodo di un'istanza di una particolare classe, si deve
dichiarare quel metodo in una classe:

classe MyClass {
pubblico:
int MioMetodo (int arg);
};

Data questa dichiarazione di classe, si inizializzerebbe quindi quella variabile in questo modo:

pmi = &MyClass::MyMethod;

Assegna alla variabile l'indirizzo del codice che implementa il metodo, completando
l'indiretto. Per chiamare un metodo, il codice necessita di a questo puntatore. Questo a sua volta,
significa che ci deve essere un oggetto di MyClass a cui fare riferimento. Un esempio semplicistico di questo è solo
chiamando un metodo indirettamente (pensa alla funzione virtuale):

int (MyClass::*pmi) (int arg) = 0; // Dichiara un PMI
pmi = &MyClass::MyMethod; // Punta al codice di implementazione

miaclasse miaclasse; // Serve un'istanza della classe
(myClass.*pmi) (1234); // Chiama il metodo con un oggetto ptr

Proprio come nell'esempio C, puoi usarlo in una chiamata asincrona a un altro modulo
che sarà chiamata precedente utilizzando un metodo e un puntatore a oggetto. L'estensione semplice
si potrebbe considerare di passare un puntatore all'oggetto e alla variabile PMI. Il modulo
farebbe solo:

(*oggettoPtr.*pmi) (1234);

per eseguire il callback sull'oggetto desiderato.

Ci si potrebbe chiedere in questo momento, ciò che è , il punto? Il modulo chiamato dovrà capire
il tipo concreto dell'oggetto chiamante per eseguire correttamente la richiamata. Perchè no
accettalo, passa il puntatore all'oggetto digitato correttamente e fai oggetto->Metodo(1234) in
il codice invece della richiamata? Questo è precisamente il problema sopra descritto. Cos'è
necessario è un modo per disaccoppiare completamente la funzione chiamante dalla classe chiamata. Questo
requisito ha portato allo sviluppo di funtore.

Un funtore è la conseguenza di qualcosa inventato negli anni '1960 chiamato chiusura. è
fondamentalmente solo una chiamata di funzione impacchettata, possibilmente con uno stato.

Un funtore ha due parti, una specifica e una generica, legate tramite ereditarietà.
Il codice chiamante (il codice che esegue il callback) eseguirà un overload generico
operatore () di un funtore generico per richiamare la richiamata. Il codice chiamato (il
codice che vuole essere richiamato) dovrà fornire un'implementazione specializzata di
, il operatore () che esegue il lavoro specifico della classe che ha causato l'accoppiamento stretto
problema sopra.

Con il funtore specifico e il suo sovraccarico operatore () creato, il codice chiamato quindi
fornisce il codice specializzato al modulo che eseguirà la richiamata (la chiamata
codice).

Il codice chiamante prenderà un funtore generico come parametro, quindi viene eseguito un cast implicito
nella chiamata di funzione per convertire il funtore specifico in un funtore generico. Questo significa
che il modulo chiamante deve solo comprendere il tipo di funtore generico. è disaccoppiato
completamente dal codice chiamante.

Le informazioni necessarie per creare un funtore specifico sono il puntatore all'oggetto e il
indirizzo del puntatore al metodo.

L'essenza di ciò che deve accadere è che il sistema dichiari una parte generica del
funtore:

modello
funzione di classe
{
pubblico:
operatore int virtuale() (T arg) = 0;
};

Il chiamante definisce una parte specifica del funtore che è davvero lì solo per implementare
lo specifico operatore() Metodo:

modello
class SpecificFunctor : public Functor
{
pubblico:
FunzioneSpecifica(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}

operatore int virtuale() (arg ARG)
{
(*m_p.*m_pmi)(arg);
}
privato:
int (T::*m_pmi)(ARG argomento);
T*m_p;
};

Ecco un esempio di utilizzo:

classe A
{
pubblico:
A (int a0): a (a0) {}
int Ciao (int b0)
{
std::cout << "Ciao da A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};

int main ()
{
A a(10);
Funzione specifica sf(&a, &A::Ciao);
sf(5);
}

NOTA:
Il codice precedente non è un vero codice ns-3. È un codice di esempio semplicistico usato solo per
illustrare i concetti coinvolti e per aiutarti a comprendere meglio il sistema. Non
aspettati di trovare questo codice ovunque nell'albero ns-3.

Notare che ci sono due variabili definite nella classe sopra. La variabile m_p è la
puntatore all'oggetto e m_pmi è la variabile contenente l'indirizzo della funzione a
eseguire.

Notare che quando operatore() viene chiamato, a sua volta chiama il metodo fornito con
puntatore all'oggetto utilizzando la sintassi PMI C++.

Per usarlo, si potrebbe quindi dichiarare un codice modello che prenda un funtore generico come a
parametro:

void LibraryFunction (funtore funtore);

Il codice che parlerà al modello costruirà un funtore specifico e lo passerà a
LibreriaFunzione:

miaclasse miaclasse;
Funzione specifica functor (&myclass, MyClass::MyMethod);

Quando LibreriaFunzione è fatto, esegue la richiamata utilizzando il tasto operatore() sul generico
functor è stato passato e, in questo caso particolare, fornisce l'argomento intero:

nulla
LibraryFunction (funtore funtore)
{
// Esegue la funzione di libreria
funtore(1234);
}

Notare che LibreriaFunzione è completamente disaccoppiato dalla specifica tipologia di cliente.
La connessione avviene tramite il polimorfismo del funtore.

L'API di richiamata in NS-3 implementa callback orientate agli oggetti utilizzando il meccanismo functor.
Questa API di callback, essendo basata su modelli C++, è indipendente dai tipi; cioè, esegue statico
controlli di tipo per imporre la corretta compatibilità delle firme tra chiamanti e chiamati. è
quindi più sicuro da usare rispetto ai puntatori a funzione tradizionali, ma la sintassi potrebbe
sembrare imponente all'inizio. Questa sezione è progettata per guidarti attraverso il sistema di Callback
in modo che tu possa sentirti a tuo agio nell'usarlo in NS-3.

utilizzando , il Richiamata API
L'API Callback è abbastanza minimale, fornendo solo due servizi:

1. dichiarazione del tipo di callback: un modo per dichiarare un tipo di callback con una data firma,
e,

2. istanza di callback: un modo per istanziare un callback di inoltro generato da un modello
che può inoltrare qualsiasi chiamata a un altro metodo membro della classe C++ o a una funzione C++.

Questo si osserva meglio camminando attraverso un esempio, basato su campioni/main-callback.cc.

utilizzando , il Richiamata API con statico funzioni
Considera una funzione:

doppio statico
CbOne (doppia a, doppia b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
restituire a;
}

Considera anche il seguente frammento di programma principale:

int main (int argc, char * argv [])
{
// tipo restituito: doppio
// primo tipo di argomento: double
// secondo tipo di argomento: double
Richiama uno;
}

Questo è un esempio di callback in stile C, che non include o non necessita di a questo
puntatore. Il modello di funzione Richiamata è essenzialmente la dichiarazione della variabile
contenente il puntatore alla funzione. Nell'esempio sopra, abbiamo mostrato esplicitamente un puntatore
a una funzione che ha restituito un intero e ha preso un singolo intero come parametro, The
Richiamata la funzione template è una versione generica di quella -- è usata per dichiarare il tipo
di una richiamata.

NOTA:
I lettori che non hanno familiarità con i modelli C++ possono consultare
http://www.cplusplus.com/doc/tutorial/templates/.

Il Richiamata template richiede un argomento obbligatorio (il tipo restituito dalla funzione a
essere assegnato a questo callback) e fino a cinque argomenti opzionali, ciascuno dei quali specifica il
tipo degli argomenti (se la tua particolare funzione di callback ha più di cinque argomenti,
quindi questo può essere gestito estendendo l'implementazione di callback).

Quindi nell'esempio sopra, abbiamo una callback dichiarata denominata "one" che alla fine sarà
tenere un puntatore di funzione. La firma della funzione che manterrà deve restituire
double e deve supportare due doppi argomenti. Se si cerca di passare una funzione il cui
la firma non corrisponde al callback dichiarato, si verificherà un errore di compilazione. Inoltre, se
si prova ad assegnare a una richiamata una incompatibile, la compilazione andrà a buon fine ma a
verrà generato NS_FATAL_ERROR runtime. Il programma di esempio
src/core/examples/main-callback.cc dimostra entrambi questi casi di errore alla fine di
, il principale() .

Ora, dobbiamo unire questa istanza di callback e l'effettiva funzione di destinazione
(CbOne). Notare sopra che CbOne ha gli stessi tipi di firma della funzione del callback:
questo è importante. Possiamo passare a questa callback qualsiasi funzione digitata correttamente.
Diamo un'occhiata più da vicino:

statico double CbOne (doppio a, doppio b) {}
^ ^ ^
| | |
| | |
Richiama uno;

Puoi associare una funzione a un callback solo se hanno la firma corrispondente. Il primo
l'argomento del modello è il tipo restituito e gli argomenti del modello aggiuntivi sono i tipi
degli argomenti della firma della funzione.

Ora, associamo il nostro callback "one" alla funzione che corrisponde alla sua firma:

// crea un'istanza di callback che punta alla funzione cbOne
uno = MakeCallback (&CbOne);

Questa chiamata a Effettua una richiamata è, in sostanza, creare uno dei funtori specializzati
sopra menzionato. La variabile dichiarata usando il Richiamata funzione modello sta per
recitare la parte del funtore generico. L'incarico prima = Effettua una richiamata (&CbUno) is
il cast che converte il funtore specializzato noto al chiamato in un funtore generico
noto al chiamante.

Quindi, più avanti nel programma, se è necessaria la richiamata, può essere utilizzata come segue:

NS_ASSERT (!one.IsNull ());

// invoca la funzione cbOne tramite l'istanza di callback
doppia riconversione;
retOne = uno (10.0, 20.0);

L'assegno per IsNull () assicura che il callback non sia nullo -- che ci sia una funzione
chiamare dietro questa richiamata. Quindi, uno() esegue il generico operatore() che è davvero
sovraccaricato di una specifica implementazione di operatore() e restituisce lo stesso risultato di se
CbUno() era stato chiamato direttamente.

utilizzando , il Richiamata API con membro funzioni
In genere, non chiamerai funzioni statiche ma invece funzioni membro pubbliche di
un oggetto. In questo caso, è necessario un argomento aggiuntivo per la funzione MakeCallback, per
dire al sistema su quale oggetto deve essere invocata la funzione. Considera questo esempio,
anche da main-callback.cc:

classe MyCb {
pubblico:
int CbDue (doppia a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
return -5;
}
};

intero principale ()
{
...
// tipo restituito: int
// primo tipo di argomento: double
Richiama Due;
MyCb cb;
// crea un'istanza di callback che punta a MyCb::cbTwo
due = MakeCallback (&MyCb::CbTwo, &cb);
...
}

Qui, passiamo un ulteriore puntatore all'oggetto al Richiama<> funzione. Richiama da
la sezione di sfondo sopra quella Operatore() utilizzerà il puntatore alla sintassi del membro quando
esegue su un oggetto:

operatore int virtuale() (arg ARG)
{
(*m_p.*m_pmi)(arg);
}

E quindi abbiamo dovuto fornire le due variabili (m_p e m_pm) quando abbiamo fatto lo specifico
funtore. La linea:

due = MakeCallback (&MyCb::CbTwo, &cb);

fa proprio questo. In questo caso, quando Tutto () viene invocato:

int risultato = due (1.0);

risulterà in una chiamata al CbDue funzione membro (metodo) sull'oggetto puntato da
&cb.

Costruzione Nullo callback
È possibile che i callback siano nulli; quindi potrebbe essere saggio controllare prima di usarli.
Esiste un costrutto speciale per un callback null, che è preferibile al semplice passaggio
"0" come argomento; è il MakeNullCallback<> costruire:

due = MakeNullCallback ();
NS_ASSERT (due.IsNull ());

Invocare un callback null è come invocare un puntatore a funzione null: si arresterà in modo anomalo a
tempo di esecuzione.

Bound callback
Un'estensione molto utile del concetto di funtore è quella di Bound Callback. In precedenza
è stato detto che le chiusure erano originariamente chiamate di funzione impacchettate per dopo
esecuzione. Nota che in tutte le descrizioni di Callback sopra, non c'è modo di
impacchettare tutti i parametri per usarli in seguito -- quando il Richiamata si chiama via operatore().
Tutti i parametri sono forniti dalla funzione chiamante.

E se si desidera consentire alla funzione client (quella che fornisce il callback) di
fornire alcuni parametri? Alexandrescu chiama il processo di consentire a un cliente di
specificare uno dei parametri "legame". Uno dei parametri di operatore() è stata
vincolato (fissato) dal cliente.

Alcuni dei nostri codici di tracciamento di pcap ne forniscono un bell'esempio. C'è una funzione che
deve essere chiamato ogni volta che viene ricevuto un pacchetto. Questa funzione chiama un oggetto che
scrive effettivamente il pacchetto su disco nel formato di file pcap. La firma di uno di questi
le funzioni saranno:

static void DefaultSink (Ptr file, Ptr P);

La parola chiave static significa che questa è una funzione statica che non necessita di a questo puntatore, quindi
utilizzerà callback in stile C. Non vogliamo che il codice chiamante debba essere a conoscenza
tutt'altro che il pacchetto. Quello che vogliamo nel codice chiamante è solo una chiamata che assomiglia a:

m_promiscSnifferTrace (m_currentPkt);

Quello che vogliamo fare è legare , il Ptr filetto alla richiamata specifica
attuazione quando viene creato e predisporre il operatore() del Richiamo a
fornire quel parametro gratuitamente.

Forniamo il MakeBoundCallback funzione modello a tale scopo. Ci vuole lo stesso
parametri come Effettua una richiamata funzione modello ma accetta anche i parametri per essere
limite. Nel caso dell'esempio sopra:

MakeBoundCallback (&DefaultSink, file);

creerà un'implementazione di callback specifica che sa di aggiungere il limite extra
argomenti. Concettualmente estende il funtore specifico sopra descritto con uno o più
argomenti legati:

modello
class SpecificFunctor: Funtore pubblico
{
pubblico:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg = limitArg;
}

operatore int virtuale() (arg ARG)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
privato:
void (T::*m_pmi)(ARG argomento);
T*m_p;
BOUND_ARG m_boundArg;
};

Puoi vedere che quando viene creato il funtore specifico, l'argomento associato viene salvato nel file
funtore / oggetto di callback stesso. Quando il operatore() viene invocato con il singolo
parametro, come in:

m_promiscSnifferTrace (m_currentPkt);

l'attuazione di operatore() aggiunge il parametro associato alla chiamata di funzione effettiva:

(*m_p.*m_pmi)(m_boundArg, arg);

È anche possibile associare due o tre argomenti. Diciamo che abbiamo una funzione con
firma:

static void NotifyEvent (Ptr a, Ptr b, MyEventType e);

Si può creare un binding di callback associato ai primi due argomenti come:

MakeBoundCallback (&NotifyEvent, a1, b1);

supponendo a1 e b1 sono oggetti di tipo A e B rispettivamente. Allo stesso modo per tre
argomenti si avrebbe funzione con una firma:

static void NotifyEvent (Ptr a, Ptr b, MyEventType e);

Binding tre argomenti in done con:

MakeBoundCallback (&NotifyEvent, a1, b1, c1);

di nuovo supponendo a1, b1 e c1 sono oggetti di tipo A, B e C rispettivamente.

Questo tipo di legame può essere utilizzato per lo scambio di informazioni tra oggetti in simulazione;
in particolare, i callback legati possono essere usati come callback tracciati, che saranno descritti in
la sezione successiva.

Tracciato callback
segnaposto sottosezione

Richiamata posizioni in NS-3
Dove vengono spesso utilizzati i callback? NS-3? Ecco alcuni dei più visibili a
utenti tipici:

· API socket

· API Layer-2/Layer-3

· Sottosistema di tracciabilità

· API tra IP e sottosistemi di routing

Implementazione/Attuazione dettagli
I frammenti di codice sopra sono semplicistici e progettati solo per illustrare il meccanismo
si. Il codice di richiamata effettivo è piuttosto complicato e molto intenso per i modelli e a
non è richiesta una profonda comprensione del codice. Se interessati, gli utenti esperti possono trovare il
seguito utile.

Il codice è stato originariamente scritto sulla base delle tecniche descritte in
http://www.codeproject.com/cpp/TTLFunction.asp. Successivamente è stato riscritto per seguire
l'architettura delineata in Moderno C++ Design Generico Programmazione e Progettazione Modelli
Applicato, Alexandrescu, capitolo 5, generalizzato Funtori.

Questo codice utilizza:

· parametri del modello predefinito per evitare che gli utenti debbano specificare parametri vuoti quando
il numero di parametri è inferiore al numero massimo supportato

· l'idioma pimpl: la classe Callback viene passata per valore e delega il punto cruciale di
il lavoro al suo puntatore pimpl.

· possono essere utilizzate due implementazioni di pimpl che derivano da CallbackImpl FunctorCallbackImpl
con qualsiasi tipo di funtore mentre MemPtrCallbackImpl può essere utilizzato con puntatori a membro
funzioni.

· un'implementazione dell'elenco di riferimento per implementare la semantica del valore di Callback.

Questo codice si discosta in particolare dall'implementazione Alexandrescu in quanto non lo fa
utilizzare elenchi di tipi per specificare e passare in giro i tipi degli argomenti di callback. Certo,
inoltre non usa la semantica di distruzione della copia e si basa su un elenco di riferimento piuttosto che
autoPtr per mantenere il puntatore.

Oggetto modello
NS-3 è fondamentalmente un sistema di oggetti C++. Gli oggetti possono essere dichiarati e istanziati come
normale, secondo le regole del C++. NS-3 aggiunge anche alcune funzionalità agli oggetti C++ tradizionali, come
descritto di seguito, per fornire maggiori funzionalità e caratteristiche. Questo capitolo del manuale è
ha lo scopo di introdurre il lettore alla NS-3 modello a oggetti.

Questa sezione descrive la progettazione della classe C++ per NS-3 oggetti. In breve, diversi design
i modelli in uso includono il classico design orientato agli oggetti (interfacce polimorfiche e
implementazioni), separazione di interfaccia e implementazione, il pubblico non virtuale
modello di progettazione dell'interfaccia, una funzione di aggregazione degli oggetti e conteggio dei riferimenti per
gestione della memoria. Chi ha familiarità con modelli di componenti come COM o Bonobo lo farà
riconoscere gli elementi del design nel NS-3 modello di aggregazione degli oggetti, sebbene NS-3
il design non è strettamente in accordo con nessuno dei due.

Orientato agli oggetti comportamento
Gli oggetti C++, in generale, forniscono capacità comuni orientate agli oggetti (astrazione,
incapsulamento, ereditarietà e polimorfismo) che fanno parte del classico orientato agli oggetti
progettazione. NS-3 gli oggetti fanno uso di queste proprietà; ad esempio:

indirizzo di classe
{
pubblico:
Indirizzo ();
Indirizzo (tipo uint8_t, const uint8_t *buffer, uint8_t len);
Indirizzo (const Indirizzo e indirizzo);
Indirizzo &operatore = (const Indirizzo &indirizzo);
...
privato:
uint8_t tipo_m;
uint8_t m_len;
...
};

Oggetto base classi
Ci sono tre classi base speciali usate in NS-3. Classi che ereditano da queste basi
le classi possono istanziare oggetti con proprietà speciali. Queste classi base sono:

· classe Oggetto

· classe BaseOggetto

· classe SimpleRefCount

Non è necessario che NS-3 gli oggetti ereditano da queste classi, ma quelli che ottengono
proprietà speciali. Classi derivanti da classe Oggetto ottenere le seguenti proprietà.

· il NS-3 tipo e sistema di attributi (vedi attributi)

· un sistema di aggregazione di oggetti

· un sistema di conteggio dei riferimenti smart-pointer (classe Ptr)

Classi che derivano da class BaseOggetto ottenere le prime due proprietà sopra, ma non farlo
ottenere puntatori intelligenti. Classi che derivano da class SimpleRefCount: prendi solo il
sistema di conteggio dei riferimenti smart-pointer.

In pratica, classe Oggetto è la variante delle tre sopra che la NS-3 lo sviluppatore lo farà
più comunemente incontro.

Memorie gestione e classe Ptr
La gestione della memoria in un programma C++ è un processo complesso e spesso viene eseguito in modo errato oppure
incoerentemente. Abbiamo optato per un modello di conteggio dei riferimenti descritto come segue.

Tutti gli oggetti che utilizzano il conteggio dei riferimenti mantengono un conteggio dei riferimenti interno per determinare
quando un oggetto può eliminarsi in sicurezza. Ogni volta che si ottiene un puntatore a un
interfaccia, il conteggio dei riferimenti dell'oggetto viene incrementato chiamando Rif(). È il
obbligo dell'utente del puntatore di esplicitamente Non rif() il puntatore al termine. quando
il conteggio dei riferimenti scende a zero, l'oggetto viene eliminato.

· Quando il codice client ottiene un puntatore dall'oggetto stesso attraverso la creazione dell'oggetto,
o tramite GetObject, non deve incrementare il conteggio dei riferimenti.

· Quando il codice client ottiene un puntatore da un'altra fonte (ad es. copiando un puntatore) deve
chiamata Rif() per incrementare il conteggio dei riferimenti.

· Tutti gli utenti del puntatore all'oggetto devono chiamare Non rif() per rilasciare il riferimento.

L'onere della chiamata Non rif() è in qualche modo alleviato dall'uso del conteggio di riferimento
classe puntatore intelligente descritta di seguito.

Utenti che utilizzano un'API di basso livello che desiderano allocare in modo esplicito oggetti non conteggiati come riferimento
sull'heap, utilizzando l'operatore new, sono responsabili dell'eliminazione di tali oggetti.

Referenze conteggio smart pointer (Ptr)
chiamata Rif() e Non rif() tutto il tempo sarebbe ingombrante, quindi NS-3 fornisce una smart
classe puntatore Ptr simile a Potenzia::ptr_intrusivo. Questa classe puntatore intelligente presuppone che
il tipo sottostante fornisce una coppia di arbitro e Non rif metodi che dovrebbero
incrementa e decrementa il refcount interno dell'istanza dell'oggetto.

Questa implementazione ti consente di manipolare il puntatore intelligente come se fosse un normale
puntatore: puoi confrontarlo con zero, confrontarlo con altri puntatori, assegnare zero a
esso, ecc.

È possibile estrarre il puntatore grezzo da questo puntatore intelligente con il GetPointer()
e Puntatore a sbirciatina() metodi.

Se desideri memorizzare un nuovo oggetto in un puntatore intelligente, ti consigliamo di utilizzare il pulsante
Funzioni del modello CreateObject per creare l'oggetto e memorizzarlo in un puntatore intelligente per
evitare perdite di memoria. Queste funzioni sono davvero piccole funzioni di comodità e il loro obiettivo
è solo per risparmiare un po' di digitazione.

CreateObject e Creare
Gli oggetti in C++ possono essere creati staticamente, dinamicamente o automaticamente. Questo è vero
per NS-3 inoltre, ma alcuni oggetti nel sistema hanno alcuni framework aggiuntivi disponibili.
In particolare, gli oggetti conteggiati di riferimento vengono solitamente allocati utilizzando un modello Crea o
CreateObject, come segue.

Per oggetti derivanti da classe Oggetto:

Ptr dispositivo = CreaOggetto ();

Si prega di non creare tali oggetti utilizzando operatore nuovi; creali usando CreaOggetto()
anziché.

Per oggetti derivanti da classe SimpleRefCount, o altri oggetti che supportano l'uso del
smart pointer class, è disponibile una funzione di supporto basata su modelli e consigliata per l'uso:

Ptr b = Crea ();

Questo è semplicemente un nuovo operatore wrapper che gestisce correttamente il conteggio dei riferimenti
.

In sintesi, usa Creare se B non è un oggetto ma usa solo il conteggio dei riferimenti (es.
Pacchetto) e utilizzare CreaOggetto se B deriva da ns3::Oggetto.

Aggregazione
Il NS-3 sistema di aggregazione degli oggetti è motivato in parte forte dal riconoscimento che a
caso d'uso comune per NS-2 è stato l'uso dell'ereditarietà e del polimorfismo per estendere
modelli di protocollo. Ad esempio, derivano versioni specializzate di TCP come RenoTcpAgent
from (e sovrascrive le funzioni dalla) classe TcpAgent.

Tuttavia, due problemi che sono sorti nel NS-2 modello sono abbattuti e "base debole
class." Il downcasting si riferisce alla procedura di utilizzo di un puntatore di classe base a un oggetto e
interrogandolo in fase di esecuzione per scoprire le informazioni sul tipo, utilizzate per lanciare esplicitamente il puntatore
a un puntatore di sottoclasse in modo che l'API della sottoclasse possa essere utilizzata. La classe base debole si riferisce al
problemi che sorgono quando una classe non può essere efficacemente riutilizzata (derivata da) perché
manca delle funzionalità necessarie, portando lo sviluppatore a dover modificare la classe base e
causando la proliferazione di chiamate API di classe base, alcune delle quali potrebbero non essere semanticamente
corretto per tutte le sottoclassi.

NS-3 utilizza una versione del modello di progettazione dell'interfaccia di query per evitare questi problemi.
Questo design si basa su elementi del Componente Oggetto Modello e GNOME Bonobo sebbene
la piena compatibilità a livello binario dei componenti sostituibili non è supportata e abbiamo
ha cercato di semplificare la sintassi e l'impatto sugli sviluppatori di modelli.

Esempi
Aggregazione esempio
Nodo è un buon esempio dell'uso dell'aggregazione in NS-3. Nota che non ci sono derivati
classi di nodi in NS-3 come la classe Nodo Internet. Invece, i componenti (protocolli) sono
aggregato a un nodo. Diamo un'occhiata a come vengono aggiunti alcuni protocolli Ipv4 a un nodo.:

vuoto statico
AddIpv4Stack(Ptr nodo)
{
Ptr ipv4 = CreaOggetto ();
ipv4->SetNode (nodo);
nodo->Oggetto aggregato (ipv4);
Ptr ipv4Impl = CreaOggetto ();
ipv4Impl->SetIpv4 (ipv4);
nodo->Oggetto aggregato (ipv4Impl);
}

Nota che i protocolli Ipv4 sono creati usando CreaOggetto(). Quindi, sono aggregati
al nodo. In questo modo, la classe base Node non ha bisogno di essere modificata per consentire agli utenti
con un puntatore Node di classe base per accedere all'interfaccia Ipv4; gli utenti possono chiedere al nodo a
puntatore alla sua interfaccia Ipv4 in fase di esecuzione. Il modo in cui l'utente chiede al nodo è descritto nel
sottosezione successiva.

Si noti che è un errore di programmazione aggregare più di un oggetto dello stesso tipo a
an ns3::Oggetto. Quindi, ad esempio, l'aggregazione non è un'opzione per memorizzare tutti i
socket attivi di un nodo.

OttieniOggetto esempio
GetObject è un modo indipendente dai tipi per ottenere un downcasting sicuro e consentire alle interfacce di essere
trovato su un oggetto.

Considera un puntatore a nodo m_nodo che punta a un oggetto Node che ha un'implementazione di
IPv4 precedentemente aggregato ad esso. Il codice client desidera configurare un percorso predefinito. Per
farlo, deve accedere a un oggetto all'interno del nodo che ha un'interfaccia per l'inoltro IP
configurazione. Esegue quanto segue:

Ptr ipv4 = m_node->GetObject ();

Se il nodo in effetti non ha un oggetto Ipv4 aggregato, allora il metodo lo farà
restituire nullo. Pertanto, è buona norma controllare il valore restituito da tale funzione
chiamata. In caso di successo, l'utente può ora utilizzare il Ptr per l'oggetto Ipv4 che era in precedenza
aggregato al nodo.

Un altro esempio di come si potrebbe utilizzare l'aggregazione consiste nell'aggiungere modelli facoltativi agli oggetti. Per
esempio, un oggetto Nodo esistente può avere un oggetto "Modello energetico" aggregato ad esso in
runtime (senza modificare e ricompilare la classe del nodo). Un modello esistente (come a
dispositivo di rete wireless) può in seguito "GetObject" per il modello energetico e agire in modo appropriato
se l'interfaccia è stata incorporata nell'oggetto Node sottostante o aggregata a
esso in fase di esecuzione. Tuttavia, altri nodi non devono sapere nulla sui modelli energetici.

Speriamo che questa modalità di programmazione richieda molto meno la modifica da parte degli sviluppatori
le classi base.

Oggetto fabbriche
Un caso d'uso comune consiste nel creare molti oggetti configurati in modo simile. Si può ripetutamente
chiamata CreaOggetto() ma c'è anche un modello di progettazione di fabbrica in uso nel NS-3 .
È molto utilizzato nell'API "helper".

Classe ObjectFactory può essere usato per istanziare oggetti e per configurare gli attributi su
quegli oggetti:

void SetTypeId (TidId tid);
void Set (std::string name, const AttributeValue &value);
Ptr Crea (vuoto) const;

Il primo metodo consente di utilizzare il NS-3 Sistema TypeId per specificare il tipo di oggetti
creato. Il secondo permette di impostare attributi sugli oggetti da creare, e il
terzo permette di creare gli oggetti stessi.

Per esempio:

Fabbrica ObjectFactory;
// Fai in modo che questa fabbrica crei oggetti di tipo FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Fa in modo che questo oggetto di fabbrica cambi un valore predefinito di un attributo, per
// oggetti creati successivamente
factory.Set ("SystemLoss", DoubleValue (2.0));
// Crea uno di questi oggetti
Ptr oggetto = fabbrica.Crea ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Crea un altro oggetto con un diverso SystemLoss
Ptr oggetto = fabbrica.Crea ();

Abbassamento
Una domanda che è sorta più volte è: "Se ho un puntatore di classe base (Ptr) a an
oggetto e voglio il puntatore della classe derivata, dovrei eseguire il downcast (tramite il cast dinamico C++) a
ottenere il puntatore derivato, o dovrei usare il sistema di aggregazione degli oggetti per OttieniOggetto<> ()
per trovare un Ptr per l'interfaccia all'API della sottoclasse?"

La risposta è che in molte situazioni entrambe le tecniche funzioneranno. NS-3 fornisce un
funzione basata su modelli per rendere la sintassi del casting dinamico degli oggetti molto più utente
amichevole:

modello
Ptr
DynamicCast (Parte const&p)
{
ritorno Ptr (dynamic_cast (PeekPointer (p)));
}

DynamicCast funziona quando il programmatore ha un puntatore di tipo base e sta testando contro a
puntatore di sottoclasse. GetObject funziona quando si cercano diversi oggetti aggregati, ma anche
funziona con le sottoclassi, allo stesso modo di DynamicCast. In caso di dubbio, il programmatore dovrebbe
usa GetObject, poiché funziona in tutti i casi. Se il programmatore conosce la gerarchia di classi di
l'oggetto in esame, è più diretto utilizzare solo DynamicCast.

Configurazione e Attributi
In NS-3 simulazioni, ci sono due aspetti principali della configurazione:

· La topologia della simulazione e il modo in cui gli oggetti sono collegati.

· I valori utilizzati dai modelli istanziati nella topologia.

Questo capitolo si concentra sul secondo elemento di cui sopra: quanti valori in uso in NS-3 sono
organizzato, documentato e modificabile da NS-3 utenti. Il NS-3 il sistema di attributi è anche il
alla base del modo in cui le tracce e le statistiche vengono raccolte nel simulatore.

Nel corso di questo capitolo discuteremo i vari modi per impostare o modificare i valori
utilizzato da NS-3 oggetti modello. In ordine crescente di specificità, questi sono:

? ?
│Metodo │ Ambito │
? ?
│Valori degli attributi predefiniti impostati │ Influenzano tutte le istanze di │
│quando gli attributi sono definiti nella classe │. ?
Ottieni IDTipo (). │ │
? ?

Riga di comando │ Influenza tutte le istanze future. ?
Configurazione::SetDefault() │ │
Archivio di configurazione │ │
? ?
ObjectFactory │ Riguarda tutte le istanze create │
│ con la fabbrica. ?
? ?
XHelperSetAttribute () │ Riguarda tutte le istanze create da │
│ │ l'aiutante. ?
? ?
MiaClasse::SetX () │ Modifica questa particolare istanza. ?
Oggetto::SetAttribute () │ Generalmente questa è l'unica forma │
Configurazione::Imposta() │ che può essere programmato per modificare │
│ │ un'istanza una volta che la simulazione
│ │ è in esecuzione. ?
? ?

Per "specificità" intendiamo che i metodi nelle righe successive nella tabella sovrascrivono i valori impostati
e in genere interessano meno istanze rispetto ai metodi precedenti.

Prima di approfondire i dettagli del sistema di valori degli attributi, sarà utile rivederne alcuni
proprietà di base della classe Oggetto.

Oggetto Panoramica
NS-3 è fondamentalmente un sistema basato su oggetti C++. Con questo intendiamo che le nuove classi C++
(tipi) possono essere dichiarati, definiti e sottoclasse come al solito.

Molti NS-3 gli oggetti ereditano dal Oggetto classe base. Questi oggetti hanno alcuni extra
proprietà che sfruttiamo per organizzare il sistema e migliorare la gestione della memoria
dei nostri oggetti:

· Sistema "Metadati" che collega il nome della classe a molte meta-informazioni sul
oggetto, tra cui:

· La classe base della sottoclasse,

· L'insieme dei costruttori accessibili nella sottoclasse,

· L'insieme degli "attributi" della sottoclasse,

· Se ogni attributo può essere impostato o è di sola lettura,

· L'intervallo di valori consentito per ciascun attributo.

· Implementazione del puntatore intelligente per il conteggio dei riferimenti, per la gestione della memoria.

NS-3 gli oggetti che utilizzano il sistema di attributi derivano da entrambi Oggetto or BaseOggetto. Più
NS-3 gli oggetti di cui parleremo derivano da Oggetto, ma alcuni che sono al di fuori della smart
il framework di gestione della memoria del puntatore deriva da BaseOggetto.

Esaminiamo un paio di proprietà di questi oggetti.

SEMPLICE Puntatori
Come introdotto nel NS-3 tutorial, NS-3 gli oggetti sono la memoria gestita da a riferimento
conteggio smart pointer implementazione, classe Ptr.

I puntatori intelligenti sono ampiamente utilizzati nel NS-3 API, per evitare di passare riferimenti a
oggetti allocati nell'heap che possono causare perdite di memoria. Per la maggior parte degli usi di base (sintassi), tratta
un puntatore intelligente come un normale puntatore:

Ptr nd = ...;
nd->CallSomeFunction ();
// eccetera.

Quindi, come si ottiene un puntatore intelligente a un oggetto, come nella prima riga di questo esempio?

CreateObject
Come discusso sopra in Gestione della memoria e classe Ptr, all'API di livello più basso, oggetti
di tipo Oggetto non sono istanziati usando operatore nuovi come al solito ma invece da un modello
funzione chiamata CreateObject ().

Un modo tipico per creare un tale oggetto è il seguente:

Ptr nd = CreaOggetto ();

Puoi pensare a questo come funzionalmente equivalente a:

WifiNetDevice* nd = nuovo WifiNetDevice ();

Oggetti che derivano da Oggetto deve essere allocato sull'heap usando CreateObject (). quelli
derivante da BaseOggetto, come NS-3 funzioni di supporto e intestazioni e trailer dei pacchetti,
possono essere allocati in pila.

In alcuni script, potresti non vedere molto CreateObject () chiamate nel codice; questo è
perché ci sono alcuni oggetti di supporto in effetti che stanno facendo il CreateObject () chiamate
per voi.

ID tipo
NS-3 classi che derivano da class Oggetto può includere una classe di metadati chiamata ID tipo che
registra le meta-informazioni sulla classe, per l'uso nell'aggregazione e nel componente dell'oggetto
sistemi di gestione:

· Una stringa univoca che identifica la classe.

· La classe base della sottoclasse, all'interno del sistema di metadati.

· L'insieme dei costruttori accessibili nella sottoclasse.

· Un elenco di proprietà pubblicamente accessibili ("attributi") della classe.

Oggetto Sommario
Mettendo insieme tutti questi concetti, diamo un'occhiata a un esempio specifico: class Nodo.

Il file di intestazione pubblico nodo.h ha una dichiarazione che include un static Ottieni IDTipo ()
chiamata di funzione:

Nodo di classe: oggetto pubblico
{
pubblico:
TypeId statico GetTypeId (vuoto);
...

Questo è definito nel nodo.cc file come segue:

ID tipo
Nodo::GetTypeId (vuoto)
{
static TypeId tid = TypeId ("ns3::Node")
.SetParent ()
.Aggiungi costruttore ()
.AddAttribute ("Elenco dispositivi",
"L'elenco dei dispositivi associati a questo nodo.",
ValoreVettoreOggetto (),
MakeObjectVectorAccessor (&Node::m_devices),
CreaOggettoVectorChecker ())
.AddAttribute ("Elenco applicazioni",
"L'elenco delle applicazioni associate a questo nodo.",
ValoreVettoreOggetto (),
MakeObjectVectorAccessor (&Node::m_applications),
CreaOggettoVectorChecker ())
.AddAttribute ("Id",
"L'id (intero unico) di questo nodo.",
TypeId::ATTR_GET, // consente solo di ottenerlo.
Valore intero (0),
MakeUintegerAccessor (&Node::m_id),
MakeUinteroChecker ())
;
ritorno a mare;
}

Si consideri il ID tipo di NS-3 Oggetto classe come una forma estesa di tipo runtime
informazioni (RTI). Il linguaggio C++ include un semplice tipo di RTTI per supportare
dynamic_cast e tipoid gli operatori.

Il Imposta Genitore () la chiamata nella definizione di cui sopra viene utilizzata in combinazione con il nostro
meccanismi di aggregazione degli oggetti per consentire l'up-casting e il downcasting sicuri negli alberi di ereditarietà
durante OttieniOggetto (). Consente inoltre alle sottoclassi di ereditare gli attributi del genitore
classe.

Il Aggiungi costruttore () la chiamata viene utilizzata in combinazione con la nostra fabbrica di oggetti astratti
meccanismi per permetterci di costruire oggetti C++ senza costringere un utente a conoscere il
classe concreta dell'oggetto che sta costruendo.

Le tre chiamate a Aggiungi attributo () associa una data stringa a un valore fortemente tipizzato in
la classe. Si noti che è necessario fornire una stringa di aiuto che può essere visualizzata, ad esempio,
via processori a riga di comando. Ogni Attributo è associato a meccanismi di accesso
la variabile membro sottostante nell'oggetto (ad esempio, MakeUntegerAccessor () dice
il generico Attributo codice come ottenere l'ID del nodo sopra). Ci sono anche "Checker"
metodi utilizzati per convalidare i valori rispetto ai limiti di intervallo, come massimo e
valori minimi ammessi.

Quando gli utenti vogliono creare nodi, di solito chiamano una qualche forma di CreateObject (),:

Ptr n = CreaOggetto ();

o più astrattamente, usando una fabbrica di oggetti, puoi creare un Nodo oggetto senza pari
conoscendo il tipo C++ concreto:

Fabbrica ObjectFactory;
const std::string typeId = "ns3::Node'';
fabbrica.SetTypeId (tipoId);
Ptr nodo = fabbrica.Crea ();

Entrambi questi metodi fanno sì che gli attributi completamente inizializzati siano disponibili nel
risultante Oggetto le istanze.

Successivamente discuteremo come gli attributi (valori associati a variabili membro o funzioni di
la classe) sono collegati a quanto sopra ID tipo.

Attributi
L'obiettivo del sistema di attributi è organizzare l'accesso degli oggetti membri interni di a
simulazione. Questo obiettivo si pone perché, tipicamente nella simulazione, gli utenti taglieranno e
incolla/modifica script di simulazione esistenti o utilizzerà costrutti di simulazione di livello superiore,
ma spesso sarà interessato allo studio o al tracciamento di particolari variabili interne. Per
esempio, casi d'uso come:

· "I volere a tracciare , il pacchetti on , il senza fili interfaccia esclusivamente on , il prima di tutto accesso punto."

· "I volere a tracciare , il APPREZZIAMO of , il TCP congestione finestra (ogni tempo it i cambiamenti) on a
particolare TCP presa."

· "I volere a cumulo di rifiuti of contro tutti i valori che sono stati utilizzato in my simulazione."

Allo stesso modo, gli utenti potrebbero volere un accesso granulare alle variabili interne nella simulazione, oppure
potrebbe voler cambiare ampiamente il valore iniziale utilizzato per un particolare parametro in tutto
oggetti successivamente creati. Infine, gli utenti potrebbero voler sapere quali variabili sono impostabili
e recuperabile in una configurazione di simulazione. Questo non è solo per la simulazione diretta
interazione sulla riga di comando; considerare anche una (futura) interfaccia utente grafica che
vorrebbe essere in grado di fornire una funzionalità in base alla quale un utente potrebbe fare clic con il pulsante destro del mouse su un nodo su
l'area di disegno e vedere un elenco gerarchico e organizzato di parametri che sono impostabili sul
nodo e i suoi oggetti membri costitutivi e testo di aiuto e valori predefiniti per ciascuno
parametro.

Definizione Attributi
Forniamo agli utenti un modo per accedere ai valori in profondità nel sistema, senza dover sondare
gli accessori (puntatori) attraverso il sistema e le catene di puntatori per raggiungerli. Considera un
classe DropTailQueue che ha una variabile membro che è un intero senza segno m_maxPacchetti;
questa variabile membro controlla la profondità della coda.

Se osserviamo la dichiarazione di DropTailQueue, vediamo quanto segue:

classe DropTailQueue : coda pubblica {
pubblico:
TypeId statico GetTypeId (vuoto);
...

privato:
std::coda > m_pacchetti;
uint32_t m_maxPacchetti;
};

Consideriamo le cose che un utente potrebbe voler fare con il valore di m_maxPacchetti:

· Impostare un valore predefinito per il sistema, tale che ogni volta che un nuovo DropTailQueue è creato,
questo membro è inizializzato su quello predefinito.

· Imposta o ottieni il valore su una coda già istanziata.

Le cose di cui sopra in genere richiedono di fornire Impostato () e Ottieni () funzioni e qualche tipo di
valore predefinito globale.

Nel NS-3 sistema di attributi, queste definizioni di valore e registrazioni di funzioni accessorie
vengono spostati nel ID tipo classe; per esempio.:

NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);

ID tipo
DropTailQueue::GetTypeId (vuoto)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent ()
.Aggiungi costruttore ()
.AddAttribute ("MaxPackets",
"Il numero massimo di pacchetti accettati da questa DropTailQueue.",
Valore intero (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MakeUinteroChecker ())
;

ritorno a mare;
}

Il Aggiungi attributo () metodo sta eseguendo una serie di cose per il m_maxPacchetti valore:

· Associazione della variabile membro (di solito privata) m_maxPacchetti a una stringa pubblica
"MaxPacchetti".

· Fornire un valore predefinito (100 pacchetti).

· Fornire un testo di aiuto che definisca il significato del valore.

· Fornire un "Checker" (non utilizzato in questo esempio) che può essere utilizzato per impostare i limiti sul
intervallo di valori consentito.

Il punto chiave è che ora il valore di questa variabile e il suo valore predefinito sono accessibili
nello spazio dei nomi dell'attributo, che è basato su stringhe come "MaxPacchetti" e ID tipo Nome
stringhe. Nella prossima sezione, forniremo uno script di esempio che mostra come gli utenti possono
manipolare questi valori.

Nota che l'inizializzazione dell'attributo si basa sulla macro NS_OBJECT_ENSURE_REGISTERED
(Coda DropTail) essere chiamato; se lo lasci fuori dalla tua nuova implementazione di classe, il tuo
gli attributi non verranno inizializzati correttamente.

Sebbene abbiamo descritto come creare attributi, non abbiamo ancora descritto come accedere
e gestire questi valori. Per esempio, non c'è globali.h file di intestazione dove si trovano
immagazzinato; gli attributi sono memorizzati con le loro classi. Le domande che sorgono naturalmente sono come
gli utenti imparano facilmente tutti gli attributi dei loro modelli e come fa un utente?
accedere a questi attributi o documentare i loro valori come parte del registro dei loro
simulazione?

Documentazione dettagliata degli attributi effettivi definiti per un tipo e un elenco globale di
tutti gli attributi definiti, sono disponibili nella documentazione API. Per il resto
documento dimostreremo i vari modi per ottenere e impostare attributi
valori.

Configurazione Predefinito Valori
Configurazione::SetDefault e Riga di comando
Diamo un'occhiata a come uno script utente potrebbe accedere a un valore di attributo specifico. Stava andando a
Usa il src/punto-punto/esempi/valore-attributo-principale.cc sceneggiatura per illustrazione, con
alcuni dettagli eliminati. Il principale inizia la funzione:

// Questo è un esempio di base di come utilizzare il sistema di attributi per
// imposta e ottiene un valore nel sistema sottostante; vale a dire, un non firmato
// intero del numero massimo di pacchetti in una coda
//

int
principale (int argc, char *argv[])
{

// Per impostazione predefinita, l'attributo MaxPackets ha un valore di 100 pacchetti
// (questo valore predefinito può essere osservato nella funzione DropTailQueue::GetTypeId)
//
// Qui lo impostiamo su 80 pacchetti. Potremmo usare uno dei due tipi di valore:
// un valore basato su stringa o un valore Uintero
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// La chiamata alla funzione seguente è ridondante
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));

// Consenti all'utente di sovrascrivere qualsiasi impostazione predefinita e quanto sopra
// SetDefaults () in fase di esecuzione, tramite argomenti della riga di comando
// Ad esempio, tramite "--ns3::DropTailQueue::MaxPackets=80"
Riga di comando cmd;
// Questo fornisce un altro modo per impostare il valore dalla riga di comando:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);

La cosa principale da notare in quanto sopra sono le due chiamate equivalenti a Configurazione::SetDefault
(). Questo è il modo in cui impostiamo il valore predefinito per tutte le successive istanze
DropTailQueueS. Illustriamo che due tipi di Valore classi, a Valore stringa e
Valore intero class, può essere utilizzato per assegnare il valore all'attributo denominato da
"ns3::DropTailQueue::MaxPackets".

È anche possibile manipolare gli attributi usando il Riga di comando; abbiamo visto alcuni esempi
all'inizio del tutorial. In particolare, è semplice aggiungere un argomento stenografico
nome, come --maxPacchetti, per un attributo particolarmente rilevante per il tuo modello,
in questo caso "ns3::DropTailQueue::MaxPackets". Questo ha la caratteristica aggiuntiva che il
la stringa di aiuto per l'attributo verrà stampata come parte del messaggio di utilizzo per lo script.
Per ulteriori informazioni, vedere la Riga di comando Documentazione API.

Ora creeremo alcuni oggetti utilizzando l'API di basso livello. Le nostre code appena create lo faranno
non hanno m_maxPacchetti inizializzato a 100 pacchetti, come definito in
DropTailQueue::GetTypeId () funzione, ma a 80 pacchetti, a causa di ciò che abbiamo fatto sopra con
valori standard.:

Ptr n0 = CreaOggetto ();

Ptr net0 = CreaOggetto ();
n0->Aggiungi dispositivo (net0);

Ptr q = CreaOggetto ();
net0->AddQueue(q);

A questo punto, abbiamo creato un unico Nodo (n0) e un singolo Dispositivo PointToPointNet
(net0), e ha aggiunto a DropTailQueue (q) A net0.

costruttori, Helpers e ObjectFactory
Combinazioni arbitrarie di attributi possono essere impostate e recuperate dall'helper e dal basso livello
API; o dai costruttori stessi:

Ptr p =
CreaOggettoConAttributi
("MinX", DoubleValue (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", DoubleValue (5.0),
"DeltaY", DoubleValue (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));

o dalle API di supporto di livello superiore, come:

mobilità.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"));

Non lo illustriamo qui, ma puoi anche configurare un ObjectFactory con nuovi valori
per attributi specifici. Istanze create da ObjectFactory avrà quelli
attributi impostati durante la costruzione. È molto simile all'utilizzo di una delle API di supporto
per la classe.

Per rivedere, ci sono diversi modi per impostare i valori per gli attributi per le istanze di classe a be
creato in , il futuro:

· Configurazione::SetDefault ()

· Riga di comando::AddValue ()

· CreaOggettoConAttributi<> ()

· Varie API di supporto

Ma cosa succede se hai già creato un'istanza e vuoi cambiare il valore di?
attributo? In questo esempio, come possiamo manipolare il m_maxPacchetti valore del già
istanziato DropTailQueue? Ecco vari modi per farlo.

Cambiare Valori
Puntatore intelligente
Supponiamo che un puntatore intelligente (Ptr) a un dispositivo di rete pertinente è in mano; nella corrente
esempio, è il net0 puntatore.

Un modo per modificare il valore è accedere a un puntatore alla coda sottostante e modificarne il
attributo.

Innanzitutto, osserviamo che possiamo ottenere un puntatore alla (classe base) Fare la coda via , il
Dispositivo PointToPointNet attributi, dove è chiamato "TxQueue":

PuntatoreValoretmp;
net0->GetAttribute ("TxQueue", tmp);
Ptr txQueue = tmp.GetObject ();

Usando il OttieniOggetto () funzione, possiamo eseguire un downcast sicuro a a DropTailQueue, Dove
"MaxPacchetti" è un attributo:

Ptr dtq = txQueue->GetObject ();
NS_ASSERT (dtq!= 0);

Successivamente, possiamo ottenere il valore di un attributo su questa coda. Abbiamo introdotto il wrapper
Valore classi per i tipi di dati sottostanti, simili ai wrapper Java attorno a questi tipi,
poiché il sistema di attributi memorizza i valori serializzati in stringhe e non i tipi disparati.
Qui, il valore dell'attributo è assegnato a a Valore intero, e il Ottieni () metodo su questo
value produce il (srotolato) uint32_t.:

UintegerValue limite;
dtq->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("1. limite dtq: " << limit.Get () << " pacchetti");

Nota che il downcast di cui sopra non è realmente necessario; avremmo potuto ottenere l'attributo
valore direttamente da txQueue, che è un Oggetto:

txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " pacchetti");

Ora impostiamolo su un altro valore (60 pacchetti):

txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("3. txQueue limit modificato: " << limit.Get () << " pacchetti");

Config Spazio dei nomi sentiero
Un modo alternativo per ottenere l'attributo consiste nell'utilizzare lo spazio dei nomi di configurazione. Qui,
questo attributo risiede su un percorso noto in questo spazio dei nomi; questo approccio è utile se si
non ha accesso ai puntatori sottostanti e vorrebbe configurare uno specifico
attributo con una singola istruzione.:

Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
Valore intero (25));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("Il limite 4. txQueue è stato modificato tramite lo spazio dei nomi: "
<< limit.Get() << " pacchetti");

Il percorso di configurazione ha spesso la forma di ".../
nome>/ /.../ / " fare riferimento a una specifica istanza per indice di an
oggetto nel contenitore. In questo caso il primo contenitore è l'elenco di tutti NodoS; il
il secondo contenitore è l'elenco di tutti NetDevices sul prescelto Nodo. Infine, il
il percorso di configurazione di solito termina con una successione di attributi del membro, in questo caso il
"MaxPacchetti" attributo di "TxQueue" dei prescelti NetDevice.

Avremmo potuto anche utilizzare i caratteri jolly per impostare questo valore per tutti i nodi e tutti i dispositivi di rete
(che in questo semplice esempio ha lo stesso effetto del precedente Config::Imposta ()):

Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
Valore intero (15));
txQueue->GetAttribute ("MaxPackets", limite);
NS_LOG_INFO ("Il limite 5. txQueue è stato modificato tramite lo spazio dei nomi con caratteri jolly: "
<< limit.Get() << " pacchetti");

Oggetto Nome Servizi
Un altro modo per ottenere l'attributo è utilizzare la funzione di servizio del nome dell'oggetto. Il
il servizio nome oggetto ci consente di aggiungere elementi allo spazio dei nomi di configurazione sotto il
"/Nomi/" percorso con una stringa di nome definita dall'utente. Questo approccio è utile se non lo si fa
avere accesso ai puntatori sottostanti ed è difficile determinare il necessario
percorso dello spazio dei nomi di configurazione concreta.

Nomi::Aggiungi ("server", n0);
Nomi::Aggiungi ("server/eth0", net0);

...

Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));

Qui abbiamo aggiunto gli elementi del percorso "server" e "et0" sotto il "/Nomi/" spazio dei nomi, quindi
utilizzato il percorso di configurazione risultante per impostare l'attributo.

Vedere i nomi degli oggetti per una trattazione più completa del NS-3 spazio dei nomi di configurazione.

Implementazione/Attuazione Dettagli
Valore Classi
I lettori noteranno il TipoValore classi che sono sottoclassi di AttributoValore base
classe. Questi possono essere pensati come classi intermedie che vengono utilizzate per convertire da raw
tipi al AttributoValores utilizzati dal sistema di attributi. Ricorda che questo
il database contiene oggetti di molti tipi serializzati su stringhe. Conversioni a questo tipo
può essere fatto usando una classe intermedia (come Valore intero, o Doppio valore per
numeri in virgola mobile) o via stringhe. Conversione implicita diretta dei tipi in
AttributoValore non è molto pratico. Quindi, in quanto sopra, gli utenti possono scegliere di utilizzare
stringhe o valori:

p->Set ("cwnd", StringValue ("100")); // setter basato su stringhe
p->Set ("cwnd", IntegerValue (100)); // setter basato su interi

Il sistema fornisce alcune macro che aiutano gli utenti a dichiarare e definire nuovi AttributeValue
sottoclassi per i nuovi tipi che vogliono introdurre nel sistema di attributi:

· ATTRIBUTE_HELPER_HEADER

· ATTRIBUTE_HELPER_CPP

Per ulteriori informazioni, vedere la documentazione dell'API per questi costrutti.

Inizializzazione Ordine
Gli attributi nel sistema non devono dipendere dallo stato di nessun altro attributo in questo
sistema. Questo perché non è specificato un ordinamento dell'inizializzazione degli attributi, né
imposto, dal sistema. Un esempio specifico di questo può essere visto nella configurazione automatizzata
programmi come Archivio di configurazione. Sebbene un dato modello possa organizzarlo in modo che gli attributi
vengono inizializzati in un ordine particolare, un altro configuratore automatico può decidere
indipendentemente per modificare gli attributi, ad esempio, in ordine alfabetico.

A causa di questo ordinamento non specifico, nessun attributo nel sistema può avere alcuna dipendenza
su qualsiasi altro attributo. Come corollario, i setter degli attributi non devono mai fallire a causa dello stato
di un altro attributo. Nessun setter di attributi può modificare (impostare) qualsiasi altro valore di attributo come a
risultato della modifica del suo valore.

Questa è una restrizione molto forte e ci sono casi in cui gli attributi devono essere impostati
coerentemente per consentire il corretto funzionamento. A tal fine consentiamo il controllo della coerenza
quando , il attributo is utilizzato (cf. NS_ASSERT_MSG or NS_ABORT_MSG).

In generale, il codice dell'attributo per assegnare valori alle variabili membro della classe sottostante
viene eseguito dopo che un oggetto è stato costruito. Ma cosa succede se hai bisogno dei valori assegnati?
prima che il corpo del costruttore venga eseguito, perché ne hai bisogno nella logica del
costruttore? C'è un modo per farlo, usato per esempio nella classe Archivio di configurazione: chiama
ObjectBase::ConstructSelf () come segue:

NegozioConfig::ConfigStore ()
{
ObjectBase::ConstructSelf (AttributeConstructionList ());
// continua con il costruttore.
}

Attenzione che l'oggetto e tutte le sue classi derivate devono implementare anche a OttieniIdtipoistanza
() metodo. Altrimenti il ObjectBase::ConstructSelf () non sarà in grado di leggere il
attributi.

Aggiunta Attributi
Il NS-3 sistema inserirà un numero di valori interni sotto il sistema di attributi, ma
indubbiamente gli utenti vorranno estenderlo per raccogliere quelli che abbiamo perso o per aggiungere il loro
proprie classi al sistema.

Esistono tre casi d'uso tipici:

· Rendere accessibile un membro di dati di una classe esistente come attributo, quando non lo è già.

· Rendere una nuova classe in grado di esporre alcuni membri dati come Attributi assegnandole un TypeId.

· Creazione di un AttributoValore sottoclasse per una nuova classe in modo che sia possibile accedervi come un
Attributo.

Esistente Member Variabile
Considera questa variabile in TCPSocket:

uint32_t m_cWnd; // Finestra di congestione

Supponiamo che qualcuno che lavora con TCP voglia ottenere o impostare il valore di quella variabile
utilizzando il sistema di metadati. Se non fosse già fornito da NS-3, l'utente può dichiarare
la seguente aggiunta nel sistema di metadati di runtime (alla OttieniIDtipo() definizione per
TCPSocket):

.AddAttribute ("Finestra di congestione",
"Finestra di congestione TCP (byte)",
Valore intero (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MakeUinteroChecker ())

Ora, l'utente con un puntatore a a TCPSocket istanza può eseguire operazioni come
impostando e ottenendo il valore, senza dover aggiungere queste funzioni in modo esplicito.
Inoltre, possono essere applicati controlli di accesso, come consentire la lettura del parametro e
non scritto, oppure è possibile applicare il controllo dei limiti sui valori consentiti.

Nuovo Classe ID tipo
Qui discutiamo l'impatto su un utente che vuole aggiungere una nuova classe a NS-3. Che cosa
cose aggiuntive devono essere fatte per consentirgli di contenere gli attributi?

Supponiamo che la nostra nuova classe, chiamata ns3::MyMobility, è un tipo di modello di mobilità. Primo,
la classe dovrebbe ereditare dalla sua classe genitore, ns3::Modello di mobilità. Nel mia-mobilità.h
file di intestazione:

spazio dei nomi ns3 {

classe MyClass : MobilityModel pubblico
{

Ciò richiede che dichiariamo il Ottieni IDTipo () funzione. Questa è una funzione pubblica di una riga
dichiarazione:

pubblico:
/ **
* Registra questo tipo.
* \return L'oggetto TypeId.
*/
TypeId statico GetTypeId (vuoto);

Abbiamo già introdotto cosa a ID tipo la definizione sarà simile a mia-mobilità.cc
file di implementazione:

NS_OBJECT_ENSURE_REGISTERED (MyMobility);

ID tipo
MyMobility::GetTypeId (vuoto)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.SetParent ()
.SetGroupName ("Mobilità")
.Aggiungi costruttore ()
.AddAttribute ("Limiti",
"Confini dell'area da crociera.",
ValoreRettangolo (Rettangolo (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
CreaRectangleChecker ())
.AddAttribute ("Ora",
"Cambiare la direzione e la velocità correnti dopo essersi mossi per questo ritardo.",
TimeValue (secondi (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// etc (più parametri).
;
ritorno a mare;
}

Se non vogliamo sottoclasse da una classe esistente, nel file di intestazione ereditiamo semplicemente
da ns3::Oggetto, e nel file oggetto impostiamo la classe genitore su ns3::Oggetto con
.SetParent ().

Gli errori tipici qui riguardano:

· Non chiama NS_OBJECT_ENSURE_REGISTERED ()

· Non chiamare il Imposta genitore () metodo o chiamandolo con il tipo sbagliato.

· Non chiamare il Aggiungi costruttore () metodo o chiamandolo con il tipo sbagliato.

· Introdurre un errore tipografico nel nome del ID tipo nel suo costruttore.

· Non utilizzare il nome del tipo C++ completo della classe C++ che lo racchiude come nome del
ID tipo. Nota che "ns3::" è richiesto.

Nessuno di questi errori può essere rilevato dal NS-3 codebase, quindi si consiglia agli utenti di controllare
attentamente più volte che li hanno azzeccati.

Nuovo AttributoValore Tipo
Dal punto di vista dell'utente che scrive una nuova classe nel sistema e vuole che lo sia
accessibile come attributo, c'è principalmente il problema di scrivere le conversioni a/da
stringhe e valori degli attributi. La maggior parte di questo può essere copiata/incollata con codice macro. Per
esempio, si consideri una dichiarazione di classe per Rettangolo nel src/mobilità/modello directory:

testata Compila il
/ **
* \breve rettangolo 2d
*/
classe Rettangolo
{
...

doppio xMin;
doppio xMax;
doppio yMin;
doppia yMax;
};

Una chiamata macro e due operatori, devono essere aggiunti sotto la dichiarazione di classe per
trasforma un Rettangolo in un valore utilizzabile dal Attributo sistema:

std::ostream &operatore << (std::ostream &os, const Rettangolo &rettangolo);
std::istream &operatore >> (std::istream &è, Rettangolo &rettangolo);

ATTRIBUTE_HELPER_HEADER (Rettangolo);

Implementazione/Attuazione Compila il
Nella definizione di classe (. Cc file), il codice è simile a questo:

ATTRIBUTE_HELPER_CPP (Rettangolo);

std::ostream &
operatore << (std::ostream &os, const Rettangolo &rettangolo)
{
os << rettangolo.xMin << "|" << rettangolo.xMax << "|" << rettangolo.yMin << "|"
<< rettangolo.yMax;
ritorno os;
}
std::istream &
operatore >> (std::istream &is, Rettangolo &rettangolo)
{
carattere c1, c2, c3;
is >> rettangolo.xMin >> c1 >> rettangolo.xMax >> c2 >> rettangolo.yMin >> c3
>> rettangolo.yMax;
if (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
il ritorno è;
}

Questi operatori di flusso convertono semplicemente da una rappresentazione di stringa del rettangolo
("xMin|xMax|yMin|yMax") al Rettangolo sottostante. Il modellista deve specificarli
operatori e la rappresentazione sintattica di stringa di un'istanza della nuova classe.

Archivio di configurazione
Valori per NS-3 gli attributi possono essere memorizzati in un file di testo ASCII o XML e caricati in a
corsa di simulazione futura. Questa funzione è nota come NS-3 ConfigStore. Il Archivio di configurazione is
un database specializzato per i valori degli attributi e i valori predefiniti.

Sebbene sia un modulo gestito separatamente nel src/archivio-config/ directory, noi
documentalo qui a causa della sua unica dipendenza da NS-3 modulo principale e attributi.

Possiamo esplorare questo sistema usando un esempio da
src/config-store/examples/config-store-save.cc.

Innanzitutto, tutti gli utenti del Archivio di configurazione deve includere la seguente dichiarazione:

#include "ns3/config-store-module.h"

Successivamente, questo programma aggiunge un oggetto di esempio Esempio di configurazione per mostrare come il sistema è esteso:

class ConfigExample: oggetto pubblico
{
pubblico:
statico TypeId GetTypeId (vuoto) {
static TypeId tid = TypeId ("ns3::A")
.SetParent ()
.AddAttribute ("TestInt16", "testo della guida",
ValoreIntero (-2),
MakeIntegerAccessor (&A::m_int16),
MakeIntegerChecker ())
;
ritorno a mare;
}
int16_tm_int16;
};

NS_OBJECT_ENSURE_REGISTERED (esempio di configurazione);

Successivamente, utilizziamo il sottosistema Config per sovrascrivere le impostazioni predefinite in un paio di modi:

Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));

Ptr a_obj = CreaOggetto ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
"Impossibile impostare l'attributo intero di ConfigExample tramite Config::SetDefault");

Ptr a2_obj = CreaOggetto ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
Valore intero iv;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Get () == -3,
"Impossibile impostare l'attributo intero di ConfigExample tramite SetAttribute");

L'istruzione successiva è necessaria per assicurarsi che (uno degli) oggetti creati sia radicato
nello spazio dei nomi di configurazione come istanza dell'oggetto. Questo accade normalmente quando tu
aggregare oggetti ad a ns3::Nodo or ns3::Canale esempio, ma qui, visto che stiamo lavorando
a livello di base, dobbiamo creare un nuovo oggetto dello spazio dei nomi di root:

Config::RegisterRootNamespaceObject (a2_obj);

scrittura
Successivamente, vogliamo produrre l'archivio di configurazione. Gli esempi mostrano come farlo in due
formati, XML e testo non elaborato. In pratica, si dovrebbe eseguire questo passaggio appena prima di chiamare
Simulatore::Esegui () per salvare la configurazione finale appena prima di eseguire la simulazione.

Ci sono tre attributi che governano il comportamento di ConfigStore: "Modalità",
"Nome del file" e "Formato del file". La modalità (predefinita "Nessuno") configura se NS-3 dovrebbero
caricare la configurazione da un file precedentemente salvato (specificare "Modalità=Carica") o salvalo in un file
(specificare "Modalità=Salva"). Il nome del file (predefinito "") è dove dovrebbe leggere ConfigStore o
scrivere i suoi dati. Il formato file (predefinito "Testo grezzo") determina se il formato ConfigStore
è testo normale o Xml ("FormatoFile=Xml")

L'esempio mostra:

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
outputConfigStoreConfig;
outputConfig.ConfigureDefaults ();
outputConfig.ConfigureAttributes ();

// Output archivio di configurazione in formato txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
outputConfigStoreConfig2;
outputConfig2.ConfigureDefaults ();
outputConfig2.ConfigureAttributes ();

Simulatore::Esegui ();

Simulatore::Distruggi ();

Notare il posizionamento di queste affermazioni appena prima del Simulatore::Esegui () .
Questo output registra tutti i valori in atto appena prima di avviare la simulazione (ie.
dopo che tutta la configurazione è avvenuta).

Dopo l'esecuzione, puoi aprire il attributi-output.txt file e vedere:

default ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
predefinito ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
predefinito ns3::PcapFileWrapper::CaptureSize "65535"
predefinito ns3::PacketSocket::RcvBufSize "131072"
default ns3::ErrorModel::IsEnabled "true"
default ns3::RateErrorModel::ErrorUnit "EU_BYTE"
predefinito ns3::RateErrorModel::ErrorRate "0"
default ns3::RateErrorModel::RanVar "Uniform:0:1"
default ns3::DropTailQueue::Modalità "Pacchetti"
predefinito ns3::DropTailQueue::MaxPackets "100"
predefinito ns3::DropTailQueue::MaxBytes "6553500"
predefinito ns3::Application::StartTime "+0.0ns"
predefinito ns3::Application::StopTime "+0.0ns"
predefinito ns3::ConfigStore::Modalità "Salva"
predefinito ns3::ConfigStore::Nome file "output-attributes.txt"
predefinito ns3::ConfigStore::FileFormat "RawText"
predefinito ns3::ConfigExample::TestInt16 "-5"
RngSeed globale "1"
RngRun globale "1"
Global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
Global SchedulerType "ns3::MapScheduler"
Checksum globaleEnabled "false"
valore /$ns3::ConfigExample/TestInt16 "-3"

In quanto sopra, sono mostrati tutti i valori predefiniti per gli attributi per il modulo principale.
Quindi, tutti i valori per NS-3 vengono registrati i valori globali. Infine, il valore di
istanza di Esempio di configurazione che è stato radicato nello spazio dei nomi di configurazione viene mostrato. In un
di rose NS-3 programma, verrebbero mostrati molti più modelli, attributi e impostazioni predefinite.

Esiste anche una versione XML in attributi-output.xml:




























Questo file può essere archiviato con lo script di simulazione e i dati di output.

Lettura
Successivamente, discutiamo della configurazione delle simulazioni via un file di configurazione di input memorizzato. Ci sono
un paio di differenze chiave rispetto alla scrittura della configurazione finale della simulazione.
Per prima cosa, dobbiamo inserire affermazioni come queste all'inizio del programma, prima
vengono scritte le istruzioni di configurazione della simulazione (quindi i valori vengono registrati prima di essere
utilizzato nella costruzione di oggetti).

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
inputConfigStoreConfig;
inputConfig.ConfigureDefaults ();

Successivamente, tieni presente che il caricamento dei dati di configurazione di input è limitato all'attributo predefinito (ie.
non istanza) valori e valori globali. I valori delle istanze degli attributi non sono supportati
perché in questa fase della simulazione, prima che gli oggetti siano costruiti, non ci sono
tali istanze di oggetti intorno. (Nota, i futuri miglioramenti all'archivio di configurazione potrebbero cambiare
questo comportamento).

In secondo luogo, mentre l'output di Archivio di configurazione stato elencherà tutto nel database, il
il file di input deve contenere solo i valori specifici da sovrascrivere. Quindi, un modo per usare
questa classe per la configurazione del file di input deve generare una configurazione iniziale utilizzando il comando
produzione ("Salvare") "Modalità" descritto sopra, estrarre da quel file di configurazione solo il
elementi che si desidera modificare e spostare questi elementi minimi in un nuovo file di configurazione
che possono quindi essere modificati e caricati in sicurezza in una successiva esecuzione di simulazione.

Quando il Archivio di configurazione l'oggetto è istanziato, i suoi attributi "Nome del file", "Modalità" e
"Formato del file" deve essere impostato, sia via riga di comando o via dichiarazioni del programma

Lettura / scrittura Esempio
Come esempio più complicato, supponiamo di voler leggere in una configurazione di
valori predefiniti da un file di input denominato input-default.xml, e scrivi il risultato
attributi a un file separato chiamato attributi-output.xml.:

#include "ns3/config-store-module.h"
...
int principale (...)
{

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Load"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
inputConfigStoreConfig;
inputConfig.ConfigureDefaults ();

//
// Consenti all'utente di sovrascrivere uno dei valori predefiniti e il precedente Bind() at
// runtime, argomenti via riga di comando
//
Riga di comando cmd;
cmd.Parse (argc, argv);

// imposta la topologia
...

// Invoca appena prima di entrare in Simulator::Run()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Save"));
outputConfigStoreConfig;
outputConfig.ConfigureAttributes ();
Simulatore::Esegui ();
}

Archivio di configurazione GUI
Esiste un front-end basato su GTK per ConfigStore. Ciò consente agli utenti di utilizzare una GUI per
accedere e modificare le variabili. Le schermate di questa funzione sono disponibili nel |ns3|
Panoramica presentazione.

Per utilizzare questa funzione, è necessario installare libgk e libgtk-dev; un esempio Ubuntu
comando di installazione è:

$ sudo apt-get install libgtk2.0-0 libgtk2.0-dev

Per verificare se è configurato o meno, controlla l'output del passaggio:

$ ./waf configure --enable-examples --enable-tests

---- Riepilogo delle funzioni opzionali di NS-3:
Associazioni Python: abilitate
Supporto per la scansione dell'API Python: abilitato
NS-3 Click Integrazione: abilitato
GtkConfigStore: non abilitato (libreria 'gtk+-2.0 >= 2.12' non trovata)

Nell'esempio sopra, non è stato abilitato, quindi non può essere utilizzato fino a quando non è disponibile una versione adatta
installato e:

$ ./waf configure --enable-examples --enable-tests
$ ./waf

è rieseguito.

L'utilizzo è quasi lo stesso della versione non basata su GTK, ma non ce ne sono Archivio di configurazione
attributi coinvolti:

// Invoca appena prima di entrare in Simulator::Run()
Configurazione GtkConfigStore;
config.ConfigureDefaults ();
config.ConfigureAttributes ();

Ora, quando esegui lo script, dovrebbe apparire una GUI che ti consente di aprire i menu di
attributi su diversi nodi/oggetti, quindi avvia l'esecuzione della simulazione quando
sono fatti.

Futuro lavoro
Ci sono un paio di possibili miglioramenti:

· Salva un numero di versione univoco con data e ora all'inizio del file.

· Salva il seme iniziale da qualche parte.

· Fare in modo che ogni variabile casuale serializzi il proprio seme iniziale e rileggerlo in seguito.

Oggetto nomi
segnaposto capitolo

Registrazione
Il NS-3 la funzione di registrazione può essere utilizzata per monitorare o eseguire il debug dell'avanzamento della simulazione
programmi. L'output di registrazione può essere abilitato dalle istruzioni del programma nel tuo principale() programma o
mediante l'uso di NS_LOG variabile d'ambiente.

Le istruzioni di registrazione non vengono compilate in build ottimizzate di NS-3. Per utilizzare la registrazione, uno
deve compilare la build di debug (predefinita) di NS-3.

Il progetto non garantisce se l'output di registrazione rimarrà lo stesso nel corso
volta. Gli utenti sono avvisati di non creare framework di output di simulazione oltre alla registrazione
codice, poiché l'uscita e il modo in cui l'uscita è abilitata possono cambiare nel tempo.

Panoramica
NS-3 le istruzioni di registrazione sono in genere utilizzate per registrare vari eventi di esecuzione del programma, come
come il verificarsi di eventi di simulazione o l'uso di una particolare funzione.

Ad esempio, questo frammento di codice proviene da Protocollo Ipv4L3::IsDestinationAddress():

if (indirizzo == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("Per me (indirizzo di trasmissione dell'interfaccia)");
return true;
}

Se la registrazione è stata abilitata per il Protocollo IPv4L3 componente con una gravità di LOGICA or
sopra (vedi sotto per la gravità del registro), verrà stampata la dichiarazione; altrimenti, è
sarà soppresso.

Abilitare Uscita
Esistono due modi in cui gli utenti in genere controllano l'output del registro. Il primo è impostando il
NS_LOG variabile d'ambiente; per esempio:

$ NS_LOG="*" ./waf --run first

eseguirà il prima di tutto programma tutorial con tutti gli output di registrazione. (Le specifiche del NS_LOG
formato sarà discusso di seguito.)

Questo può essere reso più granulare selezionando i singoli componenti:

$ NS_LOG="Protocollo Ipv4L3" ./waf --run first

L'output può essere ulteriormente personalizzato con opzioni di prefisso.

Il secondo modo per abilitare la registrazione è utilizzare istruzioni esplicite nel programma, come in
, il prima di tutto programma didattico:

int
principale (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
...

(Il significato di LOG_LEVEL_INFO, e altri possibili valori, saranno discussi di seguito.)

NS_LOG Sintassi
Il NS_LOG La variabile di ambiente contiene un elenco di componenti e opzioni di registro. Tronco d'albero
i componenti sono separati da caratteri `:':

$ NS_LOG=" : ..."

Le opzioni per ogni componente di registro sono fornite come flag dopo ogni componente di registro:

$ NS_LOG=" = | ...: ..."

Le opzioni controllano la gravità e il livello per quel componente e se facoltativo
le informazioni dovrebbero essere incluse, come il tempo di simulazione, il nodo di simulazione, la funzione
nome, e la severità simbolica.

Log Componenti
Generalmente un componente di registro fa riferimento a un singolo codice sorgente . Cc file, e comprende il
intero file.

Alcuni helper hanno metodi speciali per abilitare la registrazione di tutti i componenti in un modulo,
abbracciando diverse unità di compilazione, ma raggruppate logicamente insieme, come ad esempio NS-3
codice Wi-Fi:

WifiHelper wifiHelper;
wifiHelper.EnableLogComponents ();

Il NS_LOG Il carattere jolly `*' del componente log abiliterà tutti i componenti.

Per vedere quali componenti di registro sono definiti, funzionerà uno di questi:

$ NS_LOG="elenco-stampa" ./waf --run ...

$ NS_LOG="pippo" # un token che non corrisponde a nessun componente di log

Il primo modulo stamperà il nome e i flag abilitati per tutti i componenti di registro che sono
collegato in; provalo con simulatore di graffi. Il secondo modulo stampa tutti i log registrati
componenti, quindi uscire con un errore.

Gravità e Livello Opzioni
I singoli messaggi appartengono a un'unica "classe di gravità", impostata dalla macro che crea il
Messaggio. Nell'esempio sopra, NS_LOG_LOGIC(..) crea il messaggio nel LOG_LOGICA
classe di gravità.

Le seguenti classi di gravità sono definite come enum costanti:

? ?
│Classe di gravità │ Significato │
? ?
LOG_NESSUNO │ L'impostazione predefinita, nessuna registrazione │
? ?
ERRORE_LOG │ Solo messaggi di errore gravi │
? ?
LOG_AVVISO │ Messaggi di avviso │
? ?
LOG_DEBUG │ Per l'uso nel debug │
? ?
LOG_INFO Informativo │
? ?
FUNZIONE_LOG │ Tracciamento delle funzioni │
? ?
LOG_LOGICA │ Tracciamento del flusso di controllo all'interno di
│ │ funzioni │
? ?

In genere si vogliono vedere i messaggi in una data classe di gravità e superiore. Questo viene fatto da
definizione di "livelli" di registrazione inclusiva:

? ?
│Livello │ Significato │
? ?
LOG_LEVEL_ERRORE Solo ERRORE_LOG classe di gravità │
│ │ messaggi. ?
? ?
LOG_LEVEL_AVVISOLOG_AVVISO e al di sopra. ?
? ?
LOG_LEVEL_DEBUGLOG_DEBUG e al di sopra. ?
? ?
LOG_LEVEL_INFOLOG_INFO e al di sopra. ?
? ?
LOG_LIVELLO_FUNZIONEFUNZIONE_LOG e al di sopra. ?
? ?
LOG_LEVEL_LOGICALOG_LOGICA e al di sopra. ?
? ?
LOG_LIVELLO_ALL │ Tutte le classi di gravità. ?
? ?
REGISTRO_TUTTI Sinonimo di LOG_LIVELLO_ALL
? ?

La classe di gravità e le opzioni di livello possono essere fornite nel NS_LOG variabile d'ambiente di
questi token:

?
│Classe │ Livello │
?
errorelivello_errore
?
avvertirelivello_avviso
?
mettere a puntolivello_debug
?
infoinfo_livello
?
funzionefunzione_livello
?
logicalivello_logica
?
│ │ livello_tutto
│ │ contro tutti i
│ │ *
?

L'utilizzo di un token di classe di gravità abilita i messaggi di registro solo con tale gravità. Per esempio,
NS_LOG="*=avviso" non produrrà messaggi con gravità errore. NS_LOG="*=level_debug" andrete a
messaggi di output a livelli di gravità mettere a punto e al di sopra.

Classi e livelli di gravità possono essere combinati con il `|' operatore:
NS_LOG="*=level_warn|logica" emetterà messaggi a livelli di gravità errore, avvertire e logica.

Il NS_LOG carattere jolly del livello di gravità `*' e contro tutti i sono sinonimi di livello_tutto.

Per i componenti di registro solo menzionati in NS_LOG

$ NS_LOG=" :..."

la gravità predefinita è LOG_LIVELLO_ALL.

Prefisso Opzioni
Un certo numero di prefissi può aiutare a identificare dove e quando ha avuto origine un messaggio e in che cosa
severità.

Le opzioni di prefisso disponibili (come enum costanti) sono

? ?
│Simbolo prefisso │ Significato │
? ?
LOG_PREFIX_FUNC │ Prefisso il nome del chiamante │
│ funzione. ?
? ?
LOG_PREFIX_TIME │ Prefissare l'ora della simulazione. ?
? ?
LOG_PREFIX_NODE │ Prefisso l'id del nodo. ?
? ?
LOG_PREFIX_LEVEL │ Prefissare il livello di gravità. ?
? ?
LOG_PREFIX_ALL │ Abilita tutti i prefissi. ?
? ?

Le opzioni del prefisso sono descritte brevemente di seguito.

Le opzioni possono essere fornite nel NS_LOG variabile di ambiente da questi token:

?
│Token │ Alternativo │
?
prefisso_funzionefunc
?
prefisso_oratempo
?

prefisso_nodonodo
?
prefisso_livellolivello
?
prefisso_tuttocontro tutti i
│ │ *
?

Per i componenti di registro solo menzionati in NS_LOG

$ NS_LOG=" :..."

le opzioni di prefisso predefinite sono LOG_PREFIX_ALL.

Gravità Prefisso
La classe di gravità di un messaggio può essere inclusa con le opzioni prefisso_livello or livello.
Ad esempio, questo valore di NS_LOG abilita la registrazione per tutti i componenti di registro (`*') e tutti
classi di gravità (=tutti) e fa precedere il messaggio dalla classe di gravità (|livello_prefisso).

$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
Simulatore di graffi
Messaggio di errore [ERRORE]
[AVVISO] messaggio di avviso
[DEBUG] messaggio di debug
[INFO] messaggio informativo
Messaggio funzione [FUNCT]
[LOGICA] messaggio logico

Ora Prefisso
Il tempo di simulazione può essere incluso con le opzioni prefisso_ora or tempo. Questo stampa il
tempo di simulazione in secondi.

Nodo Prefisso
L'ID del nodo di simulazione può essere incluso con le opzioni prefisso_nodo or nodo.

Funzione Prefisso
Il nome della funzione chiamante può essere incluso con le opzioni prefisso_funzione or func.

NS_LOG I caratteri jolly
Il carattere jolly del componente di log `*' abiliterà tutti i componenti. Per abilitare tutti i componenti in a
uso del livello di gravità specifico *=.

Il carattere jolly dell'opzione del livello di gravità "*" è sinonimo di contro tutti i. Questo deve avvenire prima di qualsiasi
`|' caratteri che separano le opzioni. Per abilitare tutte le classi di gravità, utilizzare =*,
or =*|.

L'opzione carattere jolly `*' o token contro tutti i abilita tutte le opzioni di prefisso, ma deve verificarsi dopo a
`|' carattere. Per abilitare una classe o un livello di gravità specifico e tutti i prefissi, utilizzare
= |*.

Il carattere jolly dell'opzione combinata ** abilita tutte le severità e tutti i prefissi; Per esempio,
=**.

L'uber-jolly *** abilita tutte le severità e tutti i prefissi per tutti i componenti del registro.
Questi sono tutti equivalenti:

$ NS_LOG="***" ... $ NS_LOG="*=tutti|*" ... $ NS_LOG="*=*|tutti" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=livello_tutto|*" ... $ NS_LOG="*=*|prefisso_tutto" ...
$ NS_LOG="*=*|*" ...

Attenzione: anche il banale simulatore di graffi produce oltre 46 linee di output con
NS_LOG="***"!

Come a aggiungere registrazione a il tuo codice
Aggiungere la registrazione al codice è molto semplice:

1. Richiamare il NS_LOG_COMPONENT_DEFINE (...); macro all'interno di namespace ns3.
Crea un identificatore di stringa univoco (di solito basato sul nome del file e/o della classe
definito all'interno del file) e registrarlo con una chiamata macro come segue:

spazio dei nomi ns3 {

NS_LOG_COMPONENT_DEFINE ("Protocollo Ipv4L3");
...

Questo registra Protocollo IPv4L3 come componente di registro.

(La macro è stata scritta con cura per consentire l'inclusione all'interno o all'esterno di
namespace ns3e l'utilizzo varierà nella base di codice, ma l'intento originale era quello
registra questo al di fuori di spazio dei nomi ns3 in ambito globale del file.)

2. Aggiungere istruzioni di registrazione (chiamate macro) alle funzioni e ai corpi delle funzioni.

Registrazione Macro
Le macro di registrazione ei livelli di gravità associati sono

┌──────────────┬────────────────────────┐
│Classe di gravità │ Macro │
├──────────────┼────────────────────────────────────────────┤
LOG_NESSUNO │ (nessuno necessario) │
├──────────────┼────────────────────────────────────────────┤
ERRORE_LOGNS_LOG_ERRORE (...);
├──────────────┼────────────────────────────────────────────┤
LOG_AVVISONS_LOG_WARN (...);
├──────────────┼────────────────────────────────────────────┤
LOG_DEBUGNS_LOG_DEBUG (...);
├──────────────┼────────────────────────────────────────────┤
LOG_INFONS_LOG_INFO (...);
├──────────────┼────────────────────────────────────────────┤
FUNZIONE_LOGNS_LOG_FUNCTION (...);
├──────────────┼────────────────────────────────────────────┤
LOG_LOGICANS_LOG_LOGICA (...);
└──────────────┴────────────────────────┘──────────┘

Le macro funzionano come streamer di output, quindi tutto ciò a cui puoi inviare std::out, Iscritto
by << operatori, è consentito:

void MiaClasse::Check (int value, char * item)
{
NS_LOG_FUNCTION (questo << arg << elemento);
se (arg > 10)
{
NS_LOG_ERROR ("è stato rilevato un valore errato " << valore <
" durante il controllo di " << nome << "!");
}
...
}

Si noti che NS_LOG_FUNCTION inserisce automaticamente un `,' (virgola-spazio) separatore tra
ciascuno dei suoi argomenti. Ciò semplifica la registrazione degli argomenti della funzione; basta concatenare
loro << come nell'esempio sopra.

Incondizionato Registrazione
Per comodità, il NS_LOG_UNCOND (...); macro registrerà sempre i suoi argomenti, anche se
il componente log associato non è abilitato a nessuna gravità. Questa macro non ne usa
delle opzioni del prefisso. Si noti che la registrazione è abilitata solo nelle build di debug; questa macro
non produrrà output nelle build ottimizzate.

Linee Guida
· Avvia ogni metodo di classe con NS_LOG_FUNCTION (Questo << argomenti...); Ciò consente facile
traccia delle chiamate di funzione.

· Tranne: non registrare gli operatori oi costruttori di copie esplicite, poiché questi causeranno
ricorsione infinita e overflow dello stack.

· Per i metodi senza argomenti utilizzare la stessa forma: NS_LOG_FUNCTION (questo);

· Per le funzioni statiche:

· Con l'uso di argomenti NS_LOG_FUNCTION (...); come normale.

· Uso senza argomenti NS_LOG_FUNCTION_NOARGS ();

· Utilizzo NS_LOG_ERRORE per gravi condizioni di errore che probabilmente invalidano la simulazione
esecuzione.

· Utilizzo NS_LOG_WARN per condizioni insolite che possono essere correggibili. Si prega di dare alcuni suggerimenti
per quanto riguarda la natura del problema e come potrebbe essere corretto.

· NS_LOG_DEBUG è solitamente usato in un ad hoc modo di comprendere l'esecuzione di un modello.

· Utilizzo NS_LOG_INFO per ulteriori informazioni sull'esecuzione, come la dimensione di a
struttura dei dati quando si aggiunge/rimuove da essa.

· Utilizzo NS_LOG_LOGICA per tracciare rami logici importanti all'interno di una funzione.

· Verifica che le modifiche apportate alla registrazione non interrompano il codice. Esegui alcuni programmi di esempio con
tutti i componenti di log attivati ​​(es NS_LOG="***").

Tracciato
Il sottosistema di tracciamento è uno dei meccanismi più importanti da comprendere NS-3. in
la maggior parte dei casi, NS-3 gli utenti avranno un'idea brillante per una rete nuova e migliorata
caratteristica. Per verificare che questa idea funzioni, il ricercatore apporterà modifiche a un
sistema esistente e quindi eseguire esperimenti per vedere come si comporta la nuova funzionalità tramite la raccolta
statistiche che catturano il comportamento della funzione.

In altre parole, lo scopo principale dell'esecuzione di una simulazione è generare output per ulteriori informazioni
studia. Nel NS-3, il sottosistema che consente a un ricercatore di fare ciò è il tracciamento
sottosistema.

Tracciato Motivazione
Ci sono molti modi per ottenere informazioni da un programma. Il modo più semplice è
per stampare direttamente le informazioni sullo standard output, come in,

#includere
...
intero principale ()
{
...
std::cout << "Il valore di x è " << x << std::endl;
...
}

Questo è fattibile in piccoli ambienti, ma man mano che le tue simulazioni diventano sempre di più
complicato, ti ritrovi con sempre più stampe e il compito di analizzare ed eseguire
i calcoli sull'output iniziano a diventare sempre più difficili.

Un'altra cosa da considerare è che ogni volta che è necessario un nuovo bocconcino, il core del software
deve essere modificato e un'altra stampa introdotta. Non esiste un modo standardizzato per controllare tutto
di questo output, quindi la quantità di output tende a crescere senza limiti. Alla fine, il
la larghezza di banda richiesta per emettere semplicemente queste informazioni inizia a limitare il tempo di esecuzione
della simulazione. I file di output crescono fino a dimensioni enormi e l'analisi diventa un file
problema.

NS-3 fornisce un semplice meccanismo per la registrazione e per fornire un certo controllo sull'output tramite
Log Componenti, ma il livello di controllo non è affatto molto fine. La registrazione
modulo è uno strumento relativamente contundente.

È auspicabile disporre di una struttura che consenta di raggiungere il sistema centrale e solo
ottenere le informazioni richieste senza dover modificare e ricompilare il sistema principale. Persino
meglio sarebbe un sistema che informasse l'utente quando un elemento di interesse è cambiato o un
è successo un evento interessante.

Il NS-3 il sistema di tracciamento è progettato per funzionare lungo queste linee ed è ben integrato con
i sottogruppi Attributo e Config che consentono scenari di utilizzo relativamente semplici.

Panoramica
Il sottosistema di traccia si basa molto su NS-3 Meccanismi di callback e attributo. Voi
dovrebbe leggere e comprendere le sezioni corrispondenti del manuale prima di tentare di farlo
capire il sistema di tracciamento.

Il NS-3 sistema di tracciamento è costruito sui concetti di fonti di tracciamento indipendenti e
lavelli di tracciamento; insieme a un meccanismo uniforme per collegare le sorgenti ai lavandini.

Le sorgenti di traccia sono entità che possono segnalare eventi che accadono in una simulazione e fornire
accesso a dati sottostanti interessanti. Ad esempio, un'origine di traccia potrebbe indicare quando a
il pacchetto viene ricevuto da un dispositivo di rete e fornisce l'accesso al contenuto del pacchetto per
la traccia interessata sprofonda. Una fonte di traccia potrebbe anche indicare quando uno stato interessante
il cambiamento avviene in un modello. Ad esempio, la finestra di congestione di un modello TCP è un numero primo
candidato per una fonte di traccia.

Le fonti di traccia non sono utili da sole; devono essere collegati ad altri pezzi di codice
che effettivamente fanno qualcosa di utile con le informazioni fornite dalla fonte. Il
le entità che consumano informazioni di traccia sono chiamate sink di traccia. Le fonti di traccia lo sono
generatori di eventi e sink di traccia sono consumatori.

Questa divisione esplicita consente di spargere un gran numero di fonti di traccia
il sistema in luoghi che gli autori del modello ritengono possa essere utile. A meno che un utente non si connetta a
trace sink a una di queste origini, non viene emesso nulla. Questa disposizione consente relativamente
utenti non sofisticati per collegare nuovi tipi di sink alle origini di traccia esistenti, senza
che richiedono la modifica e la ricompilazione del core o dei modelli del simulatore.

Possono esserci zero o più consumatori di eventi di traccia generati da un'origine di traccia. Uno può
pensa a una fonte di traccia come a una sorta di collegamento informativo da punto a multipunto.

Il "protocollo di trasporto" per questo collegamento concettuale punto-multipunto è un NS-3 Richiamata.

Ricordiamo dalla sezione Richiamata che la funzione di richiamata è un modo per consentire l'ingresso di due moduli
il sistema per comunicare tramite chiamate di funzione e allo stesso tempo disaccoppiare la chiamata
funzione dalla classe chiamata completamente. Questo è lo stesso requisito di cui sopra
per il sistema di tracciabilità.

Fondamentalmente, una fonte di traccia is una richiamata a cui possono essere registrate più funzioni.
Quando un sink di traccia esprime interesse a ricevere eventi di traccia, aggiunge un callback a a
elenco delle richiamate trattenute dall'origine di traccia. Quando accade un evento interessante, la traccia
la sorgente invoca il suo operatore() fornendo zero o più parametri. Questo dice alla fonte di
scorrere il suo elenco di callback invocando ciascuno a turno. In questo modo il/i parametro/i
vengono comunicati ai sink di traccia, che sono solo funzioni.

Il Più semplice Esempio
Sarà utile fare un rapido esempio solo per rafforzare ciò che abbiamo detto.:

#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/valore-tracciato.h""
#include "ns3/trace-source-accessor.h"

#includere

usando lo spazio dei nomi ns3;

La prima cosa da fare è includere i file richiesti. Come accennato in precedenza, il sistema di traccia
fa un uso massiccio dei sistemi Object e Attribute. I primi due includono portare in
dichiarazioni per tali sistemi. Il file, valore-tracciato.h porta il necessario
dichiarazioni per tracciare dati che obbediscono alla semantica del valore.

In generale, la semantica del valore significa solo che puoi passare l'oggetto in giro, non un
indirizzo. Per utilizzare la semantica dei valori devi avere un oggetto con an
disponibili il costruttore di copie associato e l'operatore di assegnazione. Estendiamo i requisiti
per parlare dell'insieme di operatori predefiniti per i tipi di dati normali (POD).
Operatore=, operatore++, operatore--, operatore+, operatore==, ecc.

Tutto ciò significa che sarai in grado di tracciare le modifiche a un oggetto realizzato utilizzando
quegli operatori.:

classe MyObject : oggetto pubblico
{
pubblico:
statico TypeId GetTypeId (vuoto)
{
statico TypeId tid = TypeId ("MyObject")
.SetParent (Oggetto::GetTypeId ())
.Aggiungi costruttore ()
.AddTraceSource ("MyInteger",
"Un valore intero da tracciare.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
ritorno a mare;
}

MioOggetto () {}
Valore tracciato m_mioInt;
};

Poiché il sistema di tracciamento è integrato con gli attributi e gli attributi funzionano con gli oggetti,
ci deve essere un NS-3 Oggetto affinché la fonte della traccia possa abitare. Le due linee importanti di
codice sono i .AddTraceSource e le Valore tracciato dichiarazione.

Il .AddTraceSource fornisce i "ganci" utilizzati per connettere la sorgente di traccia al file
mondo esterno. Il Valore tracciato dichiarazione fornisce l'infrastruttura che sovraccarica il file
operatori sopra menzionati e guida il processo di callback.:

nulla
IntTrace (Int vecchioValore, Int nuovoValore)
{
std::cout << "Tracciato" << oldValue << " to " << newValue << std::endl;
}

Questa è la definizione del sink di traccia. Corrisponde direttamente a una funzione di callback.
Questa funzione verrà chiamata ogni volta che uno degli operatori del Valore tracciato is
eseguito.:

int
principale (int argc, char *argv[])
{
pt mioOggetto = CreaOggetto ();

myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));

mioOggetto->m_mioInt = 1234;
}

In questo frammento di codice, la prima cosa da fare è creare l'oggetto in cui
la sorgente della traccia vive.

Il prossimo passo, il TraceConnectWithoutContext, costituisce il collegamento tra la traccia
sorgente e il sink di traccia. Notare il Effettua una richiamata funzione modello. Richiamo dal
Sezione di richiamata che crea il funtore specializzato responsabile della fornitura del
stracarico operatore() utilizzato per "attivare" la richiamata. Gli operatori sovraccaricati (++, --, ecc.)
userà questo operatore() per richiamare effettivamente la richiamata. Il TraceConnectWithoutContext,
accetta un parametro stringa che fornisce il nome dell'attributo assegnato alla traccia
fonte. Ignoriamo la parte relativa al contesto per ora poiché non è ancora importante.

Infine, la linea:

mioOggetto->m_mioInt = 1234;

deve essere interpretato come un'invocazione di operatore= sulla variabile membro m_mioInt con
l'intero 1234 passato come parametro. Si scopre che questo operatore è definito (da
Valore tracciato) per eseguire una richiamata che restituisce void e accetta due valori interi come
parametri -- un vecchio valore e un nuovo valore per l'intero in questione. Questo è esattamente
la firma della funzione per la funzione di callback che abbiamo fornito -- IntTrace.

Per riassumere, un'origine di traccia è, in sostanza, una variabile che contiene un elenco di callback. UN
trace sink è una funzione utilizzata come destinazione di un callback. L'attributo e il tipo di oggetto
i sistemi informativi vengono utilizzati per fornire un modo per collegare le origini di traccia ai sink di traccia. Il
l'atto di "colpire" una sorgente di traccia sta eseguendo un operatore sulla sorgente di traccia che si attiva
richiamate. In questo modo i callback del sink di traccia registrano l'interesse per l'origine
essere chiamato con i parametri forniti dalla sorgente.

utilizzando , il Config Sottosistema a Connettere a Traccia fonti
Il TraceConnectWithoutContext la chiamata mostrata sopra nel semplice esempio è in realtà molto
usato raramente nel sistema. Più tipicamente, il Config il sottosistema viene utilizzato per consentire la selezione
una sorgente di traccia nel sistema che utilizza ciò che viene chiamato a config sentiero.

Ad esempio, si potrebbe trovare qualcosa che assomiglia al seguente nel sistema (preso
da esempi/tcp-large-transfer.cc):

void CwndTracer (uint32_t vecchiaval, uint32_t nuovaval) {}

...

Configurazione::ConnectWithoutContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback (&CwndTracer));

Questo dovrebbe sembrare molto familiare. È la stessa cosa dell'esempio precedente, tranne quello
una funzione membro statica di classe Config viene chiamato invece di un metodo attivo Oggetto;
e invece di un Attributo nome, viene fornito un percorso.

La prima cosa da fare è leggere il percorso all'indietro. L'ultimo segmento del percorso deve essere
an Attributo di Oggetto. In effetti, se avessi un puntatore al Oggetto quello ha il
"Finestra Congestione" Attributo a portata di mano (chiamalo l'oggetto), potresti scriverlo proprio come il
esempio precedente:

void CwndTracer (uint32_t vecchiaval, uint32_t nuovaval) {}

...

theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));

Si scopre che il codice per Configurazione::ConnectWithoutContext fa esattamente questo. Questo
la funzione prende un percorso che rappresenta una catena di Oggetto puntatori e li segue fino ad esso
arriva alla fine del percorso e interpreta l'ultimo segmento come un Attributo sull'ultimo
oggetto. Esaminiamo cosa succede.

Il carattere "/" iniziale nel percorso si riferisce a un cosiddetto spazio dei nomi. Uno di
lo spazio dei nomi predefinito nel sistema di configurazione è "NodeList" che è un elenco di tutti i
nodi nella simulazione. Gli elementi nell'elenco sono indicati da indici nell'elenco, quindi
"/NodeList/0" si riferisce al nodo zero nell'elenco di nodi creato dalla simulazione.
Questo nodo è in realtà un Ptr e così è una sottoclasse di an ns3::Oggetto.

Come descritto nella sezione Modello a oggetti, NS-3 supporta un modello di aggregazione di oggetti. Il
il segmento di percorso successivo inizia con il carattere "$" che indica a OttieniOggetto la chiamata dovrebbe essere
fatto cercando il tipo che segue. Quando un nodo viene inizializzato da un
Internet StackHelper un certo numero di interfacce sono aggregate al nodo. Uno di questi è il
Protocollo TCP di livello quattro. Il tipo di runtime di questo oggetto protocollo è ns3::TcpL4Protocollo''.
Quando , il ``Ottieni oggetto viene eseguito, restituisce un puntatore all'oggetto di questo tipo.

Il Protocollo TcpL4 class definisce un attributo chiamato "SocketList" che è un elenco di
prese. Ogni presa è in realtà un ns3::Oggetto con il proprio Attributi. Gli articoli in
l'elenco dei socket è indicato dall'indice proprio come nella NodeList, quindi "SocketList/0"
fa riferimento al socket zero nell'elenco dei socket sul nodo zero in NodeList --
il primo nodo costruito nella simulazione.

Questa presa, il cui tipo risulta essere un ns3::TcpSocketImpl definisce un attributo
chiamato "CongestionWindow" che è a Valore tracciato.
Configurazione::ConnectWithoutContext ora fa un:

oggetto->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));

utilizzando il puntatore oggetto da "SocketList/0" che effettua la connessione tra la traccia
sorgente definita nel socket alla richiamata -- CwndTracer.

Ora, ogni volta che viene apportata una modifica al Valore tracciato che rappresenta la congestione
finestra nel socket TCP, verrà eseguita la callback registrata e la funzione
CwndTracer verrà chiamato stampando il vecchio e il nuovo valore della congestione TCP
finestra.

utilizzando , il Tracciato API
Esistono tre livelli di interazione con il sistema di tracciamento:

· L'utente principiante può controllare facilmente quali oggetti partecipano al tracciamento;

· Gli utenti intermedi possono estendere il sistema di tracciamento per modificare il formato di output generato
oppure utilizzare le sorgenti di traccia esistenti in modi diversi, senza modificare il nucleo del file
simulatore;

· Gli utenti avanzati possono modificare il core del simulatore per aggiungere nuove fonti di tracciamento e sink.

utilizzando Traccia Helpers
Il NS-3 gli helper di traccia forniscono un ambiente ricco per la configurazione e la selezione di diversi
tracciare gli eventi e scriverli su file. Nelle sezioni precedenti, principalmente "Edilizia
Topologie", abbiamo visto diverse varietà di metodi di supporto della traccia progettati per l'uso
all'interno di altri (dispositivo) helper.

Forse ricorderete di aver visto alcune di queste variazioni:

pointToPoint.EnablePcapAll ("secondo");
pointToPoint.EnablePcap ("secondo", p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap ("terzo", csmaDevices.Get (0), true);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

Ciò che potrebbe non essere ovvio, tuttavia, è che esiste un modello coerente per tutti
metodi relativi alla traccia trovati nel sistema. Ora ci prendiamo un po' di tempo e diamo un'occhiata
al "quadro generale".

Attualmente ci sono due casi d'uso principali degli helper di tracciamento NS-3: Assistenti del dispositivo
e assistenti di protocollo. Gli assistenti del dispositivo esaminano il problema di specificare quali tracce dovrebbero
essere abilitato tramite un nodo, una coppia di dispositivi. Ad esempio, potresti voler specificare quel pcap
la traccia deve essere abilitata su un particolare dispositivo su un nodo specifico. Ciò deriva dal
NS-3 modello concettuale del dispositivo, e anche i modelli concettuali dei vari dispositivi
aiutanti. A seguito naturalmente di ciò, i file creati seguono a
- - convenzione di denominazione.

Gli helper del protocollo esaminano il problema di specificare quali tracce devono essere abilitate
una coppia di protocollo e interfaccia. Ciò deriva dal NS-3 modello concettuale dello stack di protocollo,
e anche i modelli concettuali di Internet Stack Helper. Naturalmente, i file di traccia
dovrebbe seguire a - - convenzione di denominazione.

Gli helper di traccia rientrano quindi naturalmente in una tassonomia bidimensionale. Ci sono
sottigliezze che impediscono a tutte e quattro le classi di comportarsi in modo identico, ma ci sforziamo di farlo
farli funzionare tutti nel modo più simile possibile; e quando possibile ci sono analoghi per
tutti i metodi in tutte le classi.

┌───────────────┬──────┬───────┐
│ │ pcap │ ascii │
├───────────────┼──────┼───────┤
│Assistente dispositivo │ │ │
├───────────────┼──────┼───────┤
│Protocollo Helper │ │ │
└────────────────┴──────┴───────┘

Usiamo un approccio chiamato a mixin per aggiungere funzionalità di tracciamento alle nostre classi di supporto. UN
mixin è una classe che fornisce funzionalità a quella ereditata da una sottoclasse.
Ereditare da un mixin non è considerato una forma di specializzazione ma è davvero un modo per farlo
raccogliere funzionalità.

Diamo una rapida occhiata a tutti e quattro questi casi e ai rispettivi mixin.

Pcap Tracciato Dispositivo Helpers
L'obiettivo di questi helper è semplificare l'aggiunta di una funzione di traccia pcap coerente a un file
NS-3 dispositivo. Vogliamo che tutti i vari tipi di tracciamento di pcap funzionino allo stesso modo
tutti i dispositivi, quindi i metodi di questi helper vengono ereditati dagli helper dei dispositivi. Guarda
at src/network/helper/trace-helper.h se vuoi seguire la discussione mentre guardi
codice reale.

La classe PcapHelperForDevice è un mixin fornisce la funzionalità di alto livello per l'utilizzo
traccia di pcap in un NS-3 dispositivo. Ogni dispositivo deve implementare un unico metodo virtuale
ereditato da questa classe.:

virtual void EnablePcapInternal (std::string prefix, Ptr nd, bool promiscuo) = 0;

La firma di questo metodo riflette la visione incentrata sul dispositivo della situazione in questo momento
livello. Tutti i metodi pubblici ereditati dalla classe PcapUserHelperForDevice ridurre a
chiamando questo singolo metodo di implementazione dipendente dal dispositivo. Ad esempio, il livello più basso
metodo pcap,:

void EnablePcap (std::string prefix, Ptr nd, bool promiscuus = false, bool esplicitoFilename = false);

chiamerà l'implementazione del dispositivo di AbilitaPcapInternal direttamente. Tutti gli altri pcap pubblici
i metodi di tracciamento si basano su questa implementazione per fornire un livello utente aggiuntivo
funzionalità. Ciò che questo significa per l'utente è che tutti gli assistenti del dispositivo nel sistema lo faranno
avere a disposizione tutti i metodi di traccia di pcap; e questi metodi funzioneranno tutti allo stesso modo
attraverso i dispositivi se il dispositivo implementa AbilitaPcapInternal correttamente.

Pcap Tracciato Dispositivo Aiutante Metodi
void EnablePcap (std::string prefix, Ptr nd,
bool promiscuo = falso, bool esplicitoFilename = falso);
void EnablePcap (std::string prefisso, std::string ndName,
bool promiscuo = falso, bool esplicitoFilename = falso);
void EnablePcap (std::string prefisso, NetDeviceContainer d,
bool promiscuo = falso);
void EnablePcap (std::string prefisso, NodeContainer n,
bool promiscuo = falso);
void EnablePcap (std::string prefix, uint32_t nodeid, uint32_t deviceid,
bool promiscuo = falso);
void EnablePcapAll (std::string prefisso, bool promiscuo = false);

In ciascuno dei metodi mostrati sopra, c'è un parametro predefinito chiamato promiscuo che
il valore predefinito è falso. Questo parametro indica che la traccia non deve essere raccolta
modalità promiscua. Se vuoi che le tue tracce includano tutto il traffico visto dal dispositivo
(e se il dispositivo supporta una modalità promiscua) aggiungi semplicemente un parametro true a uno qualsiasi dei file
chiamate sopra. Per esempio,:

pt nd;
...
helper.EnablePcap ("prefisso", nd, true);

abiliterà le acquisizioni in modalità promiscua su NetDevice specificato da nd.

I primi due metodi includono anche un parametro predefinito chiamato nomefile esplicito quella volontà
essere discusso di seguito.

Sei incoraggiato a leggere il Doxygen per le lezioni PcapHelperForDevice per trovare i dettagli
di questi metodi; ma per riassumere...

È possibile abilitare la traccia pcap su una particolare coppia nodo/dispositivo di rete fornendo a
pt a un AbilitaPcap metodo. Il Ptr è implicito dal dispositivo di rete
deve appartenere esattamente a uno Nodo. Per esempio,:

pt nd;
...
helper.EnablePcap ("prefisso", nd);

È possibile abilitare la traccia pcap su una particolare coppia nodo/dispositivo di rete fornendo a
std::stringa che rappresenta una stringa di servizio del nome oggetto in un AbilitaPcap metodo. Il
pt viene cercato dalla stringa del nome. Ancora una volta, il è implicito poiché il
denominato net device deve appartenere esattamente a uno Nodo. Per esempio,:

Nomi::Add ("server" ...);
Nomi::Add ("server/eth0" ...);
...
helper.EnablePcap ("prefisso", "server/ath0");

È possibile abilitare la traccia pcap su una raccolta di coppie nodo/rete-dispositivo fornendo a
NetDeviceContenitore. Per ciascuno NetDevice nel contenitore viene verificato il tipo. Per ciascuno
dispositivo del tipo corretto (lo stesso tipo gestito dall'helper del dispositivo), la traccia è
abilitato. Ancora una volta, il è implicito poiché il dispositivo di rete trovato deve appartenere esattamente a
prima Nodo. Per esempio,:

NetDeviceContainer d = ...;
...
helper.EnablePcap ("prefisso", d);

È possibile abilitare la traccia pcap su una raccolta di coppie nodo/rete-dispositivo fornendo a
NodoContenitore. Per ciascuno Nodo nel NodoContenitore è allegato NetDevice sono iterati.
Per ciascun NetDevice collegato a ciascun nodo nel contenitore, il tipo di quel dispositivo è
controllato. Per ogni dispositivo del tipo corretto (lo stesso tipo gestito dal dispositivo
helper), la traccia è abilitata.:

NodoContenitore n;
...
helper.EnablePcap ("prefisso", n);

È possibile abilitare il tracciamento di pcap sulla base dell'ID del nodo e dell'ID del dispositivo, nonché con esplicito
Ptr. Ogni Nodo nel sistema ha un ID nodo intero e ogni dispositivo connesso a un nodo
ha un ID dispositivo intero.:

helper.EnablePcap ("prefisso", 21, 1);

Infine, puoi abilitare il tracciamento di pcap per tutti i dispositivi nel sistema, con lo stesso tipo di
quella gestita dall'assistente dispositivo.:

helper.EnablePcapAll ("prefisso");

Pcap Tracciato Dispositivo Aiutante Nome del file Selezione
Implicita nelle descrizioni del metodo di cui sopra è la costruzione di un nome file completo da parte di
il metodo di attuazione. Per convenzione, pcap traccia nel file NS-3 sistema sono della forma
- id>- id>.pcap

Come accennato in precedenza, ogni nodo nel sistema avrà un ID nodo assegnato dal sistema; e
ogni dispositivo avrà un indice di interfaccia (chiamato anche ID dispositivo) relativo al suo nodo.
Per impostazione predefinita, quindi, un file di traccia pcap creato come risultato dell'abilitazione della traccia sul primo
dispositivo del nodo 21 utilizzando il prefisso "prefisso" sarebbe prefisso-21-1.pcap.

Puoi sempre usare il file NS-3 servizio nome oggetto per renderlo più chiaro. Ad esempio, se
si utilizza il servizio nome oggetto per assegnare il nome "server" al nodo 21, il risultante pcap
il nome del file di traccia diventerà automaticamente, prefisso-server-1.pcap e se assegni anche il
nome "eth0" al dispositivo, il nome del file pcap lo raccoglierà automaticamente e sarà
detto prefisso-server-eth0.pcap.

Infine, due dei metodi mostrati sopra:

void EnablePcap (std::string prefix, Ptr nd, bool promiscuus = false, bool esplicitoFilename = false);
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool esplicitoFilename = false);

avere un parametro predefinito chiamato nomefile esplicito. Quando è impostato su true, questo parametro
disabilita il meccanismo di completamento automatico del nome file e consente di creare un file esplicito
nome del file. Questa opzione è disponibile solo nei metodi che abilitano la traccia pcap su a
unico dispositivo.

Ad esempio, per fare in modo che un helper del dispositivo crei un singolo pcap promiscuo
acquisisci file con un nome specifico (il mio-pcap-file.pcap) su un determinato dispositivo, si potrebbe:

pt nd;
...
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);

Il primo vero parametro abilita tracce in modalità promiscua e il secondo dice all'helper
interpretare il prefisso parametro come un nome file completo.

Ascii Tracciato Dispositivo Helpers
Il comportamento dell'helper di traccia ascii mixin è sostanzialmente simile alla versione pcap.
Dare un'occhiata a src/network/helper/trace-helper.h se vuoi seguire la discussione
mentre guardi il codice reale.

La classe AsciiTraceHelperForDevice aggiunge la funzionalità di alto livello per l'utilizzo di ascii
traccia a una classe di supporto del dispositivo. Come nel caso di pcap, ogni dispositivo deve implementare a
singolo metodo virtuale ereditato dalla traccia ascii mixin.:

virtual void EnableAsciiInternal (Ptr stream, std::prefisso di stringa, Ptr nd) = 0;

La firma di questo metodo riflette la visione incentrata sul dispositivo della situazione in questo momento
livello; e anche il fatto che l'helper possa scrivere su un flusso di output condiviso. Tutto di
i metodi pubblici relativi a ascii-trace ereditati dalla classe AsciiTraceHelperForDevice
ridurre a chiamare questo singolo metodo di implementazione dipendente dal dispositivo. Ad esempio, il
metodi di traccia ascii di livello più basso:

void EnableAscii (std::string prefix, Ptr nd);
void EnableAscii (Ptr ruscello, pt nd);

chiamerà l'implementazione del dispositivo di AbilitaAsciiInternal direttamente, fornendo a
prefisso o flusso valido. Tutti gli altri metodi di tracciatura ascii pubblici si baseranno su questi
funzioni di basso livello per fornire funzionalità aggiuntive a livello di utente. Cosa significa questo per il
utente è che tutti gli assistenti del dispositivo nel sistema avranno tutti i metodi di traccia ascii
a disposizione; e questi metodi funzioneranno tutti allo stesso modo su tutti i dispositivi se i dispositivi
realizzare Abilitazione interna correttamente.

Ascii Tracciato Dispositivo Aiutante Metodi
void EnableAscii (std::string prefix, Ptr nd);
void EnableAscii (Ptr ruscello, pt nd);

void EnableAscii (std::string prefisso, std::string ndName);
void EnableAscii (Ptr stream, std::string ndName);

void EnableAscii (std::string prefisso, NetDeviceContainer d);
void EnableAscii (Ptr flusso, NetDeviceContainer d);

void EnableAscii (std::string prefisso, NodeContainer n);
void EnableAscii (Ptr flusso, NodeContainer n);

void EnableAscii (std::string prefix, uint32_t nodeid, uint32_t deviceid);
void EnableAscii (Ptr stream, uint32_t nodeid, uint32_t deviceid);

void EnableAsciiAll (std::string prefisso);
void EnableAsciiAll (Ptr flusso);

Sei incoraggiato a leggere il Doxygen per le lezioni TraceHelperForDevice per trovare l'
dettagli di questi metodi; ma per riassumere...

Ci sono il doppio dei metodi disponibili per la traccia ascii rispetto a pcap
tracciare. Questo perché, oltre al modello in stile pcap dove traccia da ciascuno
la coppia univoca di nodo/dispositivo viene scritta in un file univoco, supportiamo un modello in cui trace
le informazioni per molte coppie nodo/dispositivo vengono scritte in un file comune. Ciò significa che il
- - il meccanismo di generazione del nome file è sostituito da un meccanismo a
fare riferimento a un file comune; e il numero di metodi API è raddoppiato per consentire tutto
combinazioni.

Proprio come nella traccia pcap, puoi abilitare la traccia ascii su una particolare coppia nodo/net-dispositivo
fornendo un pt a un Abilita Ascii metodo. Il Ptr è implicito poiché
il dispositivo di rete deve appartenere esattamente a uno Nodo. Per esempio,:

pt nd;
...
helper.EnableAscii ("prefisso", nd);

In questo caso, nessun contesto di traccia viene scritto nel file di traccia ascii poiché lo sarebbe
ridondante. Il sistema selezionerà il nome del file da creare utilizzando le stesse regole di
descritto nella sezione pcap, tranne per il fatto che il file avrà il suffisso ".tr" invece di
".pcap".

Se si desidera abilitare la traccia ascii su più di un dispositivo di rete e inviare tutte le tracce
in un singolo file, puoi farlo anche usando un oggetto per fare riferimento a un singolo file:

pt nd1;
pt nd2;
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAscii (flusso, nd1);
helper.EnableAscii (flusso, nd2);

In questo caso, i contesti di traccia vengono scritti nel file di traccia ascii poiché sono obbligatori
per disambiguare le tracce dai due dispositivi. Si noti che poiché l'utente è completamente
specificando il nome del file, la stringa dovrebbe includere ".tr" per coerenza.

È possibile abilitare la traccia ascii su una particolare coppia nodo/dispositivo di rete fornendo a
std::stringa che rappresenta una stringa di servizio del nome oggetto in un AbilitaPcap metodo. Il
pt viene cercato dalla stringa del nome. Ancora una volta, il è implicito poiché il
denominato net device deve appartenere esattamente a uno Nodo. Per esempio,:

Nomi::Add ("client" ...);
Nomi::Add ("client/eth0" ...);
Nomi::Add ("server" ...);
Nomi::Add ("server/eth0" ...);
...
helper.EnableAscii ("prefisso", "client/eth0");
helper.EnableAscii ("prefisso", "server/eth0");

Ciò risulterebbe in due file denominati prefisso-client-eth0.tr e prefisso-server-eth0.tr con
tracce per ciascun dispositivo nel rispettivo file di traccia. Dal momento che tutti gli EnableAscii
le funzioni sono sovraccaricate per prendere un wrapper di flusso, puoi usare anche quel modulo:

Nomi::Add ("client" ...);
Nomi::Add ("client/eth0" ...);
Nomi::Add ("server" ...);
Nomi::Add ("server/eth0" ...);
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAscii (stream, "client/eth0");
helper.EnableAscii (stream, "server/eth0");

Ciò risulterebbe in un unico file di traccia chiamato nome-file-traccia.tr che contiene tutto
gli eventi di traccia per entrambi i dispositivi. Gli eventi sarebbero disambiguati dal contesto di traccia
stringhe.

È possibile abilitare la traccia ascii su una raccolta di coppie nodo/rete-dispositivo fornendo a
NetDeviceContenitore. Per ciascuno NetDevice nel contenitore viene verificato il tipo. Per ciascuno
dispositivo del tipo corretto (lo stesso tipo gestito dall'helper del dispositivo), la traccia è
abilitato. Ancora una volta, il è implicito poiché il dispositivo di rete trovato deve appartenere esattamente a
prima Nodo. Per esempio,:

NetDeviceContainer d = ...;
...
helper.EnableAscii ("prefisso", d);

Ciò comporterebbe la creazione di un numero di file di traccia ascii, ognuno dei quali segue
il - - convenzione .tr. Combinando tutte le tracce in a
singolo file viene eseguito in modo simile agli esempi precedenti:

NetDeviceContainer d = ...;
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAscii (flusso, d);

È possibile abilitare la traccia ascii su una raccolta di coppie nodo/rete-dispositivo fornendo a
NodoContenitore. Per ciascuno Nodo nel NodoContenitore è allegato NetDevice sono iterati.
Per ciascun NetDevice collegato a ciascun nodo nel contenitore, il tipo di quel dispositivo è
controllato. Per ogni dispositivo del tipo corretto (lo stesso tipo gestito dal dispositivo
helper), la traccia è abilitata.:

NodoContenitore n;
...
helper.EnableAscii ("prefisso", n);

Ciò comporterebbe la creazione di un numero di file di traccia ascii, ognuno dei quali segue
il - - convenzione .tr. Combinando tutte le tracce in a
singolo file viene eseguito in modo simile agli esempi precedenti:

È possibile abilitare il tracciamento di pcap sulla base dell'ID del nodo e dell'ID del dispositivo, nonché con esplicito
Ptr. Ogni Nodo nel sistema ha un ID nodo intero e ogni dispositivo connesso a un nodo
ha un ID dispositivo intero.:

helper.EnableAscii ("prefisso", 21, 1);

Naturalmente, le tracce possono essere combinate in un unico file come mostrato sopra.

Infine, puoi abilitare il tracciamento di pcap per tutti i dispositivi nel sistema, con lo stesso tipo di
quella gestita dall'assistente dispositivo.:

helper.EnableAsciiAll ("prefisso");

Ciò comporterebbe la creazione di un numero di file di traccia ascii, uno per ogni dispositivo in
il sistema del tipo gestito dall'helper. Tutti questi file seguiranno il
- - convenzione .tr. Combinando tutte le tracce in un unico
il file viene eseguito in modo simile agli esempi precedenti.

Ascii Tracciato Dispositivo Aiutante Nome del file Selezione
Implicita nelle descrizioni del metodo in stile prefisso di cui sopra è la costruzione del completo
nomi di file dal metodo di implementazione. Per convenzione, ascii tracce nel NS-3 il sistema sono
del modulo - id>- id>.tr.

Come accennato in precedenza, ogni nodo nel sistema avrà un ID nodo assegnato dal sistema; e
ogni dispositivo avrà un indice di interfaccia (chiamato anche ID dispositivo) relativo al suo nodo.
Per impostazione predefinita, quindi, un file di traccia ascii creato a seguito dell'abilitazione del tracciamento sul primo
dispositivo del nodo 21, utilizzando il prefisso "prefisso", sarebbe prefisso-21-1.tr.

Puoi sempre usare il file NS-3 servizio nome oggetto per renderlo più chiaro. Ad esempio, se
si utilizza il servizio nome oggetto per assegnare il nome "server" al nodo 21, il risultato
il nome del file di traccia ascii diventerà automaticamente, prefisso-server-1.tr e se assegni anche tu
il nome "eth0" al dispositivo, il nome del file di traccia ascii lo rileverà automaticamente
ed essere chiamato prefisso-server-eth0.tr.

Pcap Tracciato Protocollo Helpers
L'obiettivo di questi mixin è semplificare l'aggiunta di una funzione di traccia pcap coerente a
protocolli. Vogliamo che tutti i vari tipi di tracciamento di pcap funzionino allo stesso modo su tutti
protocolli, quindi i metodi di questi helper vengono ereditati dagli stack helper. Date un'occhiata al
src/network/helper/trace-helper.h se vuoi seguire la discussione mentre guardi
codice reale.

In questa sezione illustreremo i metodi applicati al protocollo Ipv4. A
specificare le tracce in protocolli simili, basta sostituire il tipo appropriato. Per esempio,
Usa un pt invece di a pt e chiama AbilitaPcapIpv6 invece di AbilitaPcapIpv4.

La classe PcapHelperForIpv4 fornisce la funzionalità di alto livello per l'utilizzo della traccia pcap
nel Ipv4 protocollo. Ciascun helper di protocollo che abilita questi metodi deve implementarne uno
metodo virtuale ereditato da questa classe. Ci sarà un'implementazione separata per
Ipv6, ad esempio, ma l'unica differenza sarà nei nomi e nelle firme dei metodi.
Sono necessari nomi di metodi diversi per disambiguare la classe Ipv4 da Ipv6 che sono entrambi
derivato da classe Oggettoe metodi che condividono la stessa firma.:

virtual void EnablePcapIpv4Internal (std::string prefix, Ptr ipv4, interfaccia uint4_t) = 32;

La firma di questo metodo riflette il protocollo e la vista incentrata sull'interfaccia di
situazione a questo livello. Tutti i metodi pubblici ereditati dalla classe PcapHelperForIpv4
ridurre a chiamare questo singolo metodo di implementazione dipendente dal dispositivo. Ad esempio, il
metodo pcap di livello più basso:

void EnablePcapIpv4 (std::string prefix, Ptr ipv4, interfaccia uint4_t);

chiamerà l'implementazione del dispositivo di AbilitaPcapIpv4Internal direttamente. Tutti gli altri pubblici
I metodi di tracciamento di pcap si basano su questa implementazione per fornire un livello utente aggiuntivo
funzionalità. Ciò che questo significa per l'utente è che tutti gli helper del protocollo nel sistema lo faranno
avere a disposizione tutti i metodi di traccia di pcap; e questi metodi funzioneranno tutti allo stesso modo
attraverso i protocolli se l'helper implementa AbilitaPcapIpv4Internal correttamente.

Pcap Tracciato Protocollo Aiutante Metodi
Questi metodi sono progettati per essere in corrispondenza uno a uno con il Nodo- E
NetDevice- versioni centriche delle versioni del dispositivo. Invece di Nodo e NetDevice (coppia),
vincoli, utilizziamo vincoli di protocollo e di interfaccia.

Nota che, proprio come nella versione del dispositivo, ci sono sei metodi:

void EnablePcapIpv4 (std::string prefix, Ptr ipv4, interfaccia uint4_t);
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, interfaccia uint32_t);
void EnablePcapIpv4 (std::prefisso di stringa, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std::prefisso di stringa, NodeContainer n);
void EnablePcapIpv4 (std::prefisso stringa, uint32_t nodeid, interfaccia uint32_t);
void EnablePcapIpv4All (std::prefisso stringa);

Sei incoraggiato a leggere il Doxygen per le lezioni PcapHelperForIpv4 per trovare i dettagli
di questi metodi; ma per riassumere...

È possibile abilitare la traccia pcap su una particolare coppia protocollo/interfaccia fornendo a
pt e interfaccia a un AbilitaPcap metodo. Per esempio,:

pt ipv4 = nodo->GetObject ();
...
helper.EnablePcapIpv4 ("prefisso", ipv4, 0);

È possibile abilitare la traccia pcap su una particolare coppia nodo/dispositivo di rete fornendo a
std::stringa che rappresenta una stringa di servizio del nome oggetto in un AbilitaPcap metodo. Il
pt viene cercato dalla stringa del nome. Per esempio,:

Nomi::Add ("serverIPv4" ...);
...
helper.EnablePcapIpv4 ("prefisso", "serverIpv4", 1);

È possibile abilitare la traccia pcap su una raccolta di coppie protocollo/interfaccia fornendo un file
Contenitore di interfaccia IPv4. Per ciascuno Ipv4 / coppia di interfaccia nel contenitore il tipo di protocollo
è controllato. Per ogni protocollo del tipo proprio (lo stesso tipo gestito dal
device helper), la traccia è abilitata per l'interfaccia corrispondente. Per esempio,:

nodi NodeContainer;
...
Dispositivi NetDeviceContainer = deviceHelper.Install (nodi);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfacce Ipv4InterfaceContainer = ipv4.Assign (dispositivi);
...
helper.EnablePcapIpv4 ("prefisso", interfacce);

È possibile abilitare la traccia pcap su una raccolta di coppie protocollo/interfaccia fornendo a
NodoContenitore. Per ciascuno Nodo nel NodoContenitore viene trovato il protocollo appropriato. Per
ogni protocollo, le sue interfacce sono enumerate e la traccia è abilitata sul risultante
coppie. Per esempio,:

NodoContenitore n;
...
helper.EnablePcapIpv4 ("prefisso", n);

È anche possibile abilitare il tracciamento di pcap sulla base dell'ID del nodo e dell'interfaccia. In questo caso,
il node-id viene tradotto in a Ptr e il protocollo appropriato viene cercato in
nodo. Il protocollo e l'interfaccia risultanti vengono utilizzati per specificare la traccia risultante
fonte.:

helper.EnablePcapIpv4 ("prefisso", 21, 1);

Infine, puoi abilitare il tracciamento di pcap per tutte le interfacce del sistema, con le associate
protocollo essendo dello stesso tipo di quello gestito dall'helper del dispositivo.:

helper.EnablePcapIpv4All ("prefisso");

Pcap Tracciato Protocollo Aiutante Nome del file Selezione
Implicita in tutte le descrizioni del metodo di cui sopra è la costruzione del completo
nomi di file dal metodo di implementazione. Per convenzione, le tracce pcap prese per i dispositivi in
, il NS-3 sistema sono della forma - id>- id>.pcap. In caso di
tracce di protocollo, esiste una corrispondenza biunivoca tra protocolli e Nodes. Questo è
perché protocollo Oggetti sono aggregati a Nodo Oggetti. Poiché non esiste un protocollo globale
id nel sistema, utilizziamo l'id del nodo corrispondente nella denominazione dei file. Quindi c'è un
possibilità di collisioni di nomi di file nei nomi di file di traccia scelti automaticamente. Per questo
motivo, la convenzione del nome file è stata modificata per le tracce del protocollo.

Come accennato in precedenza, ogni nodo nel sistema avrà un ID nodo assegnato dal sistema.
Poiché esiste una corrispondenza uno a uno tra le istanze del protocollo e le istanze del nodo
usiamo l'id del nodo. Ogni interfaccia ha un ID interfaccia relativo al proprio protocollo. Noi usiamo
la convenzione" -n -io .pcap" per la denominazione dei file di traccia in
aiutanti del protocollo.

Pertanto, per impostazione predefinita, viene creato un file di traccia pcap come risultato dell'abilitazione della traccia
l'interfaccia 1 del protocollo Ipv4 del nodo 21 che utilizza il prefisso "prefisso" sarebbe
"prefisso-n21-i1.pcap".

Puoi sempre usare il file NS-3 servizio nome oggetto per renderlo più chiaro. Ad esempio, se
si utilizza il servizio nome oggetto per assegnare il nome "serverIpv4" al Ptr sul nodo
21, il nome del file di traccia pcap risultante diventerà automaticamente,
"prefisso-nserverIpv4-i1.pcap".

Ascii Tracciato Protocollo Helpers
Il comportamento degli helper di traccia ascii è sostanzialmente simile al caso pcap. Prendi un
guardare src/network/helper/trace-helper.h se vuoi seguire la discussione mentre
guardando il codice reale.

In questa sezione illustreremo i metodi applicati al protocollo Ipv4. A
specificare le tracce in protocolli simili, basta sostituire il tipo appropriato. Per esempio,
Usa un pt invece di a pt e chiama Abilita AsciiIpv6 invece di
Abilita AsciiIpv4.

La classe AsciiTraceHelperForIpv4 aggiunge la funzionalità di alto livello per l'utilizzo di ascii
tracciamento a un assistente di protocollo. Ogni protocollo che abilita questi metodi deve implementare a
singolo metodo virtuale ereditato da questa classe.:

virtual void EnableAsciiIpv4Internal (Ptr stream, std::prefisso di stringa,
pt ipv4, interfaccia uint4_t) = 32;

La firma di questo metodo riflette la vista incentrata sul protocollo e sull'interfaccia di
situazione a questo livello; e anche il fatto che l'assistente possa scrivere a una persona condivisa
flusso di uscita. Tutti i metodi pubblici ereditati dalla classe
PcapAndAsciiTraceHelperForIpv4 ridurre a chiamare questo singolo dispositivo dipendente
metodo di attuazione. Ad esempio, i metodi di traccia ascii di livello più basso:

void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interfaccia uint4_t);
void EnableAsciiIpv4 (Ptr ruscello, pt ipv4, interfaccia uint4_t);

chiamerà l'implementazione del dispositivo di AbilitaAsciiIpv4Internal direttamente, fornendo entrambi
il prefisso o il flusso. Tutti gli altri metodi di tracciatura ascii pubblici si baseranno su questi
funzioni di basso livello per fornire funzionalità aggiuntive a livello di utente. Cosa significa questo per il
utente è che tutti gli assistenti del dispositivo nel sistema avranno tutti i metodi di traccia ascii
a disposizione; e questi metodi funzioneranno tutti allo stesso modo su tutti i protocolli se il
implementano i protocolli Abilitazione interna correttamente.

Ascii Tracciato Dispositivo Aiutante Metodi
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interfaccia uint4_t);
void EnableAsciiIpv4 (Ptr ruscello, pt ipv4, interfaccia uint4_t);

void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, interfaccia uint32_t);
void EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, interfaccia uint32_t);

void EnableAsciiIpv4 (std::prefisso di stringa, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (Ptr flusso, Ipv4InterfaceContainer c);

void EnableAsciiIpv4 (std::prefisso di stringa, NodeContainer n);
void EnableAsciiIpv4 (Ptr flusso, NodeContainer n);

void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiIpv4 (Ptr stream, uint32_t nodeid, interfaccia uint32_t);

void EnableAsciiIpv4All (std::prefisso di stringa);
void EnableAsciiIpv4All (Ptr flusso);

Sei incoraggiato a leggere il Doxygen per le lezioni PcapAndAsciiHelperForIpv4 per trovare l'
dettagli di questi metodi; ma per riassumere...

Ci sono il doppio dei metodi disponibili per la traccia ascii rispetto a pcap
tracciare. Questo perché, oltre al modello in stile pcap dove traccia da ciascuno
la coppia protocollo/interfaccia univoca viene scritta in un file univoco, supportiamo un modello in cui
le informazioni di traccia per molte coppie protocollo/interfaccia vengono scritte in un file comune. Questo
significa che il -n - il meccanismo di generazione del nome del file viene sostituito
da un meccanismo per fare riferimento a un file comune; e il numero di metodi API viene raddoppiato
consenti tutte le combinazioni.

Proprio come nella traccia pcap, puoi abilitare la traccia ascii su un protocollo/interfaccia particolare
coppia fornendo a pt e interfaccia a un Abilita Ascii metodo. Per esempio,:

pt ipv4;
...
helper.EnableAsciiIpv4 ("prefisso", ipv4, 1);

In questo caso, nessun contesto di traccia viene scritto nel file di traccia ascii poiché lo sarebbe
ridondante. Il sistema selezionerà il nome del file da creare utilizzando le stesse regole di
descritto nella sezione pcap, tranne per il fatto che il file avrà il suffisso ".tr" invece di
".pcap".

Se si desidera abilitare la traccia ascii su più di un'interfaccia e inviare tutte le tracce a
un singolo file, puoi farlo anche usando un oggetto per fare riferimento a un singolo file. Noi
hanno già qualcosa di simile a questo nell'esempio "cwnd" sopra:

pt protocollo4 = nodo1->GetObject ();
pt protocollo4 = nodo2->GetObject ();
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAsciiIpv4 (flusso, protocollo1, 1);
helper.EnableAsciiIpv4 (flusso, protocollo2, 1);

In questo caso, i contesti di traccia vengono scritti nel file di traccia ascii poiché sono obbligatori
per disambiguare le tracce dalle due interfacce. Si noti che poiché l'utente è completamente
specificando il nome del file, la stringa dovrebbe includere ".tr" per coerenza.

È possibile abilitare la traccia ascii su un protocollo particolare fornendo a std::stringa
che rappresenta una stringa di servizio del nome oggetto in un AbilitaPcap metodo. Il pt is
alzato lo sguardo dalla stringa del nome. Il nei nomi dei file risultanti è implicito poiché
esiste una corrispondenza uno-a-uno tra le istanze del protocollo e i nodi, ad esempio:

Nomi::Add ("node1Ipv4" ...);
Nomi::Add ("node2Ipv4" ...);
...
helper.EnableAsciiIpv4 ("prefisso", "node1Ipv4", 1);
helper.EnableAsciiIpv4 ("prefisso", "node2Ipv4", 1);

Ciò risulterebbe in due file denominati "prefix-nnode1Ipv4-i1.tr" e
"prefix-nnode2Ipv4-i1.tr" con tracce per ciascuna interfaccia nel rispettivo file di traccia.
Poiché tutte le funzioni EnableAscii sono sovraccaricate per accettare un wrapper di flusso, è possibile
usa anche quel modulo:

Nomi::Add ("node1Ipv4" ...);
Nomi::Add ("node2Ipv4" ...);
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAsciiIpv4 (flusso, "node1Ipv4", 1);
helper.EnableAsciiIpv4 (flusso, "node2Ipv4", 1);

Ciò risulterebbe in un unico file di traccia chiamato "trace-file-name.tr" che contiene tutto
gli eventi di traccia per entrambe le interfacce. Gli eventi sarebbero disambiguati dal contesto di traccia
stringhe.

È possibile abilitare la traccia ascii su una raccolta di coppie protocollo/interfaccia fornendo un file
Contenitore di interfaccia IPv4. Per ogni protocollo del tipo corretto (lo stesso tipo gestito
dall'assistente del dispositivo), il tracciamento è abilitato per l'interfaccia corrispondente. Ancora una volta, il
è implicito poiché esiste una corrispondenza uno-a-uno tra ciascun protocollo e
il suo nodo. Per esempio,:

nodi NodeContainer;
...
Dispositivi NetDeviceContainer = deviceHelper.Install (nodi);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfacce Ipv4InterfaceContainer = ipv4.Assign (dispositivi);
...
...
helper.EnableAsciiIpv4 ("prefisso", interfacce);

Ciò comporterebbe la creazione di un numero di file di traccia ascii, ognuno dei quali segue
il -n -io convenzione .tr. Combinando tutte le tracce in a
singolo file viene eseguito in modo simile agli esempi precedenti:

nodi NodeContainer;
...
Dispositivi NetDeviceContainer = deviceHelper.Install (nodi);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfacce Ipv4InterfaceContainer = ipv4.Assign (dispositivi);
...
pt stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAsciiIpv4 (stream, interfacce);

È possibile abilitare la traccia ascii su una raccolta di coppie protocollo/interfaccia fornendo a
NodoContenitore. Per ciascuno Nodo nel NodoContenitore viene trovato il protocollo appropriato. Per
ogni protocollo, le sue interfacce sono enumerate e la traccia è abilitata sul risultante
coppie. Per esempio,:

NodoContenitore n;
...
helper.EnableAsciiIpv4 ("prefisso", n);

Ciò comporterebbe la creazione di un numero di file di traccia ascii, ognuno dei quali segue
il - - convenzione .tr. Combinando tutte le tracce in a
singolo file viene eseguito in modo simile agli esempi precedenti:

È possibile abilitare la traccia pcap anche sulla base dell'ID del nodo e dell'ID del dispositivo. In questo caso,
il node-id viene tradotto in a Ptr e il protocollo appropriato viene cercato in
nodo. Il protocollo e l'interfaccia risultanti vengono utilizzati per specificare la traccia risultante
fonte.:

helper.EnableAsciiIpv4 ("prefisso", 21, 1);

Naturalmente, le tracce possono essere combinate in un unico file come mostrato sopra.

Infine, puoi abilitare il tracciamento ascii per tutte le interfacce del sistema, con associate
protocollo essendo dello stesso tipo di quello gestito dall'helper del dispositivo.:

helper.EnableAsciiIpv4All ("prefisso");

Ciò comporterebbe la creazione di un numero di file di traccia ascii, uno per ogni interfaccia
nel sistema relativo ad un protocollo del tipo gestito dall'helper. Tutti questi file
seguirà il -n -io
in un unico file si ottiene in modo simile agli esempi precedenti.

Ascii Tracciato Dispositivo Aiutante Nome del file Selezione
Implicita nelle descrizioni del metodo in stile prefisso di cui sopra è la costruzione del completo
nomi di file dal metodo di implementazione. Per convenzione, ascii tracce nel NS-3 il sistema sono
della forma" - - .tr."

Come accennato in precedenza, ogni nodo nel sistema avrà un ID nodo assegnato dal sistema.
Poiché esiste una corrispondenza uno-a-uno tra protocolli e nodi, utilizziamo node-id
per identificare l'identità del protocollo. Ogni interfaccia su un determinato protocollo avrà un
indice di interfaccia (chiamato anche semplicemente interfaccia) relativo al suo protocollo. Per impostazione predefinita,
quindi, un file di traccia ascii creato come risultato dell'abilitazione della traccia sul primo dispositivo di
il nodo 21, utilizzando il prefisso "prefisso", sarebbe "prefisso-n21-i1.tr". Usa il prefisso per
disambiguare più protocolli per nodo.

Puoi sempre usare il file NS-3 servizio nome oggetto per renderlo più chiaro. Ad esempio, se
si utilizza il servizio nome oggetto per assegnare il nome "serverIpv4" al protocollo sul nodo
21 e specificare anche l'interfaccia uno, il nome del file di traccia ascii risultante verrà automaticamente
diventa "prefix-nserverIpv4-1.tr".

Tracciato implementazione dettagli
Dati Collezione
Questo capitolo descrive il ns-3 Data Collection Framework (DCF), che fornisce
capacità di ottenere dati generati da modelli nel simulatore, per eseguire on-line
riduzione ed elaborazione dei dati, e per marshalling dati grezzi o trasformati in vari output
formati.

Il framework attualmente supporta le esecuzioni ns-3 standalone che non si basano su alcun elemento esterno
controllo dell'esecuzione del programma. Gli oggetti forniti dalla DCF possono essere agganciati a NS-3 tracciare
fonti per consentire l'elaborazione dei dati.

Il codice sorgente per le classi risiede nella directory src/statistiche.

Questo capitolo è organizzato come segue. Innanzitutto, una panoramica dell'architettura è
presentata. Successivamente, vengono presentati gli helper per queste classi; questo trattamento iniziale
dovrebbe consentire l'uso di base del quadro di raccolta dati per molti casi d'uso. Utenti che
desiderano produrre output al di fuori dello scopo degli attuali aiutanti, o che desiderano creare
i propri oggetti di raccolta dati, dovrebbe leggere il resto del capitolo, che va
in dettaglio su tutti i tipi di oggetti DCF di base e fornisce la codifica di basso livello
esempi.

Progettazione
Il DCF si compone di tre classi fondamentali:

· Campione è un meccanismo per strumentare e controllare l'output dei dati di simulazione che è
utilizzato per monitorare eventi interessanti. Produce output sotto forma di uno o più NS-3
fonti di traccia. Gli oggetti sonda sono collegati a una o più tracce lavelli (chiamato
Collezionisti), che elaborano i campioni in linea e li preparano per l'output.

· Collettore consuma i dati generati da uno o più oggetti Probe. si esibisce
trasformazioni sui dati, come la normalizzazione, la riduzione e il calcolo di
statistiche di base. Gli oggetti Collector non producono dati che vengono direttamente emessi dal
ns-3 corsa; invece, emettono i dati a valle su un altro tipo di oggetto, chiamato
Aggregator, che svolge tale funzione. In genere, i raccoglitori emettono i loro dati in
anche la forma di fonti di tracce, che consentono ai collezionisti di essere concatenati in serie.

· Aggregator è il punto finale dei dati raccolti da una rete di Probe e Collector.
La responsabilità principale dell'Aggregatore è quella di organizzare i dati e il loro corrispondente
metadati, in diversi formati di output come file di testo normale, file di fogli di calcolo o
banche dati.

Tutte e tre queste classi offrono la capacità di accendersi o spegnersi in modo dinamico
durante una simulazione.

Qualsiasi standalone NS-3 l'esecuzione di simulazione che utilizza il DCF in genere ne creerà almeno uno
istanza di ciascuna delle tre classi precedenti.
[immagine] Panoramica del quadro di raccolta dati.UNINDENT

Il flusso complessivo del trattamento dei dati è rappresentato in Dati Collezione Contesto panoramica.
Sul lato sinistro, una corsa NS-3 simulazione è rappresentata. Nel corso dell'esecuzione del
simulazione, i dati sono resi disponibili dai modelli tramite fonti di traccia o tramite altri mezzi.
Il diagramma mostra che le sonde possono essere collegate a queste sorgenti di traccia per ricevere dati
in modo asincrono o le sonde possono eseguire il polling dei dati. I dati vengono quindi passati a un oggetto di raccolta
che trasforma i dati. Infine, un aggregatore può essere collegato alle uscite del
collector, per generare grafici, file o database.
[immagine] Data Collection Framework aggregation.UNINDENT

Una variazione alla figura sopra è fornita in Dati Collezione Contesto aggregazione.
Questa seconda figura illustra che gli oggetti DCF possono essere concatenati in un modo
che gli oggetti a valle ricevano input da più oggetti a monte. La figura
mostra concettualmente che più sonde possono generare un output che viene alimentato in un singolo
collettore; ad esempio, un collettore che emette un rapporto di due contatori sarebbe
in genere acquisiscono i dati di ciascun contatore da sonde separate. Anche più collezionisti possono
alimentare in un unico aggregatore, che (come suggerisce il nome) può raccogliere una serie di dati
flussi per l'inclusione in un singolo grafico, file o database.

Dati Collezione Helpers
La piena flessibilità del quadro di raccolta dati è fornita dall'interconnessione
di sonde, collettori e aggregatori. L'esecuzione di tutte queste interconnessioni porta a
molte istruzioni di configurazione nei programmi utente. Per facilità d'uso, alcuni dei più comuni
le operazioni possono essere combinate e incapsulate in funzioni di supporto. Inoltre, alcuni
dichiarazioni che coinvolgono NS-3 le fonti di traccia non hanno collegamenti Python, a causa di limitazioni in
le legature.

Dati Collezione Helpers Panoramica
In questa sezione, forniamo una panoramica di alcune classi di supporto che sono state create per
facilitare la configurazione del framework di raccolta dati per alcuni casi d'uso comuni. Il
gli helper consentono agli utenti di formare operazioni comuni con solo poche istruzioni nel loro C++ o
Programmi Python. Ma questa facilità d'uso ha un costo notevolmente inferiore
flessibilità rispetto a quella che può fornire la configurazione di basso livello e la necessità di codificare esplicitamente
supporto per nuovi tipi di sonda negli helper (per aggirare un problema descritto di seguito).

L'enfasi sugli attuali aiutanti è quella di smistare i dati da NS-3 rintracciare le fonti in
gnuplot grafici o file di testo, senza un alto grado di personalizzazione dell'output o statistica
elaborazione (inizialmente). Inoltre, l'uso è vincolato ai tipi di sonda disponibili in
NS-3. Le sezioni successive di questa documentazione andranno più in dettaglio sulla creazione di nuovi
Tipi di sonda, nonché dettagli sull'aggancio di sonde, collettori e aggregatori
in accordi personalizzati.

Ad oggi sono stati implementati due aiutanti di Data Collection:

· GnuplotHelper

· FileHelper

GnuplotHelper
GnuplotHelper è una classe di supporto per la produzione di file di output utilizzati per creare gnuplot. Il
l'obiettivo generale è fornire la possibilità agli utenti di creare rapidamente grafici dai dati esportati
in NS-3 fonti di traccia. Per impostazione predefinita, viene eseguita una quantità minima di trasformazione dei dati;
l'obiettivo è generare grafici con il minor numero di istruzioni di configurazione (predefinite) come
possibile.

GnuplotHelper Panoramica
GnuplotHelper creerà 3 file diversi alla fine della simulazione:

· Un file di dati gnuplot separato da spazi

· Un file di controllo gnuplot

· Uno script di shell per generare gnuplot

Sono necessarie due istruzioni di configurazione per produrre grafici. Il primo
l'istruzione configura la trama (nome file, titolo, legende e tipo di output, dove l'output
il tipo predefinito è PNG se non specificato):

void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &titolo,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");

La seconda istruzione aggancia la fonte di interesse della traccia:

void PlotProbe (const std::string &typeId,
const std::string &percorso,
const std::string &probeTraceSource,
const std::string &titolo);

Gli argomenti sono i seguenti:

· typeId: Il NS-3 TypeId della sonda

· percorso: Il percorso nel NS-3 spazio dei nomi di configurazione a una o più origini di traccia

· probeTraceSource: quale uscita della sonda (a sua volta una sorgente di traccia) deve essere tracciata

· titolo: il titolo da associare al set di dati (nella legenda di gnuplot)

Una variante del PlotProbe sopra è specificare un quinto argomento opzionale che controlli
dove nella trama è posizionata la chiave (leggenda).

Un esempio completamente funzionante (da settimo.cc) è mostrato di seguito:

// Crea l'helper gnuplot.
GnuplotHelper plotHelper;

// Configura la trama.
// Configura la trama. Il primo argomento è il prefisso del nome del file
// per i file di output generati. Il secondo, il terzo e il quarto
// gli argomenti sono, rispettivamente, il titolo del grafico, le etichette dell'asse x e dell'asse y
plotHelper.ConfigurePlot ("settimo-pacchetto-byte-count",
"Conteggio byte pacchetto vs. tempo",
"Tempo (secondi)",
"Conteggio byte pacchetto",
"png");

// Specifica il tipo di sonda, traccia il percorso di origine (nello spazio dei nomi di configurazione) e
// sonda l'origine della traccia dell'output ("OutputBytes") da tracciare. Il quarto argomento
// specifica il nome dell'etichetta della serie di dati sul grafico. L'ultimo
// argomento formatta il grafico specificando dove deve essere posizionata la chiave.
plotHelper.PlotProbe (probeType,
tracePath,
"Output Byte",
"Conteggio byte pacchetto",
GnuplotAggregator::KEY_BELOW);

In questo esempio, il tipo sonda e tracePath sono i seguenti (per IPv4):

probeType = "ns3::Ipv4PacketProbe";
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx";

Il probeType è un parametro chiave per il funzionamento di questo helper. Questo TypeId deve essere registrato
nel sistema e la firma sul sink di traccia della sonda deve corrispondere a quella della traccia
fonte a cui è collegato. I tipi di sonda sono predefiniti per un numero di tipi di dati
corrispondente NS-3 valori tracciati e per alcune altre firme di origine di traccia come
la fonte di traccia 'Tx' di ns3::Protocollo IPv4L3 classe.

Si noti che il percorso di origine della traccia specificato può contenere caratteri jolly. In questo caso, multiplo
i set di dati sono tracciati su un grafico; uno per ogni percorso abbinato.

L'output principale prodotto sarà tre file:

settimo-pacchetto-byte-count.dat
settimo-pacchetto-byte-count.plt
settimo-pacchetto-byte-count.sh

A questo punto, gli utenti possono modificare manualmente il file .plt per ulteriori personalizzazioni, oppure
basta eseguirlo tramite gnuplot. In esecuzione sh settimo-pacchetto-byte-count.sh esegue semplicemente la trama
tramite gnuplot, come mostrato di seguito.
[immagine] 2-D Gnuplot Creato da seventh.cc Esempio..UNINDENT

Si può notare che gli elementi chiave (leggenda, titolo, posizionamento legenda, xlabel, ylabel,
e percorso per i dati) sono tutti posizionati sul grafico. Dato che c'erano due partite per il
percorso di configurazione fornito, vengono mostrate le due serie di dati:

· Packet Byte Count-0 corrisponde a /NodeList/0/$ns3::Ipv4L3Protocol/Tx

· Packet Byte Count-1 corrisponde a /NodeList/1/$ns3::Ipv4L3Protocol/Tx

GnuplotHelper ConfiguraTrama
Il GnuplotHelper's ConfiguraTrama() la funzione può essere utilizzata per configurare i grafici.

Ha il seguente prototipo:

void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &titolo,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");

Ha i seguenti argomenti:

? ?
│Argomento │ Descrizione │
? ?
outputFileNameWithoutExtension │ Nome dei file relativi a gnuplot a │
│ scrivi senza estensione. ?
? ?
│title │ Traccia la stringa del titolo da usare per │
│ │ questa trama. ?
? ?
│xLegend │ La legenda per la x orizzontale │
│ asse. ?
? ?
│yLegend │ La legenda per la y verticale │
│ asse. ?
? ?

│terminalType │ Stringa di impostazione del tipo di terminale per │
│ uscita. Il terminale predefinito
│ il tipo è "png". ?
? ?

Il GnuplotHelper's ConfiguraTrama() la funzione configura i parametri relativi alla trama per questo
gnuplot helper in modo che crei un file di dati gnuplot separato da spazi chiamato
outputFileNameWithoutExtension + ".dat", un file di controllo gnuplot chiamato
outputFileNameWithoutExtension + ".plt" e uno script di shell per generare lo gnuplot denominato
outputNomeFileSenzaEstensione + ".sh".

Un esempio di come utilizzare questa funzione può essere visto nel settimo.cc codice sopra descritto
dove è stato utilizzato come segue:

plotHelper.ConfigurePlot ("settimo-pacchetto-byte-count",
"Conteggio byte pacchetto vs. tempo",
"Tempo (secondi)",
"Conteggio byte pacchetto",
"png");

GnuplotHelper PlotProbe
Il GnuplotHelper's Plotprobe() La funzione può essere utilizzata per tracciare i valori generati dalle sonde.

Ha il seguente prototipo:

void PlotProbe (const std::string &typeId,
const std::string &percorso,
const std::string &probeTraceSource,
const std::string &titolo,
enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);

Ha i seguenti argomenti:

? ?
│Argomento │ Descrizione │
? ?
│typeId │ L'ID del tipo per la sonda │
│ creato da questo aiutante. ?
? ?
│percorso │ Percorso di configurazione per accedere alla traccia │
│ fonte. ?
? ?
│probeTraceSource │ La sorgente di traccia della sonda su │
│ accesso. ?
? ?
│titolo │ Il titolo da associare a │
│ │ questo set di dati │
? ?
│keyLocation │ La posizione della chiave nella │
│ trama. La posizione predefinita è │
│ dentro. ?
? ?

Il GnuplotHelper's Plotprobe() la funzione traccia un set di dati generato agganciando il NS-3
tracciare la sorgente con una sonda creata dall'helper e quindi tracciare i valori da
probeTraceSource. Il set di dati avrà il titolo fornito e sarà composto da
'newValue' ad ogni timestamp.

Se il percorso di configurazione ha più di una corrispondenza nel sistema perché è presente un carattere jolly, allora
verrà tracciato un set di dati per ogni partita. I titoli dei set di dati saranno suffissi con il
caratteri corrispondenti per ciascuno dei caratteri jolly nel percorso di configurazione, separati da spazi. Per
esempio, se il titolo del set di dati proposto è la stringa "byte" e sono presenti due caratteri jolly
nel percorso, saranno possibili titoli del set di dati come "bytes-0 0" o "byte-12 9" come
etichette per i set di dati che vengono tracciati.

Un esempio di come utilizzare questa funzione può essere visto nel settimo.cc codice sopra descritto
dove è stato utilizzato (con sostituzione di variabile) come segue:

plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Output Byte",
"Conteggio byte pacchetto",
GnuplotAggregator::KEY_BELOW);

Altri Esempi
gnuplot Aiutante Esempio
Un esempio leggermente più semplice del settimo.cc esempio può essere trovato in
src/stats/examples/gnuplot-helper-example.cc. Il seguente gnuplot 2-D è stato creato utilizzando
l'esempio.
[immagine] Gnuplot 2-D Creato da gnuplot-helper-example.cc Esempio..UNINDENT

In questo esempio, c'è un oggetto Emettitore che incrementa il suo contatore secondo a
Processo di Poisson e quindi emette il valore del contatore come sorgente di traccia.

Ptr emettitore = CreaOggetto ();
Nomi::Aggiungi ("/Nomi/Emettitore", emettitore);

Si noti che poiché non ci sono caratteri jolly nel percorso utilizzato di seguito, solo 1 flusso di dati era
disegnato nella trama. Questo singolo flusso di dati nella trama è semplicemente etichettato "Conteggio emettitori",
senza suffissi aggiuntivi come si vedrebbe se ci fossero caratteri jolly nel percorso.

// Crea l'helper gnuplot.
GnuplotHelper plotHelper;

// Configura la trama.
plotHelper.ConfigurePlot ("gnuplot-helper-example",
"Conteggi emettitori rispetto al tempo",
"Tempo (secondi)",
"Conteggio emettitori",
"png");

// Traccia i valori generati dalla sonda. Il percorso che forniamo
// aiuta a disambiguare l'origine della traccia.
plotHelper.PlotProbe ("ns3::Uinteger32Probe",
"/Nomi/Emettitore/Contatore",
"Produzione",
"Conteggio emettitori",
GnuplotAggregator::KEY_INSIDE);

FileHelper
Il FileHelper è una classe di supporto utilizzata per inserire i valori dei dati in un file. L'obiettivo generale è
per fornire agli utenti la possibilità di creare rapidamente file di testo formattati dai dati esportati
in NS-3 fonti di traccia. Per impostazione predefinita, viene eseguita una quantità minima di trasformazione dei dati;
l'obiettivo è generare file con il minor numero di istruzioni di configurazione (predefinite) come
possibile.

FileHelper Panoramica
Il FileHelper creerà 1 o più file di testo alla fine della simulazione.

Il FileHelper può creare 4 diversi tipi di file di testo:

· Formattato

· Separati da spazi (impostazione predefinita)

· Separato da virgola

· Tab separato

I file formattati utilizzano stringhe di formato in stile C e la funzione sprintf() per stamparli
valori nel file in fase di scrittura.

Il seguente file di testo con 2 colonne di valori formattati denominati
settimo-pacchetto-byte-count-0.txt è stato creato utilizzando più nuovo codice che è stato aggiunto al
i NS-3 Codice dell'esempio tutorial. Vengono mostrate solo le prime 10 righe di questo file
qui per brevità.

Tempo (secondi) = 1.000e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.004e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.004e+00 Conteggio byte pacchetto = 576
Tempo (secondi) = 1.009e+00 Conteggio byte pacchetto = 576
Tempo (secondi) = 1.009e+00 Conteggio byte pacchetto = 576
Tempo (secondi) = 1.015e+00 Conteggio byte pacchetto = 512
Tempo (secondi) = 1.017e+00 Conteggio byte pacchetto = 576
Tempo (secondi) = 1.017e+00 Conteggio byte pacchetto = 544
Tempo (secondi) = 1.025e+00 Conteggio byte pacchetto = 576
Tempo (secondi) = 1.025e+00 Conteggio byte pacchetto = 544

...

Il seguente file di testo diverso con 2 colonne di valori formattati denominati
settimo-pacchetto-byte-count-1.txt è stato anche creato utilizzando lo stesso nuovo codice che è stato aggiunto a
l'originale NS-3 Codice dell'esempio tutorial. Vengono mostrate solo le prime 10 righe di questo file
qui per brevità.

Tempo (secondi) = 1.002e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.007e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.013e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.020e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.028e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.036e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.045e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.053e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.061e+00 Conteggio byte pacchetto = 40
Tempo (secondi) = 1.069e+00 Conteggio byte pacchetto = 40

...

Di seguito è riportato il nuovo codice che è stato aggiunto per produrre i due file di testo. Maggiori dettagli su
questa API sarà trattata in una sezione successiva.

Nota che poiché c'erano 2 corrispondenze per il carattere jolly nel percorso, 2 file di testo separati
sono stati creati. Il primo file di testo, denominato "seventh-packet-byte-count-0.txt",
corrisponde alla corrispondenza del carattere jolly con "*" sostituito da "0". Il secondo file di testo,
che si chiama "seventh-packet-byte-count-1.txt", corrisponde alla corrispondenza con caratteri jolly con
il "*" sostituito con "1". Si noti inoltre che la funzione chiamata a ScriviSonda() darà un
messaggio di errore se non ci sono corrispondenze per un percorso che contiene caratteri jolly.

// Crea l'helper file.
FileHelper fileHelper;

// Configura il file da scrivere.
fileHelper.ConfigureFile ("settimo-pacchetto-byte-count",
FileAggregator::FORMATTATO);

// Imposta le etichette per questo file di output formattato.
fileHelper.Set2dFormat ("Tempo (secondi) = %.3e\tConteggio byte pacchetto = %.0f");

// Scrive i valori generati dalla sonda.
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Output Byte");

FileHelper ConfiguraFile
Il FileHelper's ConfiguraFile() La funzione può essere utilizzata per configurare i file di testo.

Ha il seguente prototipo:

void ConfigureFile (const std::string &outputFileNameWithoutExtension,
enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);

Ha i seguenti argomenti:

? ?
│Argomento │ Descrizione │
? ?
outputFileNameWithoutExtension │ Nome del file di output da scrivere │
│ │ senza estensione. ?
? ?
fileType │ Tipo di file da scrivere. il
│ │ il tipo di file predefinito è lo spazio │
│ separato. ?
? ?

Il FileHelper's ConfiguraFile() la funzione configura i parametri relativi ai file di testo per il
file helper in modo che crei un file chiamato outputFileNameWithoutExtension plus
possibili informazioni extra dalle corrispondenze con caratteri jolly più ".txt" con valori stampati come
specificato da fileType. Il tipo di file predefinito è separato da spazi.

Un esempio di come utilizzare questa funzione può essere visto nel settimo.cc codice sopra descritto
dove è stato utilizzato come segue:

fileHelper.ConfigureFile ("settimo-pacchetto-byte-count",
FileAggregator::FORMATTATO);

FileHelper Scrivi Sonda
Il FileHelper's ScriviSonda() la funzione può essere utilizzata per scrivere i valori generati dalle sonde su
file di testo.

Ha il seguente prototipo:

void WriteProbe (const std::string &typeId,
const std::string &percorso,
const std::string &probeTraceSource);

Ha i seguenti argomenti:

? ?
│Argomento │ Descrizione │
? ?
typeId │ L'ID del tipo per la sonda da
│ creato. ?
? ?
│percorso │ Percorso di configurazione per accedere alla traccia │
│ fonte. ?
? ?
│probeTraceSource │ La sorgente di traccia della sonda su │
│ accesso. ?
? ?

Il FileHelper's ScriviSonda() la funzione crea file di testo di output generati agganciando il
ns-3 trace source con una sonda creata dall'helper e quindi scrivendo i valori da
probeTraceSource. I nomi dei file di output avranno il testo memorizzato nella variabile membro
m_outputFileNameWithoutExtension più ".txt", e consisterà in 'newValue' ad ogni
marca temporale.

Se il percorso di configurazione ha più di una corrispondenza nel sistema perché è presente un carattere jolly, allora
verrà creato un file di output per ogni corrispondenza. I nomi dei file di output conterranno il
testo in m_outputFileNameWithoutExtension più i caratteri corrispondenti per ciascuno dei
caratteri jolly nel percorso di configurazione, separati da trattini, più ".txt". Ad esempio, se il valore
in m_outputFileNameWithoutExtension c'è la stringa "packet-byte-count", e ce ne sono due
caratteri jolly nel percorso, quindi emettere nomi di file come "packet-byte-count-0-0.txt" o
"packet-byte-count-12-9.txt" sarà possibile come nomi per i file che verranno creati.

Un esempio di come utilizzare questa funzione può essere visto nel settimo.cc codice sopra descritto
dove è stato utilizzato come segue:

fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Output Byte");

Altri Esempi
Compila il Aiutante Esempio
Un esempio leggermente più semplice del settimo.cc esempio può essere trovato in
src/stats/examples/file-helper-example.cc. Questo esempio utilizza solo FileHelper.

Il seguente file di testo con 2 colonne di valori formattati denominati file-helper-esempio.txt
è stato creato utilizzando l'esempio. Qui vengono mostrate solo le prime 10 righe di questo file per
brevità.

Tempo (secondi) = 0.203 Conteggio = 1
Tempo (secondi) = 0.702 Conteggio = 2
Tempo (secondi) = 1.404 Conteggio = 3
Tempo (secondi) = 2.368 Conteggio = 4
Tempo (secondi) = 3.364 Conteggio = 5
Tempo (secondi) = 3.579 Conteggio = 6
Tempo (secondi) = 5.873 Conteggio = 7
Tempo (secondi) = 6.410 Conteggio = 8
Tempo (secondi) = 6.472 Conteggio = 9
...

In questo esempio, c'è un oggetto Emettitore che incrementa il suo contatore secondo a
Processo di Poisson e quindi emette il valore del contatore come sorgente di traccia.

Ptr emettitore = CreaOggetto ();
Nomi::Aggiungi ("/Nomi/Emettitore", emettitore);

Si noti che poiché non sono presenti caratteri jolly nel percorso utilizzato di seguito, è stato utilizzato solo 1 file di testo
creato. Questo singolo file di testo si chiama semplicemente "file-helper-example.txt", senza extra
suffissi come vedresti se ci fossero caratteri jolly nel percorso.

// Crea l'helper file.
FileHelper fileHelper;

// Configura il file da scrivere.
fileHelper.ConfigureFile ("esempio-file-helper",
FileAggregator::FORMATTATO);

// Imposta le etichette per questo file di output formattato.
fileHelper.Set2dFormat ("Tempo (secondi) = %.3e\tCount = %.0f");

// Scrive i valori generati dalla sonda. Il percorso che noi
//fornire aiuta a disambiguare l'origine della traccia.
fileHelper.WriteProbe ("ns3::Uinteger32Probe",
"/Nomi/Emettitore/Contatore",
"Produzione");

Obbiettivo e Limiti
Attualmente, solo queste Probe sono state implementate e collegate a GnuplotHelper e
al FileHelper:

· Sonda Booleana

· Doppia sonda

· Uintero8Sonda

· Uintero16Sonda

· Uintero32Sonda

· Sonda temporale

·PacketProbe

· ApplicationPacketProbe

· Ipv4PacketProbe

Queste sonde, quindi, sono gli unici TypeId disponibili per essere utilizzati in Plotprobe() e
ScriviSonda().

Nelle prossime sezioni, tratteremo ciascuno dei tipi di oggetti fondamentali (Probe, Collector,
e Aggregator) in modo più dettagliato e mostra come possono essere collegati tra loro utilizzando
API di livello inferiore.

Sonde
Questa sezione descrive in dettaglio le funzionalità fornite dalla classe Probe ad un NS-3
simulazione e fornisce esempi su come codificarli in un programma. Questa sezione è pensata per
utenti interessati a sviluppare simulazioni con il NS-3 strumenti e utilizzo dei Dati
Collection Framework, di cui fa parte la classe Probe, per generare output di dati con
i risultati della loro simulazione.

Campione Panoramica
Si suppone che un oggetto Probe sia connesso a una variabile della simulazione i cui valori
durante l'esperimento sono rilevanti per l'utente. La sonda registrerà ciò che era
valori assunti dalla variabile durante la simulazione e passare tali dati a un altro
membro del Data Collection Framework. Anche se non rientra nell'ambito di questa sezione
discutere cosa succede dopo che la sonda produce il suo output, è sufficiente dire che, da
alla fine della simulazione, l'utente avrà informazioni dettagliate su quali valori erano
memorizzato all'interno della variabile da sondare durante la simulazione.

Tipicamente, una Sonda è collegata ad un NS-3 fonte di traccia. In questo modo, ogni volta che
trace source esporta un nuovo valore, la sonda consuma il valore (e lo esporta a valle
a un altro oggetto tramite la propria sorgente di traccia).

Il Probe può essere pensato come una sorta di filtro sulle sorgenti di traccia. Le ragioni principali per
eventualmente agganciarsi a un Probe piuttosto che direttamente a una sorgente di traccia sono i seguenti:

· Le sonde possono essere attivate e disattivate dinamicamente durante la simulazione con chiamate a Abilitare()
e Disattivare(). Ad esempio, l'emissione dei dati può essere disattivata durante il
fase di riscaldamento della simulazione.

· Le sonde possono eseguire operazioni sui dati per estrarre valori da più complicati
strutture; per esempio, emettendo il valore della dimensione del pacchetto da un ns3::Packet ricevuto.

· Le sonde registrano un nome nello spazio dei nomi ns3::Config (usando Nomi::Aggiungi ()) in modo che altro
gli oggetti possono fare riferimento ad essi.

· Le sonde forniscono un metodo statico che consente di manipolare una sonda per nome, come
cosa viene fatto in ns2measure [Cic06]

Stat::put ("my_metric", ID, campione);

L'equivalente ns-3 del codice ns2measure sopra è, ad es

DoubleProbe::SetValueByPath ("/percorso/a/probe", esempio);

coerenti
Nota che un oggetto della classe base Probe non può essere creato perché è una base astratta
class, cioè ha funzioni virtuali pure che non sono state implementate. Un oggetto di
il tipo DoubleProbe, che è una sottoclasse della classe Probe, verrà creato qui per mostrare
cosa bisogna fare.

Si dichiara un DoubleProbe in memoria dinamica usando la classe puntatore intelligente (Ptr ). a
creare un DoubleProbe in memoria dinamica con puntatori intelligenti, basta chiamare il
NS-3 metodo CreaOggetto():

Ptr myprobe = CreaOggetto ();

La dichiarazione precedente crea DoubleProbes utilizzando i valori predefiniti per i suoi attributi.
Ci sono quattro attributi nella classe DoubleProbe; due nell'oggetto della classe base
DataCollectionObject e due nella classe base Probe:

· "Nome" (DataCollectionObject), un valore String

· "Enabled" (DataCollectionObject), un valore booleano

· "Start" (Sonda), un TimeValue

· "Stop" (Sonda), un TimeValue

È possibile impostare tali attributi alla creazione dell'oggetto utilizzando il seguente metodo:

Ptr myprobe = CreateObjectWithAttributes (
"Nome", StringValue ("myprobe"),
"Abilitato", BooleanValue (falso),
"Start", TimeValue (secondi (100.0)),
"Stop", TimeValue (secondi (1000.0)));

Start e Stop sono variabili di Tempo che determinano l'intervallo di azione della Sonda. Il
La sonda emetterà dati solo se l'ora corrente della simulazione è all'interno di quella
intervallo. Il valore temporale speciale di 0 secondi per Stop disabiliterà questo attributo (es
tenere accesa la sonda per tutta la simulazione). Abilitato è un flag che accende la sonda o
off e deve essere impostato su true affinché la sonda possa esportare i dati. Il Nome è il nome dell'oggetto
nel quadro DCF.

Importazione e esportazione dati
NS-3 le sorgenti di traccia sono fortemente tipizzate, quindi i meccanismi per agganciare le sonde a una traccia
sorgente e per l'esportazione dei dati appartengono alle sue sottoclassi. Ad esempio, l'impostazione predefinita
distribuzione di NS-3 fornisce una classe DoubleProbe progettata per agganciarsi a una traccia
sorgente che esporta un valore doppio. Successivamente descriveremo in dettaglio il funzionamento di DoubleProbe e
poi discutere come altre classi Probe possono essere definite dall'utente.

Doppia sonda Panoramica
Il DoubleProbe si connette a un doppio valore NS-3 trace source e esporta esso stesso a
diverso valore doppio NS-3 fonte di traccia.

Il codice seguente, tratto da src/stats/examples/double-probe-example.cc, mostra la base
operazioni di inserimento del DoubleProbe in una simulazione, dove sta sondando un contatore
esportato da un oggetto emettitore (classe Emettitore).

Ptr emettitore = CreaOggetto ();
Nomi::Aggiungi ("/Nomi/Emettitore", emettitore);
...

Ptr probe1 = CreaOggetto ();

// Collega la sonda al contatore dell'emettitore
bool connesso = probe1->ConnectByObject ("Contatore", emettitore);

Il codice seguente sta sondando lo stesso contatore esportato dallo stesso oggetto emettitore. Questo
DoubleProbe, tuttavia, utilizza un percorso nello spazio dei nomi di configurazione per rendere il
connessione. Nota che l'emettitore si è registrato nello spazio dei nomi di configurazione dopo
è stato creato; in caso contrario, ConnectByPath non funzionerebbe.

Ptr probe2 = CreaOggetto ();

// Nota, qui non viene controllato alcun valore di ritorno
probe2->ConnectByPath ("/Nomi/Emettitore/Contatore");

Il prossimo DoubleProbe mostrato sotto avrà il suo valore impostato usando il suo percorso in
lo spazio dei nomi di configurazione. Nota che questa volta DoubleProbe si è registrato nel
spazio dei nomi di configurazione dopo che è stato creato.

Ptr probe3 = CreaOggetto ();
probe3->SetName ("StaticallyAccessedProbe");

// Dobbiamo aggiungerlo al database di configurazione
Names::Add ("/Names/Probes", probe3->GetName (), probe3);

La funzione Count() dell'emettitore è ora in grado di impostare il valore per questo DoubleProbe come
segue:

nulla
Emettitore:: Conteggio (vuoto)
{
...
m_contatore += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
...
}

L'esempio sopra mostra come il codice che chiama la sonda non debba avere un'esplicita
riferimento al Probe, ma può indirizzare l'impostazione del valore tramite lo spazio dei nomi Config.
Questo è simile nella funzionalità al Statistica::Messa metodo introdotto da ns2measure paper
[Cic06] e consente agli utenti di inserire temporaneamente istruzioni Probe come printf dichiarazioni
all'interno dell'esistente NS-3 Modelli. Nota che per poter utilizzare DoubleProbe in questo
esempio come questo, erano necessarie 2 cose:

1. il file di intestazione del modulo statistiche è stato incluso nel file .cc di esempio

2. l'esempio è stato reso dipendente dal modulo stats nel suo file wscript.

Analoghe operazioni devono essere fatte per aggiungere altre Probe in altri punti del NS-3
base di codice.

I valori per DoubleProbe possono essere impostati anche utilizzando la funzione DoubleProbe::SetValue(),
mentre i valori per il DoubleProbe possono essere ottenuti utilizzando la funzione
DoubleProbe::GetValue().

Il DoubleProbe esporta i valori double nella sua origine di traccia "Output"; un oggetto a valle
può agganciare un trace sink (NotifyViaProbe) a questo come segue:

connesso = probe1->TraceConnect ("Output", probe1->GetName (), MakeCallback (&NotifyViaProbe));

Altri sonde
Oltre al DoubleProbe, sono disponibili anche le seguenti Sonde:

· Uintger8Probe si connette a un NS-3 sorgente di traccia che esporta un uint8_t.

· Uintger16Probe si connette a un NS-3 sorgente di traccia che esporta un uint16_t.

· Uintger32Probe si connette a un NS-3 sorgente di traccia che esporta un uint32_t.

· PacketProbe si connette a un NS-3 sorgente di traccia che esporta un pacchetto.

· ApplicationPacketProbe si connette a un NS-3 trace source esportando un pacchetto e un socket
indirizzo.

· Ipv4PacketProbe si connette a un NS-3 trace source esportando un pacchetto, un oggetto IPv4 e
un'interfaccia.

Creazione nuovi Campione Tipi di
Per creare un nuovo tipo di sonda, è necessario eseguire i seguenti passaggi:

· Assicurati che la tua nuova classe Probe sia derivata dalla classe base Probe.

· Assicurati che le funzioni virtuali pure che la tua nuova classe Probe eredita dal
La classe base della sonda è implementata.

· Trova una classe Probe esistente che utilizza una sorgente di traccia che è più vicina nel tipo al
tipo di sorgente di traccia che utilizzerà la sonda.

· Copiare il file di intestazione della classe Probe esistente (.h) e il file di implementazione (.cc) su due
nuovi file con nomi corrispondenti alla tua nuova sonda.

· Sostituisci i tipi, gli argomenti e le variabili nei file copiati con l'appropriato
digita per la tua sonda.

· Apportare le modifiche necessarie per compilare il codice e farlo comportare come faresti tu
piace.

Esempi
Due esempi saranno discussi in dettaglio qui:

· Esempio di doppia sonda

· Esempio di grafico del pacchetto IPv4

Doppio Campione Esempio
L'esempio della doppia sonda è stato discusso in precedenza. Il programma di esempio può essere trovato
in src/stats/examples/double-probe-example.cc. Per riassumere ciò che accade in questo programma,
c'è un emettitore che esporta un contatore che incrementa secondo un processo di Poisson.
In particolare vengono mostrate due modalità di emissione dei dati:

1. tramite una variabile tracciata agganciata ad una Probe:

TracedValue m_contatore; // normalmente questo sarebbe di tipo intero

2. tramite un contatore il cui valore è inviato a una seconda sonda, referenziata dal suo nome in
il sistema di configurazione:

nulla
Emettitore:: Conteggio (vuoto)
{
NS_LOG_FUNCTION (questo);
NS_LOG_DEBUG ("Conteggio a " << Simulator::Now ().GetSeconds ());
m_contatore += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (Secondi (m_var->GetValue ()), &Emitter::Count, this);
}

Esaminiamo più attentamente la sonda. Le sonde possono ricevere i loro valori in multipli
modi:

1. dalla sonda accedendo direttamente alla sorgente di traccia e collegando un sink di traccia ad essa

2. dal Probe che accede all'origine della traccia tramite lo spazio dei nomi di configurazione e si connette a
traccia affondare ad esso

3. dal codice chiamante chiamando esplicitamente i Probe's Valore impostato() metodo

4. dal codice chiamante chiamando esplicitamente ImpostaValorePerPercorso
("/percorso/attraverso/Config/spazio dei nomi", ...)

Le prime due tecniche dovrebbero essere le più comuni. Anche nell'esempio, il
viene mostrato l'aggancio di una normale funzione di callback, come avviene tipicamente in NS-3. Questo
la funzione di callback non è associata a un oggetto Probe. Chiameremo questo caso 0) di seguito.

// Questa è una funzione per testare l'aggancio di una funzione non elaborata alla sorgente di traccia
nulla
NotifyViaTraceSource (std::string contesto, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}

Innanzitutto, l'emettitore deve essere configurato:

Ptr emettitore = CreaOggetto ();
Nomi::Aggiungi ("/Nomi/Emettitore", emettitore);

// L'oggetto Emettitore non è associato a un nodo ns-3, quindi
// non si avvia automaticamente, quindi dobbiamo farlo da soli
Simulatore::Schedule (secondi (0.0), &Emitter::Start, emettitore);

I vari DoubleProbe interagiscono con l'emettitore nell'esempio come mostrato di seguito.

Caso 0):

// Quanto segue mostra la funzionalità tipica senza sonda
// (connette una funzione sink a una sorgente di traccia)
//
connesso = emettitore->TraceConnect ("Contatore", "contesto campione", MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (connesso, "Sorgente di traccia non connesso");

caso 1):

//
// Probe1 verrà agganciata direttamente all'oggetto sorgente di traccia dell'emettitore
//

// probe1 verrà agganciato alla sorgente di traccia dell'emettitore
Ptr probe1 = CreaOggetto ();
// il nome della sonda può fungere da contesto nel tracciamento
probe1->SetName ("ObjectProbe");

// Collega la sonda al contatore dell'emettitore
connesso = probe1->ConnectByObject ("Contatore", emettitore);
NS_ASSERT_MSG (connesso, "Trace source non connesso a probe1");

caso 2):

//
// Probe2 verrà agganciata all'oggetto sorgente di traccia dell'emettitore da
// accedendovi tramite il nome del percorso nel database di configurazione
//

// Crea un'altra sonda simile; questo si collegherà tramite un percorso di configurazione
Ptr probe2 = CreaOggetto ();
probe2->SetName ("PathProbe");

// Nota, qui non viene controllato alcun valore di ritorno
probe2->ConnectByPath ("/Nomi/Emettitore/Contatore");

caso 4) (il caso 3 non è mostrato in questo esempio):

//
// La sonda 3 verrà chiamata dall'emettitore direttamente tramite il
// metodo statico SetValueByPath().
//
Ptr probe3 = CreaOggetto ();
probe3->SetName ("StaticallyAccessedProbe");
// Dobbiamo aggiungerlo al database di configurazione
Names::Add ("/Names/Probes", probe3->GetName (), probe3);

E infine, l'esempio mostra come le sonde possono essere agganciate per generare output:

// La sonda stessa dovrebbe generare un output. Il contesto che forniamo
// a questa sonda (in questo caso, il nome della sonda) aiuterà a disambiguare
// la fonte della traccia
connesso = probe3->TraceConnect ("Uscita",
"/Names/Probes/StaticallyAccessedProbe/Output",
MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (connesso, "Trace source not .. connected to probe3 Output");

Il seguente callback è agganciato al Probe in questo esempio a scopo illustrativo;
normalmente, la sonda sarebbe agganciata a un oggetto Collector.

// Questa è una funzione per testare l'aggancio all'uscita della sonda
nulla
NotifyViaProbe (std::string contesto, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " old " << oldVal << " new " << newVal);
}

IPv4 Pacchetto Parcella Esempio
L'esempio del grafico del pacchetto IPv4 si basa sul quinto esempio.cc del NS-3 Tutorial. Esso
possono essere trovati nell' src/stats/examples/ipv4-packet-plot-example.cc.

nodo 0 nodo 1
+----------------+ +----------------+
| ns-3TCP | | ns-3TCP |
+----------------+ +----------------+
| 10.1.1.1| | 10.1.1.2|
+----------------+ +----------------+
| punto-punto | | punto-punto |
+----------------+ +----------------+
| |
+----------------------+

Esamineremo solo la sonda, poiché illustra che le sonde possono anche decomprimere i valori da
strutture (in questo caso, pacchetti) e riportano quei valori come output della sorgente di traccia, piuttosto
piuttosto che passare attraverso lo stesso tipo di dati.

Ci sono altri aspetti di questo esempio che verranno spiegati più avanti nella documentazione.
I due tipi di dati che vengono esportati sono il pacchetto stesso (Uscita) e un conteggio di
numero di byte nel pacchetto (Byte di output).

ID tipo
Ipv4PacketProbe::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
.SetParent ()
.Aggiungi costruttore ()
.AddTraceSource ("Uscita",
"Il pacchetto più il suo oggetto IPv4 e l'interfaccia che servono come output per questa sonda",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource ("OutputBytes",
"Il numero di byte nel pacchetto",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
ritorno a mare;
}

Quando il sink di traccia della sonda riceve un pacchetto, se la sonda è abilitata, verrà emesso
il pacchetto sul suo Uscita trace source, ma produrrà anche il numero di byte sul
Byte di output fonte di traccia.

nulla
Ipv4PacketProbe::TraceSink (Parte) pacchetto, Ptr ipv4, interfaccia uint4_t)
{
NS_LOG_FUNCTION (questo << pacchetto << ipv4 << interfaccia);
if (è abilitato ())
{
m_packet = pacchetto;
m_ipv4 = ipv4;
m_interface = interfaccia;
m_output (pacchetto, ipv4, interfaccia);

uint32_t packetSizeNew = pacchetto->GetSize ();
m_outputBytes (m_packetSizeOld, packetSizeNew);
m_packetSizeOld = pacchettoSizeNew;
}
}

Testimonianze
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, "Un quadro integrato per
Consentire un'efficace raccolta dati e analisi statistica con ns2, Workshop on
ns-2 (WNS2), Pisa, Italia, ottobre 2006.

Collezionisti
Questa sezione è un segnaposto per dettagliare le funzionalità fornite dal Collector
classe ad an NS-3 simulazione e fornisce esempi su come codificarli in un programma.

Nota: A partire dal ns-3.18, i Collector sono ancora in fase di sviluppo e non sono ancora forniti come parte
del quadro.

Aggregatori
Questa sezione descrive in dettaglio le funzionalità fornite dalla classe Aggregator ad un NS-3
simulazione. Questa sezione è pensata per gli utenti interessati a sviluppare simulazioni con il
NS-3 strumenti e utilizzando il Data Collection Framework, di cui la classe Aggregator è una
parte, per generare dati in uscita con i risultati della loro simulazione.

Aggregator Panoramica
Si suppone che un oggetto Aggregator sia agganciato a una o più sorgenti di traccia per
ricevere input. Gli aggregatori sono il punto finale dei dati raccolti dalla rete di
Sonde e collettori durante la simulazione. È compito dell'aggregatore prenderli
valori e trasformarli nel loro formato di output finale come file di testo normale,
file di fogli di calcolo, grafici o database.

In genere, un aggregatore è connesso a uno o più Collector. In questo modo, ogni volta che
le sorgenti di traccia dei Collector esportano nuovi valori, l'Aggregator può elaborare il valore così
che può essere utilizzato nel formato di output finale in cui i valori dei dati risiederanno dopo il
simulazione.

Nota quanto segue sugli aggregatori:

· Gli aggregatori possono essere attivati ​​e disattivati ​​dinamicamente durante la simulazione con chiamate a
Abilitare() e Disattivare(). Ad esempio, l'aggregazione dei dati può essere disattivata durante
la fase di riscaldamento della simulazione, il che significa che quei valori non saranno inclusi nella finale
mezzo di uscita.

· Gli aggregatori ricevono i dati dai servizi di raccolta tramite callback. Quando un Collector è associato
a un aggregatore, viene effettuata una chiamata a TraceConnect per stabilire la traccia dell'aggregatore
sink come callback.

Ad oggi sono stati implementati due Aggregatori:

· Gnuplot Aggregatore

· FileAgregator

GnuplotAggregatore
GnuplotAggregator produce file di output usati per creare gnuplot.

Il GnuplotAggregator creerà 3 diversi file alla fine della simulazione:

· Un file di dati gnuplot separato da spazi

· Un file di controllo gnuplot

· Uno script di shell per generare gnuplot

coerenti
Qui verrà creato un oggetto di tipo GnuplotAggregator per mostrare cosa deve essere fatto.

Si dichiara un GnuplotAggregator nella memoria dinamica usando la classe puntatore intelligente
(Ptr ). Per creare un GnuplotAggregator in memoria dinamica con puntatori intelligenti, basta
deve chiamare il NS-3 metodo CreaOggetto(). Il seguente codice da
src/stats/examples/gnuplot-aggregator-example.cc mostra come farlo:

string fileNameWithoutExtension = "gnuplot-aggregator";

// Crea un aggregatore.
Ptr aggregatore =
CreaOggetto (nomefileSenzaEstensione);

Il primo argomento per il costruttore, fileNameWithoutExtension, è il nome del
file relativi a gnuplot da scrivere senza estensione. Questo GnuplotAggregator creerà un
file di dati gnuplot separato da spazi chiamato "gnuplot-aggregator.dat", un file di controllo di gnuplot
denominato "gnuplot-aggregator.plt", e uno script di shell per generare lo gnuplot denominato +
"gnuplot-aggregator.sh".

Lo gnuplot che viene creato può avere la sua chiave in 4 posizioni diverse:

· Nessuna chiave

· Chiave all'interno della trama (impostazione predefinita)

· Tasto sopra la trama

· Chiave sotto la trama

I seguenti valori enum della posizione della chiave gnuplot possono specificare la posizione della chiave:

enum posizione chiave {
NO_CHIAVE,
CHIAVE_INTERNA,
CHIAVE_SOPRA,
CHIAVE_SOTTO
};

Se si desiderava avere la chiave in basso anziché la posizione predefinita all'interno, allora
potresti fare quanto segue.

aggregatore->SetKeyLocation(GnuplotAggregator::KEY_BELOW);

Esempi
Un esempio sarà discusso in dettaglio qui:

· Esempio di aggregatore Gnuplot

gnuplot Aggregator Esempio
Un esempio che esercita GnuplotAggregator può essere trovato in
src/stats/examples/gnuplot-aggregator-example.cc.

Il seguente gnuplot 2-D è stato creato utilizzando l'esempio.
[immagine] Gnuplot 2-D Creato da gnuplot-aggregator-example.cc Esempio..UNINDENT

Questo codice dell'esempio mostra come costruire GnuplotAggregator come discusso
sopra.

void Create2dPlot()
{
usando lo spazio dei nomi std;

string fileNameWithoutExtension = "gnuplot-aggregator";
string plotTitle = "Gnuplot Aggregator Plot";
string plotXAxisHeading = "Tempo (secondi)";
string plotYAxisHeading = "Valori doppi";
string plotDatasetLabel = "Valori dati";
string datasetContext = "Set di dati/Contesto/Stringa";

// Crea un aggregatore.
Ptr aggregatore =
CreaOggetto (nomefileSenzaEstensione);

Sono impostati vari attributi di GnuplotAggregator, incluso il set di dati 2-D che sarà
tracciato.

// Imposta le proprietà dell'aggregatore.
aggregatore->SetTerminal ("png");
aggregatore->SetTitle (plotTitle);
aggregatore->SetLegend (plotXAxisHeading, plotYAxisHeading);

// Aggiunge un set di dati all'aggregatore.
aggregatore->Add2dDataset (datasetContext, plotDatasetLabel);

// l'aggregatore deve essere acceso
aggregatore->Abilita ();

Successivamente, vengono calcolati i valori 2-D e ciascuno di essi viene scritto individualmente nel
GnuplotAggregator utilizzando il Scrivi2d() funzione.

doppio tempo;
doppio valore;

// Crea il set di dati 2D.
per (tempo = -5.0; tempo <= +5.0; tempo += 1.0)
{
// Calcola la curva 2D
//
// 2
// valore = tempo.
//
valore = tempo * tempo;

// Aggiungi questo punto alla trama.
aggregatore->Write2d (datasetContext, ora, valore);
}

// Disabilita la registrazione dei dati per l'aggregatore.
aggregatore->Disabilita ();
}

FileAggregatore
Il FileAggregator invia i valori che riceve a un file.

Il FileAggregator può creare 4 diversi tipi di file:

· Formattato

· Separati da spazi (impostazione predefinita)

· Separato da virgola

· Tab separato

I file formattati utilizzano stringhe di formato in stile C e la funzione sprintf() per stamparli
valori nel file in fase di scrittura.

coerenti
Un oggetto di tipo FileAggregator verrà creato qui per mostrare cosa deve essere fatto.

Si dichiara un FileAggregator nella memoria dinamica utilizzando la classe puntatore intelligente (Ptr ).
Per creare un FileAggregator in memoria dinamica con puntatori intelligenti, basta chiamare
, il NS-3 metodo CreateObject. Il seguente codice da
src/stats/examples/file-aggregator-example.cc mostra come farlo:

string fileName = "file-aggregator-formatted-values.txt";

// Crea un aggregatore che avrà valori formattati.
pt aggregatore =
Crea oggetto (NomeFile, AggregatoreFile::FORMATTATO);

Il primo argomento per il costruttore, filename, è il nome del file da scrivere; il
il secondo argomento, fileType, è il tipo di file da scrivere. Questo FileAggregator creerà a
file denominato "file-aggregator-formatted-values.txt" con i suoi valori stampati come specificato da
fileType, ovvero formattato in questo caso.

Sono consentiti i seguenti valori di enum del tipo di file:

enum TipoFile {
FORMATTATO,
SPAZIO_SEPARATO,
SEPARATO DA VIRGOLA,
TAB_SEPARATI
};

Esempi
Un esempio sarà discusso in dettaglio qui:

· Esempio di aggregatore di file

Compila il Aggregator Esempio
Un esempio che esercita il FileAggregator può essere trovato in
src/stats/examples/file-aggregator-example.cc.

Il seguente file di testo con 2 colonne di valori separati da virgole è stato creato utilizzando il file
esempio.

all'5,25 ottobre
all'4,16 ottobre
all'3,9 ottobre
all'2,4 ottobre
all'1,1 ottobre
0,0
1,1
2,4
3,9
4,16
5,25

Questo codice dell'esempio mostra come costruire FileAggregator come è stato discusso
sopra.

void CreateCommaSeparatedFile ()
{
usando lo spazio dei nomi std;

string fileName = "file-aggregator-comma-separated.txt";
string datasetContext = "Set di dati/Contesto/Stringa";

// Crea un aggregatore.
pt aggregatore =
Crea oggetto (nomefile, aggregatore file::COMMA_SEPARATED);

Gli attributi di FileAggregator sono impostati.

// l'aggregatore deve essere acceso
aggregatore->Abilita ();

Successivamente, vengono calcolati i valori 2-D e ciascuno di essi viene scritto individualmente nel
FileAggregator usando il Scrivi2d() funzione.

doppio tempo;
doppio valore;

// Crea il set di dati 2D.
per (tempo = -5.0; tempo <= +5.0; tempo += 1.0)
{
// Calcola la curva 2D
//
// 2
// valore = tempo.
//
valore = tempo * tempo;

// Aggiungi questo punto alla trama.
aggregatore->Write2d (datasetContext, ora, valore);
}

// Disabilita la registrazione dei dati per l'aggregatore.
aggregatore->Disabilita ();
}

Anche il seguente file di testo con 2 colonne di valori formattati è stato creato utilizzando il file
esempio.

Tempo = -5.000e+00 Valore = 25
Tempo = -4.000e+00 Valore = 16
Tempo = -3.000e+00 Valore = 9
Tempo = -2.000e+00 Valore = 4
Tempo = -1.000e+00 Valore = 1
Tempo = 0.000e+00 Valore = 0
Tempo = 1.000e+00 Valore = 1
Tempo = 2.000e+00 Valore = 4
Tempo = 3.000e+00 Valore = 9
Tempo = 4.000e+00 Valore = 16
Tempo = 5.000e+00 Valore = 25

Questo codice dell'esempio mostra come costruire FileAggregator come è stato discusso
sopra.

void CreateFormattedFile ()
{
usando lo spazio dei nomi std;

string fileName = "file-aggregator-formatted-values.txt";
string datasetContext = "Set di dati/Contesto/Stringa";

// Crea un aggregatore che avrà valori formattati.
pt aggregatore =
Crea oggetto (NomeFile, AggregatoreFile::FORMATTATO);

Gli attributi FileAggregator sono impostati, inclusa la stringa di formato C da utilizzare.

// Imposta il formato per i valori.
aggregatore->Set2dFormat ("Ora = %.3e\tValore = %.0f");

// l'aggregatore deve essere acceso
aggregatore->Abilita ();

Successivamente, vengono calcolati i valori 2-D e ciascuno di essi viene scritto individualmente nel
FileAggregator usando il Scrivi2d() funzione.

doppio tempo;
doppio valore;

// Crea il set di dati 2D.
per (tempo = -5.0; tempo <= +5.0; tempo += 1.0)
{
// Calcola la curva 2D
//
// 2
// valore = tempo.
//
valore = tempo * tempo;

// Aggiungi questo punto alla trama.
aggregatore->Write2d (datasetContext, ora, valore);
}

// Disabilita la registrazione dei dati per l'aggregatore.
aggregatore->Disabilita ();
}

Adattatori
Questa sezione descrive in dettaglio le funzionalità fornite dalla classe Adapter a un NS-3
simulazione. Questa sezione è pensata per gli utenti interessati a sviluppare simulazioni con il
NS-3 strumenti e utilizzando il Data Collection Framework, di cui fa parte la classe Adapter,
per generare dati in uscita con i risultati della loro simulazione.

Nota: il termine 'adattatore' può anche essere scritto 'adattatore'; abbiamo scelto l'ortografia allineata
con lo standard C++.

Adattatore Panoramica
Un adattatore viene utilizzato per effettuare connessioni tra diversi tipi di oggetti DCF.

Ad oggi è stato implementato un Adapter:

· Adattatore TimeSeries

Ora Serie Adattatore
TimeSeriesAdaptor consente alle sonde di connettersi direttamente agli aggregatori senza bisogno di alcuno
Collezionista in mezzo.

Entrambi gli helper DCF implementati utilizzano TimeSeriesAdaptors per essere sondati
valori di diverso tipo ed emette l'ora corrente più il valore con entrambi convertiti
raddoppiare.

Il ruolo della classe TimeSeriesAdaptor è quello di un adattatore, che accetta valori non elaborati
sonda dati di diverso tipo ed emette una tupla di due doppi valori. Il primo è un
timestamp, che può essere impostato su risoluzioni diverse (es. Secondi, Millisecondi, ecc.) in
il futuro ma che attualmente è codificato in Seconds. La seconda è la conversione di a
valore non doppio a un valore doppio (possibilmente con perdita di precisione).

Ambito/Limitazioni
Questa sezione discute l'ambito e le limitazioni del Data Collection Framework.

Attualmente, solo queste sonde sono state implementate in DCF:

· Sonda Booleana

· Doppia sonda

· Uintero8Sonda

· Uintero16Sonda

· Uintero32Sonda

· Sonda temporale

·PacketProbe

· ApplicationPacketProbe

· Ipv4PacketProbe

Attualmente, nessun Collector è disponibile nel DCF, sebbene sia presente un BasicStatsCollector
.

Attualmente, solo questi Aggregatori sono stati implementati in DCF:

· Gnuplot Aggregatore

· FileAgregator

Attualmente, solo questo adattatore è stato implementato in DCF:

Adattatore per serie temporali.

Futuro Lavora
Questa sezione discute il lavoro futuro da svolgere sul quadro di raccolta dei dati.

Ecco alcune cose che devono ancora essere fatte:

· Collega più fonti di traccia in NS-3 codice per ottenere più valori dal simulatore.

· Implementare più tipi di sonde di quante ce ne siano attualmente.

· Implementa più del semplice raccoglitore 2-D corrente, BasicStatsCollector.

· Implementare più aggregatori.

· Implementa più che semplici adattatori.

Statistico Contesto
Questo capitolo delinea il lavoro sulla raccolta dei dati di simulazione e il quadro statistico per
ns-3.

Il codice sorgente per il framework statistico risiede nella directory src/statistiche.

Obiettivi
Gli obiettivi primari di questo sforzo sono i seguenti:

· Fornire funzionalità per registrare, calcolare e presentare dati e statistiche per l'analisi
di simulazioni di rete.

· Migliora le prestazioni della simulazione riducendo la necessità di generare log di traccia estesi
ordine di raccogliere dati.

· Abilitare il controllo della simulazione tramite statistiche online, ad es. terminazione di simulazioni o
prove ripetute.

Gli obiettivi secondari derivati ​​e altre funzionalità di destinazione includono quanto segue:

· Integrazione con il sistema di tracciamento ns-3 esistente come quadro di base della strumentazione
del motore di simulazione interno, ad esempio stack di rete, dispositivi di rete e canali.

· Consentire agli utenti di utilizzare il quadro statistico senza richiedere l'uso del tracciamento
.

· Aiutare gli utenti a creare, aggregare e analizzare i dati su più prove.

· Supporto per la strumentazione creata dall'utente, ad esempio di eventi specifici dell'applicazione e
provvedimenti.

· Memoria insufficiente e sovraccarico della CPU quando il pacchetto non è in uso.

· Sfruttare il più possibile gli strumenti di analisi e output esistenti. Il quadro può
fornire alcune statistiche di base, ma l'obiettivo è raccogliere dati e realizzarli
accessibile per la manipolazione in strumenti consolidati.

· L'eventuale supporto per la distribuzione di repliche indipendenti è importante ma non incluso
nel primo round di funzionalità.

Panoramica
Il quadro statistico include le seguenti caratteristiche:

· Il framework principale e due raccoglitori di dati di base: un contatore e un min/max/avg/total
osservatore.

· Estensioni di quelli per lavorare facilmente con tempi e pacchetti.

· Output in testo normale formattato per OMNet++.

· Utilizzo dell'output del database SQLite, un motore SQL autonomo, leggero e ad alte prestazioni.

· Metadati obbligatori e aperti per la descrizione e l'utilizzo delle esecuzioni.

· Un esempio basato sull'esperimento teorico di esaminare le proprietà di NS-3
prestazioni Wi-Fi ad hoc predefinite. Incorpora quanto segue:

· Costruisce una rete WiFi ad hoc a due nodi, con i nodi a distanza parametrizzata
a parte.

· Applicazioni sorgente e sink di traffico UDP con un comportamento leggermente diverso e
ganci di misura rispetto alle classi di azioni.

· Raccolta dati dal core NS-3 tramite segnali di traccia esistenti, in particolare dati su
frame trasmessi e ricevuti dagli oggetti WiFi MAC.

· Strumentazione di applicazioni personalizzate collegando nuovi segnali di traccia alla stat
framework, nonché tramite aggiornamenti diretti. Le informazioni vengono registrate sui pacchetti totali
inviati e ricevuti, byte trasmessi e ritardo end-to-end.

· Un esempio di utilizzo dei tag di pacchetto per tenere traccia del ritardo end-to-end.

· Un semplice script di controllo che esegue una serie di prove dell'esperimento al variare
distanzia e interroga il database risultante per produrre un grafico usando GNUPlot.

Fare
Gli elementi ad alta priorità includono:

· Inserimento di codici statistici online, ad es. per intervalli di confidenza efficienti in termini di memoria.

· Disposizioni nei raccoglitori di dati per l'interruzione delle esecuzioni, ad es. quando una soglia o
la fiducia è soddisfatta.

· Raccoglitori di dati per la registrazione dei campioni nel tempo e l'output nei vari formati.

· Dimostrare di scrivere una semplice colla per eventi ciclici per ottenere regolarmente un certo valore.

Ciascuno di questi dovrebbe rivelarsi semplice da incorporare nel quadro attuale.

Approccio
Il quadro si basa sui seguenti principi fondamentali:

· Una prova sperimentale è condotta da un'istanza di un programma di simulazione, sia in
in parallelo o in serie.

· Uno script di controllo esegue le istanze della simulazione, variando i parametri secondo necessità.

· I dati vengono raccolti e archiviati per la stampa e l'analisi utilizzando script esterni e
strumenti esistenti.

· Le misure all'interno del core ns-3 sono prese collegando il framework stat a quello esistente
segnali di traccia.

· Segnali di traccia o manipolazione diretta del framework possono essere utilizzati per strumenti personalizzati
codice di simulazione.

Questi componenti di base del framework e le loro interazioni sono illustrati nel
figura seguente. [Immagine]

Esempio
Questa sezione passa attraverso il processo di costruzione di un esperimento nel quadro e
produrre dati per l'analisi (grafici) da esso, dimostrando la struttura e l'API insieme
la via.

La Domanda
''Qual è la prestazione (simulata) dei NetDevices WiFi di ns-3 (usando l'impostazione predefinita
impostazioni)? Quanto possono essere distanti i nodi wireless in una simulazione prima che non possano
comunicare in modo affidabile?''

· Ipotesi: Basandosi sulla conoscenza delle prestazioni della vita reale, i nodi dovrebbero comunicare
ragionevolmente bene ad almeno 100 m di distanza. La comunicazione oltre i 200 m non dovrebbe essere
fattibile.

Sebbene non sia una domanda molto comune nei contesti di simulazione, questa è una proprietà importante
di cui gli sviluppatori di simulazione dovrebbero avere una conoscenza di base. È anche un comune
studio fatto su hardware live.

Simulazione Programma
La prima cosa da fare per implementare questo esperimento è sviluppare la simulazione
programma. Il codice per questo esempio può essere trovato in esempi/statistiche/wifi-esempio-sim.cc.
Esegue i seguenti passaggi principali.

· Dichiarazione dei parametri e analisi della riga di comando utilizzando ns3::Riga di comando.

doppia distanza = 50.0;
formato stringa ("OMNet++");
esperimento di stringhe ("test-distanza-wifi");
strategia delle stringhe ("wifi-default");
stringa runID;

Riga di comando cmd;
cmd.AddValue("distance", "Distanza per posizionare i nodi (in metri).", distanza);
cmd.AddValue("format", "Formato da utilizzare per l'output dei dati.", format);
cmd.AddValue("esperimento", "Identificatore per esperimento.", esperimento);
cmd.AddValue("strategia", "Identificatore per strategia.", strategia);
cmd.AddValue("run", "Identificatore per run.", runID);
cmd.Parse (argc, argv);

· Creazione di nodi e stack di rete utilizzando ns3::ContenitoreNodo, ns3::WiFiHelper e
ns3::InternetStackHelper.

nodi NodeContainer;
nodi.Crea(2);

Wi-FiAiuto Wi-Fi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(nodi);

InternetStackHelper Internet;
internet.Installa(nodi);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(dispositivinodo);

· Posizionamento dei nodi utilizzando ns3::MobilityHelper. Per impostazione predefinita, i nodi sono statici
mobilità e non si muove, ma deve essere posizionato alla distanza data l'uno dall'altro. Ci sono
diversi modi per farlo; è fatto qui usando ns3::ListPositionAllocatore, che disegna
posizioni da una data lista.

MobilitàAiutante mobilità;
pt positionAlloc =
Crea oggetto ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, distance, 0.0));
mobilità.SetPositionAllocator(posizioneAlloc);
mobilità.Installa(nodi);

· Installazione di un generatore di traffico e di un pozzo di traffico. La scorta Applicazioni potrebbe essere
utilizzato, ma l'esempio include oggetti personalizzati in src/test/test02-apps.(cc|h). Queste
hanno un comportamento semplice, generando un dato numero di pacchetti distanziati in un dato intervallo.
Poiché ce n'è solo uno per ciascuno, vengono installati manualmente; per un set più grande il
ns3::ApplicationHelper la classe potrebbe essere utilizzata. Il commentato Config::Imposta cambi di linea
la destinazione dei pacchetti, impostata su broadcast per impostazione predefinita in questo esempio. Notare che
in generale il WiFi potrebbe avere prestazioni diverse per i frame broadcast e unicast a causa di
diverse politiche di controllo della velocità e di ritrasmissione MAC.

pt appSource = NodeList::GetNode(0);
pt mittente = CreaOggetto ();
appSource->AggiungiApplicazione(mittente);
mittente->Avvia(secondi(1));

pt appSink = NodeList::GetNode(1);
pt ricevitore = CreaOggetto ();
appSink->AddApplication(ricevitore);
ricevitore->Avvia(secondi(0));

// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
// Ipv4AddressValue("192.168.0.2"));

· Configurazione dei dati e delle statistiche da raccogliere. Il paradigma di base è che un
ns3::Collettore di dati viene creato per contenere informazioni su questa particolare esecuzione, a
quali osservatori e calcolatori sono collegati per generare effettivamente i dati. È importante sottolineare che
le informazioni sull'esecuzione includono etichette per ''esperimento'', ''strategia'', ''input'' e
''correre''. Questi vengono utilizzati per identificare in seguito e raggruppare facilmente i dati di più prove.

· L'esperimento è lo studio di cui questo studio fa parte. Eccolo in Wi-Fi
prestazioni e distanza.

· La strategia è il codice oi parametri esaminati in questa prova. In questo esempio
è fisso, ma un'estensione ovvia sarebbe quella di indagare su diversi bit WiFi
tariffe, ognuna delle quali sarebbe una strategia diversa.

· L'input è il problema particolare dato a questa prova. Qui è semplicemente il
distanza tra i due nodi.

· Il runID è un identificatore univoco per questa prova con il quale le sue informazioni sono contrassegnate
per l'identificazione in analisi successive. Se non viene fornito alcun ID di esecuzione, il programma di esempio esegue
un (debole) ID di esecuzione che utilizza l'ora corrente.

Questi quattro metadati sono necessari, ma potrebbero essere desiderati di più. Possono essere aggiunti
al record utilizzando il ns3::DataCollector::AddMetadata() metodo.

Dati del collezionista di dati;
data.DescribeRun(esperimento, strategia, input, runID);
data.AddMetadata("autore", "tjkopena");

L'osservazione e il calcolo effettivi sono effettuati da ns3::Calcoladati oggetti, di cui
esistono diversi tipi. Questi sono creati dal programma di simulazione, allegato a
codice di segnalazione o campionamento, e quindi registrato presso il ns3::Collettore di dati così lo faranno
essere interrogati in seguito per il loro output. Un semplice meccanismo di osservazione consiste nell'utilizzare l'esistente
sorgenti di traccia, ad esempio per strumentare oggetti nel core ns-3 senza cambiarli
codice. Qui un contatore è collegato direttamente a un segnale di traccia nel livello MAC WiFi attivo
il nodo di destinazione.

pt totalRx = CreaOggetto ();
totalRx->SetKey("frames wifi-rx");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
dati.AddDataCalculator(totalRx);

Le calcolatrici possono anche essere manipolate direttamente. In questo esempio viene creato un contatore e
passato all'applicazione del sink di traffico per essere aggiornato alla ricezione dei pacchetti.

pt > appRx = CreaOggetto >();
appRx->SetKey("ricevitore-rx-pacchetti");
ricevitore->SetCounter(appRx);
dati.AddDataCalculator(appRx);

Per incrementare il conteggio, il codice di elaborazione dei pacchetti del sink chiama quindi uno dei
metodi di aggiornamento della calcolatrice.

m_calc->Aggiorna();

Il programma include anche molti altri esempi, utilizzando sia la primitiva
calcolatrici come ns3::ContatoreCalcola e quelli adattati per l'osservazione di pacchetti e
volte. In src/test/test02-apps.(cc|h) crea anche un semplice tag personalizzato che utilizza
per tenere traccia del ritardo end-to-end per i pacchetti generati, riportando i risultati a a
ns3::TimeMinMaxAvgTotalCalculator calcolatore di dati.

· Esecuzione della simulazione, che è molto semplice una volta costruita.

Simulatore::Esegui();

· Generazione sia OMNet++ or SQLite output, a seconda degli argomenti della riga di comando. A
fare questo a ns3::DataOutputInterface l'oggetto viene creato e configurato. Il tipo specifico
di questo determinerà il formato di output. A questo oggetto viene quindi assegnato il
ns3::Collettore di dati oggetto che interroga per produrre l'output.

pt produzione;
if (formato == "OMNet++") {
NS_LOG_INFO("Creazione di output dati formattati OMNet++.");
output = CreaOggetto ();
} Else {
# ifdef STAT_USE_DB
NS_LOG_INFO("Creazione output dati formattati SQLite.");
output = CreaOggetto ();
# finisci se
}

output->Output(dati);

· Liberare tutta la memoria utilizzata dalla simulazione. Questo dovrebbe arrivare alla fine del main
funzione per l'esempio.

Simulatore::Distruggi();

Registrazione
Per vedere in dettaglio cosa stanno facendo il programma di esempio, le applicazioni e il framework stat, set
, il NS_LOG variabile in modo appropriato. Quanto segue fornirà un output copioso da tutti
tre.

$ export NS_LOG=EsperimentoWiFiDistance:WiFiDistanceApps

Si noti che questo rallenta straordinariamente la simulazione.

Campione Uscita
Verrà aggiunta la compilazione e la semplice esecuzione del programma di test OMNet++ output formattato come
quanto segue a dati.sca.

corri corri-1212239121

esperimento attr "test-distanza wifi"
strategia attr "wifi-default"
attributo ingresso "50"
attr descrizione ""

attr "autore" "tjkopena"

i frame wifi-tx scalari contano 30
i frame scalari wifi-rx contano 30
i pacchetti scalari mittente-tx contano 30
i pacchetti scalari ricevitore-rx contano 30
conteggio scalare tx-pkt-size 30
scalare tx-pkt-size totale 1920
media scalare tx-pkt-size 64
dimensione scalare tx-pkt max 64
dimensione scalare tx-pkt min 64
conteggio del ritardo scalare 30
ritardo scalare totale 5884980ns
ritardo scalare medio 196166ns
ritardo scalare max 196166ns
ritardo scalare min 196166ns

Controllo Copione
Per automatizzare la raccolta dei dati a una varietà di input (distanze), un semplice Bash
lo script viene utilizzato per eseguire una serie di simulazioni. Si può trovare a
esempi/stats/wifi-example-db.sh. Lo script deve essere eseguito da esempi/statistiche/
directory.

Lo script percorre una serie di distanze, raccogliendo i risultati in un file SQLite
Banca dati. Ad ogni distanza vengono condotte cinque prove per dare un quadro migliore di quanto atteso
prestazione. L'intero esperimento richiede solo poche decine di secondi per essere eseguito su una fascia bassa
macchina in quanto non c'è output durante la simulazione e viene generato poco traffico.

#!/bin/sh

DISTANZE="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
PROVE="1 2 3 4 5"

Esempio di esperimento echo WiFi

se [ -e data.db ]
poi
echo Uccidi data.db?
leggi ANS
if [ "$ANS" = "sì" -o "$ANS" = "y" ]
poi
echo Cancellazione database
rm dati.db
fi
fi

per la prova in $TRIALS
do
per la distanza in $DISTANCES
do
echo Prova $prova, distanza $distanza
./bin/test02 --format=db --distance=$distanza --run=run-$distanza-$trial
fatto
fatto

Analisi e Conclusione
Una volta che tutte le prove sono state condotte, lo script esegue una semplice query SQL sul file
database utilizzando il SQLite programma a riga di comando. La query calcola la perdita media di pacchetti in
ogni serie di prove associate a ciascuna distanza. Non tiene conto del diverso
strategie, ma le informazioni sono presenti nel database per fare delle semplici estensioni
e fallo. I dati raccolti vengono quindi passati a GNUPlot per la rappresentazione grafica.

CMD="select exp.input,avg(100-((rx.value*100)/tx.value)) \
da Singletons rx, Singletons tx, Experiments exp \
dove rx.run = tx.run AND \
rx.run = exp.run AND \
rx.name='ricevitore-rx-pacchetti' AND \
tx.name='pacchetti-mittenti-tx' \
raggruppa per exp.input \
ordine per abs(exp.input) ASC;"

sqlite3 -noheader data.db "$CMD" > wifi-default.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-esempio.gnuplot

Lo script GNUPlot trovato in esempi/stats/wifi-example.gnuplot definisce semplicemente l'output
format e alcune formattazioni di base per il grafico.

impostare il ritratto poscritto terminale migliorato lw 2 "Helvetica" 14

impostare la dimensione 1.0, 0.66

#------------------------------------------------ ------
impostare "wifi-default.eps"
#set titolo "Perdita di pacchetti a distanza"
set xlabel "Distanza (m) --- media di 5 prove per punto"
imposta xrange [0:200]
set ylabel "% di perdita di pacchetti"
imposta yrange [0:110]

traccia "wifi-default.data" con il titolo delle righe "WiFi Defaults"

Fine Risultato
Il grafico risultante non fornisce alcuna prova che le prestazioni del modello WiFi predefinito lo siano
necessariamente irragionevole e presta una certa fiducia a una fedeltà almeno simbolica
realtà. Ancora più importante, questa semplice indagine è stata portata avanti fino in fondo
utilizzando il quadro statistico. Successo! [Immagine]

Tempo reale
NS-3 è stato progettato per l'integrazione in ambienti di testbed e macchine virtuali. Per
integrarsi con stack di rete reali ed emettere/consumare pacchetti, è uno scheduler in tempo reale
necessario per provare a bloccare l'orologio di simulazione con l'orologio hardware. Descriviamo qui a
componente di questo: lo scheduler RealTime.

Lo scopo dello scheduler in tempo reale è quello di causare la progressione dell'orologio di simulazione
avvenire in modo sincrono rispetto a una base temporale esterna. Senza la presenza di
una base temporale esterna (orologio da parete), il tempo di simulazione salta istantaneamente da quello simulato
tempo al prossimo.

Comportamento
Quando si utilizza uno scheduler non in tempo reale (l'impostazione predefinita in NS-3), il simulatore avanza il
tempo di simulazione al prossimo evento programmato. Durante l'esecuzione dell'evento, il tempo di simulazione è
congelato. Con lo scheduler in tempo reale, il comportamento è simile dal punto di vista di
modelli di simulazione (ovvero, il tempo di simulazione è congelato durante l'esecuzione dell'evento), ma tra
eventi, il simulatore tenterà di mantenere l'orologio di simulazione allineato con la macchina
orologio.

Al termine dell'esecuzione di un evento e lo scheduler passa all'evento successivo, il
lo scheduler confronta il tempo di esecuzione dell'evento successivo con l'orologio della macchina. Se il prossimo
l'evento è programmato per un'ora futura, il simulatore rimane inattivo fino al raggiungimento dell'ora reale
e quindi esegue l'evento successivo.

Può accadere che, a causa dell'elaborazione inerente all'esecuzione degli eventi di simulazione,
che il simulatore non riesce a stare al passo con il tempo reale. In tal caso, spetta all'utente
configurazione cosa fare. Ci sono due NS-3 attributi che regolano il comportamento. Il
il primo è ns3::RealTimeSimulatorImpl::SynchronizationMode. Le due voci possibili per
questo attributo sono Miglior sforzo (predefinito) o Limite difficile. In modalità "BestEffort", il
simulator cercherà semplicemente di recuperare il ritardo in tempo reale eseguendo eventi fino a raggiungere un
punto in cui l'evento successivo è nel futuro (in tempo reale), oppure la simulazione termina. In
In modalità BestEffort, quindi, è possibile che la simulazione consumi più tempo rispetto al
ora dell'orologio da parete. L'altra opzione "HardLimit" causerà l'interruzione della simulazione se il
soglia di tolleranza superata. Questo attributo è ns3::RealTimeSimulatorImpl::HardLimit
e il valore predefinito è 0.1 secondi.

Una diversa modalità di funzionamento è quella in cui si trova il tempo simulato non è un congelato durante un evento
esecuzione. Questa modalità di simulazione in tempo reale è stata implementata ma rimossa dal NS-3 albero
a causa della domanda se sarebbe utile. Se gli utenti sono interessati in tempo reale
simulatore per il quale il tempo di simulazione non si blocca durante l'esecuzione dell'evento (ovvero ogni
chiama a Simulatore::Ora() restituisce l'ora corrente dell'orologio da parete, non l'ora in cui il
evento iniziato in esecuzione), contattare la mailing list di ns-developers.

Impiego
L'utilizzo del simulatore in tempo reale è semplice, dal punto di vista dello scripting.
Gli utenti devono solo impostare l'attributo SimulatorImplementazioneType al tempo reale
simulatore, come segue:

GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl"));

C'è un copione esempi/realtime/realtime-udp-echo.cc che ha un esempio di come
configurare il comportamento in tempo reale. Provare:

$ ./waf --run realtime-udp-echo

È governato se il simulatore funzionerà al meglio o in modo rigido
dagli attributi spiegati nella sezione precedente.

Implementazione/Attuazione
L'implementazione è contenuta nei seguenti file:

· src/core/modello/realtime-simulator-impl.{cc,h}

· src/core/modello/sincronizzatore-orologio-da-parete.{cc,h}

Per creare uno scheduler in tempo reale, in prima approssimazione vuoi solo causare
il tempo di simulazione salta per consumare il tempo reale. Proponiamo di farlo usando una combinazione di
sonno-e occupato- aspetta. Le attese di sospensione fanno sì che il processo di chiamata (thread) produca il file
processore per un certo periodo di tempo. Anche se è possibile passare questo periodo di tempo specificato
alla risoluzione di nanosecondi, viene effettivamente convertito in una granularità specifica del sistema operativo. In
Linux, la granularità è chiamata Jiffy. In genere questa risoluzione è insufficiente per
le nostre esigenze (dell'ordine di dieci millisecondi), quindi arrotondiamo per difetto e dormiamo per alcuni
minor numero di Jiffies. Il processo viene quindi risvegliato dopo il numero specificato di
Jiffies è passato. In questo momento, abbiamo un po' di tempo residuo da aspettare. Questa volta lo è
generalmente inferiore al tempo di sonno minimo, quindi attendiamo impegnati per il resto del
volta. Ciò significa che il thread si trova in un ciclo che consuma cicli fino al
arriva l'ora desiderata. Dopo la combinazione di sonno e attesa, il tempo reale è trascorso
L'orologio (a parete) dovrebbe concordare con l'ora di simulazione dell'evento successivo e della simulazione
provento.

Helpers
I capitoli precedenti ti hanno presentato vari NS-3 concetti di programmazione come smart
puntatori per la gestione della memoria conteggiata per riferimento, attributi, spazi dei nomi, callback, ecc.
Gli utenti che lavorano con questa API di basso livello possono interconnettersi NS-3 oggetti con granulosità fine.
Tuttavia, un programma di simulazione scritto interamente utilizzando l'API di basso livello sarebbe piuttosto lungo
e noioso da codificare. Per questo motivo, è stata sovrapposta una cosiddetta "API di supporto" separata
sul nucleo NS-3 API. Se hai letto il NS-3 tutorial, avrai già familiarità
con l'API di supporto, poiché è l'API a cui in genere vengono presentati per primi i nuovi utenti.
In questo capitolo, introduciamo la filosofia di progettazione dell'API di supporto e la contrapponiamo a
l'API di basso livello. Se diventi un utente pesante di NS-3, probabilmente ti sposterai avanti e indietro
tra queste API anche nello stesso programma.

L'API di supporto ha alcuni obiettivi:

1. il resto src / non ha dipendenze dall'API di supporto; tutto ciò che può essere fatto con
l'API di supporto può essere codificata anche nell'API di basso livello

2. Contenitore: Spesso le simulazioni dovranno eseguire una serie di azioni identiche ai gruppi
di oggetti. L'API di supporto fa un uso massiccio di contenitori di oggetti simili a cui
possono essere eseguite operazioni simili o identiche.

3. L'API di supporto non è generica; non si sforza di massimizzare il riutilizzo del codice. Così,
lo sono costrutti di programmazione come il polimorfismo e i modelli che consentono il riutilizzo del codice
non così prevalente. Ad esempio, ci sono helper CsmaNetDevice separati e
PointToPointNetDevice helper ma non derivano da una base NetDevice comune
classe.

4. L'API di supporto in genere funziona con oggetti allocati nello stack (rispetto agli oggetti allocati nell'heap). Per
alcuni programmi, NS-3 gli utenti potrebbero non doversi preoccupare di creare oggetti di basso livello o
Ptr manipolazione; possono accontentarsi di contenitori di oggetti e helper allocati nello stack
che operano su di essi.

L'API di supporto riguarda davvero la creazione NS-3 programmi più facili da scrivere e leggere, senza
togliendo il potere dell'interfaccia di basso livello. Il resto di questo capitolo ne fornisce alcuni
esempi delle convenzioni di programmazione dell'API di supporto.

Fare Terreni utilizzando , il gnuplot Classe
Esistono 2 metodi comuni per creare una trama utilizzando NS-3 e gnuplot (‐
http://www.gnuplot.info):

1. Crea un file di controllo gnuplot usando NS-3è la classe Gnuplot.

2. Crea un file di dati gnuplot usando i valori generati da NS-3.

Questa sezione riguarda il metodo 1, ovvero come creare una trama utilizzando NS-3è Gnuplot
classe. Se sei interessato al metodo 2, consulta la sottosezione "Un esempio reale" sotto il
Sezione "Tracciamento" nel NS-3 Esercitazione.

Creazione Terreni utilizzando , il gnuplot Classe
È necessario eseguire i seguenti passaggi per creare una trama utilizzando NS-3Classe Gnuplot:

1. Modifica il tuo codice in modo che utilizzi la classe Gnuplot e le sue funzioni.

2. Esegui il tuo codice in modo che crei un file di controllo gnuplot.

3. Chiamate gnuplot con il nome del file di controllo di gnuplot.

4. Visualizza il file grafico che è stato prodotto nel tuo visualizzatore grafico preferito.

Per i dettagli sul passaggio 1, vedere il codice dei grafici di esempio discussi di seguito.

An Esempio Programma che si utilizza , il gnuplot Classe
Un programma di esempio che utilizza NS-3La classe Gnuplot può essere trovata qui:

src/stats/examples/gnuplot-example.cc

Per eseguire questo esempio, procedere come segue:

$ ./conchiglia
$ cd build/debug/src/stats/examples
$ ./gnuplot-esempio

Questo dovrebbe produrre i seguenti file di controllo gnuplot nella directory in cui l'esempio
si trova:

trama-2d.plt
plot-2d-con-barre-di-errore.plt
trama-3d.plt

Per elaborare questi file di controllo di gnuplot, procedi come segue:

$ gnuplot plot-2d.plt
$ gnuplot plot-2d-con-barre-di-errore.plt
$ gnuplot plot-3d.plt

Questo dovrebbe produrre i seguenti file grafici nella directory in cui si trova l'esempio
situato:

trama-2d.png
plot-2d-con-barre-di-errore.png
trama-3d.png

Puoi visualizzare questi file grafici nel tuo visualizzatore grafico preferito. Se hai gimp
installato sulla tua macchina, ad esempio, puoi fare questo:

$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png

An Esempio 2 dimensionale Parcella
La seguente trama bidimensionale
[immagine]

è stato creato utilizzando il seguente codice da gnuplot-example.cc:

usando lo spazio dei nomi std;

stringa fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Plogramma 2-D";
stringa dataTitle = "Dati 2D";

// Crea un'istanza della trama e imposta il titolo.
trama Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);

// Crea il file grafico, che il file di stampa creerà quando lo farà
// è usato con Gnuplot, essere un file PNG.
plot.SetTerminal ("png");

// Imposta le etichette per ogni asse.
plot.SetLegend ("Valori X", "Valori Y");

// Imposta l'intervallo per l'asse x.
plot.AppendExtra ("imposta xrange [-6:+6]");

// Crea un'istanza del set di dati, imposta il suo titolo e imposta i punti
// tracciato insieme alle linee di collegamento.
Gnuplot2dDataset set di dati;
dataset.SetTitle (dataTitolo);
dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);

doppia x;
doppio y;

// Crea il set di dati 2D.
per (x = -5.0; x <= +5.0; x += 1.0)
{
// Calcola la curva 2D
//
// 2
//y = x.
//
y = x*x;

// Aggiungi questo punto.
set di dati.Aggiungi (x, y);
}

// Aggiunge il set di dati al grafico.
plot.AddDataset (set di dati);

// Apre il file di stampa.
ofstream plotFile (plotFileName.c_str());

// Scrivi il file di stampa.
plot.GenerateOutput (plotFile);

// Chiude il file di stampa.
tracciaFile.close ();

An Esempio 2 dimensionale Parcella con Errore I bar
Il seguente grafico bidimensionale con barre di errore nelle direzioni xey
[immagine]

è stato creato utilizzando il seguente codice da gnuplot-example.cc:

usando lo spazio dei nomi std;

string fileNameWithNoExtension = "plot-2d-with-error-bars";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Grafico 2D con barre di errore";
string dataTitle = "Dati 2D con barre di errore";

// Crea un'istanza della trama e imposta il titolo.
trama Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);

// Crea il file grafico, che il file di stampa creerà quando lo farà
// è usato con Gnuplot, essere un file PNG.
plot.SetTerminal ("png");

// Imposta le etichette per ogni asse.
plot.SetLegend ("Valori X", "Valori Y");

// Imposta l'intervallo per l'asse x.
plot.AppendExtra ("imposta xrange [-6:+6]");

// Crea un'istanza del set di dati, imposta il suo titolo e imposta i punti
// tracciato senza linee di collegamento.
Gnuplot2dDataset set di dati;
dataset.SetTitle (dataTitolo);
dataset.SetStyle (Gnuplot2dDataset::POINTS);

// Fai in modo che il set di dati abbia barre di errore in entrambe le direzioni x e y.
dataset.SetErrorBars (Gnuplot2dDataset::XY);

doppia x;
doppio xErrorDelta;
doppio y;
doppio yErrorDelta;

// Crea il set di dati 2D.
per (x = -5.0; x <= +5.0; x += 1.0)
{
// Calcola la curva 2D
//
// 2
//y = x.
//
y = x*x;

// Rendi costante l'incertezza nella direzione x e crea
// l'incertezza nella direzione y è una frazione costante di
// il valore di y.
xDelta errore = 0.25;
yDelta errore = 0.1 * y;

// Aggiungi questo punto con incertezze sia in x che in y
// direzione.
set di dati.Aggiungi (x, y, xErrorDelta, yErrorDelta);
}

// Aggiunge il set di dati al grafico.
plot.AddDataset (set di dati);

// Apre il file di stampa.
ofstream plotFile (plotFileName.c_str());

// Scrivi il file di stampa.
plot.GenerateOutput (plotFile);

// Chiude il file di stampa.
tracciaFile.close ();

An Esempio 3 dimensionale Parcella
La seguente trama bidimensionale
[immagine]

è stato creato utilizzando il seguente codice da gnuplot-example.cc:

usando lo spazio dei nomi std;

stringa fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Plogramma 3-D";
stringa dataTitle = "Dati 3D";

// Crea un'istanza della trama e imposta il titolo.
trama Gnuplot (graphicsFileName);
plot.SetTitle (plotTitle);

// Crea il file grafico, che il file di stampa creerà quando lo farà
// è usato con Gnuplot, essere un file PNG.
plot.SetTerminal ("png");

// Ruota il grafico di 30 gradi attorno all'asse x, quindi ruota il
// traccia 120 gradi attorno al nuovo asse z.
plot.AppendExtra ("imposta vista 30, 120, 1.0, 1.0");

// Fai in modo che lo zero per l'asse z sia nel piano dell'asse x e dell'asse y.
plot.AppendExtra ("imposta ticslevel 0");

// Imposta le etichette per ogni asse.
plot.AppendExtra ("imposta xlabel 'Valori X'");
plot.AppendExtra ("imposta ylabel 'Y Values'");
plot.AppendExtra ("set zlabel 'Z Values'");

// Imposta gli intervalli per gli assi xey.
plot.AppendExtra ("imposta xrange [-5:+5]");
plot.AppendExtra ("imposta yrange [-5:+5]");

// Crea un'istanza del set di dati, imposta il suo titolo e imposta i punti
// collegato da linee.
Gnuplot3dDataset set di dati;
dataset.SetTitle (dataTitolo);
dataset.SetStyle ("con linee");

doppia x;
doppio y;
doppia z;

// Crea il set di dati 3D.
per (x = -5.0; x <= +5.0; x += 1.0)
{
per (y = -5.0; y <= +5.0; y += 1.0)
{
// Calcola la superficie 3D
//
// 2
// z = x * y .
//
z = x * x * y * y;

// Aggiungi questo punto.
set di dati.Aggiungi (x, y, z);
}

// La riga vuota è necessaria alla fine dei dati di ogni valore x
// punti per far funzionare la griglia della superficie 3D.
set di dati.AddEmptyLine ();
}

// Aggiunge il set di dati al grafico.
plot.AddDataset (set di dati);

// Apre il file di stampa.
ofstream plotFile (plotFileName.c_str());

// Scrivi il file di stampa.
plot.GenerateOutput (plotFile);

// Chiude il file di stampa.
tracciaFile.close ();

utilizzando Python a Correre NS-3
I collegamenti Python consentono l'inserimento del codice C++ NS-3 essere chiamato da Python.

Questo capitolo mostra come creare uno script Python che può essere eseguito NS-3 e anche la
processo di creazione di collegamenti Python per un C++ NS-3 modulo.

Introduzione
L'obiettivo dei collegamenti Python per NS-3 sono due volte:

1. Consenti al programmatore di scrivere script di simulazione completi in Python (‐
http://www.python.org);

2. Prototipare nuovi modelli (es. protocolli di routing).

Per il momento, l'obiettivo principale degli attacchi è il primo obiettivo, ma il secondo
anche l'obiettivo alla fine sarà supportato. Collegamenti Python per NS-3 sono in fase di sviluppo
utilizzando un nuovo strumento chiamato PyBindGen (http://code.google.com/p/pybindgen).

An Esempio Python Copione che Esegue NS-3
Ecco un esempio di codice scritto in Python e che viene eseguito NS-3, che è scritto
in C++. Questo esempio di Python può essere trovato in esempi/tutorial/first.py:

importare ns.applicazioni
importa ns.core
importa ns.internet
importa ns.network
importa ns.punto_punto

ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)

nodi = ns.network.NodeContainer()
nodi.Crea(2)

puntoToPunto = ns.punto_punto.PuntoToPuntoHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))

dispositivi = pointToPoint.Install(nodi)

pila = ns.internet.InternetStackHelper()
stack.Install(nodi)

indirizzo = ns.internet.Ipv4AddressHelper()
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))

interfacce = indirizzo.Assegna (dispositivi);

ecoserver = ns.applications.UdpEchoServerHelper(9)

serverApps = echoServer.Install(nodi.Ottieni(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))

echoClient = ns.applications.UdpEchoClientHelper(interfacce.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))

clientApps = echoClient.Install(nodi.Ottieni(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))

ns.core.Simulator.Run()
ns.core.Simulator.Destroy()

corsa Python Script
waf contiene alcune opzioni che aggiornano automaticamente il percorso python per trovare ns3
modulo. Per eseguire programmi di esempio, ci sono due modi per utilizzare waf per occuparsi di questo. Uno
è eseguire una shell waf; per esempio:

$ ./waf --shell
$ python esempi/wireless/mixed-wireless.py

e l'altro è usare l'opzione --pyrun per waf:

$ ./waf --pyrun esempi/wireless/mixed-wireless.py

Per eseguire uno script Python con il debugger C:

$ ./waf --shell
$ gdb --args python esempi/wireless/mixed-wireless.py

Per eseguire il tuo script Python che chiama NS-3 e che ha questo percorso,
/percorso/del/tuo/esempio/mio-script.py, Fare quanto segue:

$ ./waf --shell
$ python /percorso/del/tuo/esempio/mio-script.py

Avvertenze
Collegamenti Python per NS-3 sono un lavoro in corso e alcune limitazioni sono note
sviluppatori. Alcune di queste limitazioni (non tutte) sono elencate qui.

Incompleto Copertura
Prima di tutto, tieni presente che non il 100% delle API è supportato in Python. Alcuni dei
i motivi sono:

1. alcune delle API coinvolgono puntatori, che richiedono la conoscenza di quale tipo di memoria
semantica di passaggio (chi possiede quale memoria). Tale conoscenza non fa parte della funzione
firme, ed è documentato o talvolta nemmeno documentato. Le annotazioni sono
necessario per vincolare tali funzioni;

2. A volte viene utilizzato un tipo di dati fondamentale insolito o un costrutto C++ che non lo è ancora
supportato da PyBindGen;

3. GCC-XML non riporta le classi basate su modelli a meno che non siano istanziate.

È possibile eseguire il wrapping della maggior parte delle API mancanti, con tempo, pazienza ed esperienza sufficienti e
sarà probabilmente avvolto se vengono inviate segnalazioni di bug. Tuttavia, non presentare una segnalazione di bug
dicendo "le rilegature sono incomplete", perché non abbiamo manodopera per completare il 100% del
legature.

Conversione Costruttori
Conversione costruttori non sono ancora completamente supportati da PyBindGen e agiscono sempre come
costruttori espliciti durante la traduzione di un'API in Python. Ad esempio, in C++ puoi fare
Questo:

Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);

In Python, per il momento devi fare:

ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(dispositivi backbone)

Riga di comando
Riga di comando::AddValue() funziona in modo diverso in Python rispetto a NS-3. In Python, il
il primo parametro è una stringa che rappresenta il nome dell'opzione della riga di comando. Quando l'opzione
è impostato, viene impostato un attributo con lo stesso nome del nome dell'opzione Riga di comando()
oggetto. Esempio:

NUM_NODES_SIDE_DEFAULT = 3

cmd = ns3.RigaCommand()

cmd.NumNodesSide = Nessuno
cmd.AddValue("NumNodesSide", "Numero di nodi lato griglia (il numero totale di nodi sarà questo numero al quadrato)")

cmd.Parse(argv)

[...]

se cmd.NumNodesSide è Nessuno:
num_nodi_lato = NUM_NODES_SIDE_DEFAULT
altro:
num_nodes_side = int(cmd.NumNodesSide)

Tracciato
La traccia basata su callback non è ancora supportata correttamente per Python, come nuova NS-3 L'API deve farlo
essere previsto affinché questo sia supportato.

La scrittura di file Pcap è supportata tramite la normale API.

La traccia Ascii è supportata da allora NS-3.4 tramite la normale API C++ tradotta in Python.
Tuttavia, la traccia ascii richiede la creazione di un oggetto ostream da passare nell'ascii
metodi di tracciamento. In Python, il C++ std::ofstream è stato avvolto minimamente per consentire
questo. Per esempio:

ascii = ns3.ofstream("wifi-ap.tr") # crea il file
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # chiude il file

C'è un avvertimento: non devi consentire che l'oggetto file venga raccolto durante la raccolta dei rifiuti NS-3
lo sta ancora usando. Ciò significa che la variabile 'ascii' sopra non deve essere consentita
fuori portata, altrimenti il ​​programma andrà in crash.

Cygwin limitazione
I collegamenti Python non funzionano su Cygwin. Ciò è dovuto a un bug di gccxml.

Potresti farla franca eseguendo nuovamente la scansione delle definizioni API dall'interno di cygwin
ambiente (./waf --python-scan). Tuttavia, la soluzione più probabile dovrà probabilmente farlo
sia che disabilitiamo i collegamenti Python in CygWin.

Se ti interessano davvero i collegamenti Python su Windows, prova a creare con mingw e native
Python invece. Oppure, per compilare senza collegamenti python, disabilitare i collegamenti python nel file
fase di configurazione:

$ ./waf configure --disable-python

lavoro con Python Associazioni
Attualmente ci sono due tipi di binding Python in NS-3:

1. I collegamenti monolitici contengono le definizioni API per tutti i moduli e possono essere trovati in
un'unica directory, attacchi/python.

2. I collegamenti modulari contengono le definizioni API per un singolo modulo e possono essere trovati in ciascuno
moduli attacchi directory.

Python Associazioni Workflow
Il processo mediante il quale vengono gestiti i collegamenti Python è il seguente:

1. Periodicamente uno sviluppatore utilizza un GCC-XML (http://www.gccxml.org) scansione API basata
script, che salva la definizione API scansionata come binding/python/ns3_module_*.py file
o come file Python in ogni modulo attacchi directory. Questi file sono mantenuti sotto
controllo della versione nel main NS-3 deposito;

2. Altri sviluppatori clonano il repository e utilizzano le definizioni API già scansionate;

3. Durante la configurazione NS-3, pybindgen verrà scaricato automaticamente se non già
installato. Rilasciato NS-3 tarballs spedirà una copia di pybindgen.

Se qualcosa va storto con la compilazione dei collegamenti Python e vuoi semplicemente ignorarli
e vai avanti con C++, puoi disabilitare Python con:

$ ./waf --disable-python

Istruzioni per Manovrabilità Nuovo File or Cambiato API
Quindi hai cambiato esistente NS-3 Le API e i collegamenti Python non vengono più compilati? Fare
non disperare, puoi scansionare nuovamente le associazioni per creare nuove associazioni che riflettano le modifiche
ai NS-3 API.

A seconda se si utilizzano rilegature monolitiche o modulari, vedere le discussioni di seguito per
scopri come scansionare nuovamente i tuoi binding Python.

Monolitico Python Associazioni
Scansione , il Monolitico Python Associazioni
Per scansionare i collegamenti Python monolitici, procedi come segue:

$ ./waf --python-scan

Organizzazione of , il Monolitico Python Associazioni
Le definizioni monolitiche dell'API Python sono organizzate come segue. Per ciascuno NS-3 modulo
, il file binding/python/ns3_module_ .py descrive la sua API. Ognuno di quelli
i file hanno 3 funzioni di primo livello:

1. def registri_tipi(modulo)(): questa funzione si occupa di registrare nuove tipologie (es
classi C++, enum) che sono definiti nel modulo;

2. def registri_metodi(modulo)(): questa funzione chiama, per ogni classe , altro
funzione register_methods_Ns3 (modulo). Queste ultime funzioni aggiungono il metodo
definizioni per ogni classe;

3. def funzioni_di_registro(modulo)(): questa funzione registra NS-3 funzioni a cui appartengono
quel modulo.

componibile Python Associazioni
Panoramica
A partire dalla ns 3.11 si stanno aggiungendo le legature modulari, parallelamente al vecchio monolitico
legature.

I nuovi collegamenti Python vengono generati in uno spazio dei nomi 'ns', invece di 'ns3' per il vecchio
legature. Esempio:

da ns.network import Node
n1 = Nodo()

Con collegamenti Python modulari:

1. C'è un modulo di estensione Python separato per ciascuno NS-3 modulo;

2. La scansione delle definizioni API (apidefs) viene eseguita su base per ns-modulo;

3. I file apidefs di ogni modulo sono archiviati in una sottodirectory 'bindings' del modulo
elenco;

Scansione , il componibile Python Associazioni
Per eseguire la scansione dei collegamenti Python modulari per il modulo principale, ad esempio, procedere come segue:

$ ./waf --apiscan=core

Per scansionare i collegamenti Python modulari per tutti i moduli, procedi come segue:

$ ./waf --apiscan=tutto

Creazione a Nuovo Moduli
Se stai aggiungendo un nuovo modulo, i collegamenti Python continueranno a essere compilati ma non lo faranno
coprire il nuovo modulo.

Per coprire un nuovo modulo, devi creare a binding/python/ns3_module_ .py file,
simile a quanto descritto nelle sezioni precedenti, e registrarlo nella variabile
LOCALE_MODULI() in binding/python/ns3modulegen.py

Aggiunta componibile Associazioni A A Esistente Moduli
Per aggiungere il supporto per le associazioni modulari a un esistente NS-3 modulo, aggiungi semplicemente quanto segue
riga alla sua funzione wscript build():

bld.ns3_python_bindings()

Organizzazione of , il componibile Python Associazioni
Il fonte/ /legami directory può contenere i seguenti file, alcuni dei quali
facoltativo:

· callbacks_list.py: questo è un file scansionato, NON TOCCARE. Contiene un elenco di
Callback<...> istanze del modello trovate nelle intestazioni scansionate;

· modulegen__gcc_LP64.py: questo è un file scansionato, NON TOCCARE. Definizioni API scansionate
per GCC, architettura LP64 (64 bit)

· modulegen__gcc_ILP32.py: questo è un file scansionato, NON TOCCARE. Definizioni API scansionate
per GCC, architettura ILP32 (32 bit)

· modulegen_customizations.py: puoi opzionalmente aggiungere questo file per personalizzare il file
generazione del codice pybindgen

· scansione-intestazione.h: puoi facoltativamente aggiungere questo file per personalizzare quale file di intestazione viene scansionato
per il modulo. Fondamentalmente questo file viene scansionato invece di ns3/ -modulo.h.
Tipicamente, la prima istruzione è #include "ns3/ -module.h", più qualche altro
roba per forzare le istanze del modello;

· modulo_helpers.cc: puoi aggiungere file aggiuntivi, come questo, da collegare a python
modulo di estensione, ma devono essere registrati nel wscript. Guarda a
src/core/wscript per un esempio di come farlo;

· .py: se questo file esiste, diventa il modulo python "frontend" per ns3
module e il modulo di estensione (file .so) diventa _ .so invece di .Così.
Il Il file .py deve importare tutti i simboli dal modulo _ (questo è di più
complicato di quanto sembri, vedi src/core/bindings/core.py per un esempio), e poi puoi aggiungere
alcune definizioni aggiuntive di puro pitone.

altro Informazioni per Sviluppatori
Se sei uno sviluppatore e hai bisogno di maggiori informazioni su NS-3's Python binding, vedere il file
Python Associazioni wiki pagina.

Test
Panoramica
Questo documento riguarda il test e la convalida di NS-3 Software.

Questo documento fornisce

· background sulla terminologia e test del software (Capitolo 2);

· una descrizione del framework di test ns-3 (Capitolo 3);

· una guida per sviluppatori di modelli o nuovi contributori di modelli su come scrivere i test (Capitolo
4);

In breve, i primi tre capitoli dovrebbero essere letti dagli sviluppatori e dai contributori di ns che
è necessario capire come contribuire al codice di test e ai programmi convalidati e il resto
del documento offre spazio alle persone per riferire su quali aspetti dei modelli selezionati
sono stati convalidati.

sfondo
Si capitolo può be saltato by lettori familiare con , il "Basics" of Software test.

Scrivere un software privo di difetti è una proposta difficile. Ci sono molte dimensioni per il
problema e c'è molta confusione riguardo a cosa si intenda per termini diversi in
contesti diversi. Abbiamo ritenuto utile dedicare un po' di tempo alla revisione del
soggetto e definire alcuni termini.

Il test del software può essere vagamente definito come il processo di esecuzione di un programma con il
intenzione di trovare errori. Quando si entra in una discussione relativa al test del software, esso
diventa subito evidente che ci sono molte mentalità distinte con cui si può
avvicinarsi all'argomento.

Ad esempio, si potrebbe suddividere il processo in ampie categorie funzionali come
''test di correttezza'', ''test delle prestazioni'', ''test di robustezza'' e ''sicurezza
testing.'' Un altro modo per esaminare il problema è per ciclo di vita: ''requirements testing,''
"test di progettazione", "test di accettazione" e "test di manutenzione". Ancora un'altra vista
rientra nell'ambito del sistema testato. In questo caso si può parlare di ''unit test''
''test dei componenti'', ''test dell'integrazione'' e ''test del sistema''. Questi termini sono
anche non standardizzato in alcun modo, e quindi ''test di manutenzione'' e ''regressione
testing'' può essere ascoltato in modo intercambiabile. Inoltre, questi termini sono spesso usati in modo improprio.

Esistono anche diversi approcci filosofici al test del software. Per
Ad esempio, alcune organizzazioni consigliano di scrivere programmi di test prima di implementarli effettivamente
il software desiderato, ottenendo uno "sviluppo basato su test". Alcune organizzazioni sostengono
test dal punto di vista del cliente il prima possibile, seguendo un parallelo con il
processo di sviluppo agile: ''testare presto e testare spesso''. Questo è talvolta chiamato
''test agile''. Sembra che ci sia almeno un approccio al test per ciascuno
metodologia di sviluppo.

Il NS-3 il progetto non è nel business di sostenere nessuno di questi processi, ma
il progetto nel suo insieme ha requisiti che aiutano a informare il processo di test.

Come tutti i principali prodotti software, NS-3 ha una serie di qualità per le quali deve essere presente
il prodotto per avere successo. Dal punto di vista del test, alcune di queste qualità devono essere
affrontato sono quello NS-3 deve essere ''corretto'', ''robusto'', ''performante'' e
''manutenibile''. Idealmente dovrebbero esserci metriche per ciascuna di queste dimensioni che sono
controllato dai test per identificare quando il prodotto non soddisfa le sue aspettative /
requisiti.

Correttezza
Lo scopo essenziale del test è determinare il comportamento di un pezzo di software
''correttamente.'' Per NS-3 questo significa che se simuliamo qualcosa, la simulazione dovrebbe
rappresentare fedelmente un'entità fisica o un processo con una precisione specificata e
precisione.

Si scopre che ci sono due prospettive da cui si può vedere la correttezza.
Verificare che un particolare modello sia implementato secondo le sue specifiche è
genericamente chiamato verifica. Il processo per decidere se il modello è corretto
la sua destinazione d'uso è genericamente chiamata convalida.

Convalida e Convalida
Un modello al computer è una rappresentazione matematica o logica di qualcosa. Può
rappresentare un veicolo, un elefante (vedi David di Harel parlare circa modellismo an elefante at
SIMUTools 2009o una scheda di rete. I modelli possono anche rappresentare processi come globali
riscaldamento, flusso di traffico autostradale o una specifica di un protocollo di rete. I modelli possono essere
rappresentazioni del tutto fedeli di una specificazione di un processo logico, ma essi
necessariamente non può mai simulare completamente un oggetto fisico o un processo. Nella maggior parte dei casi, a
numero di semplificazioni vengono apportate al modello per eseguire la simulazione a livello computazionale
trattabile.

Ogni modello ha un bersaglio sistema che sta tentando di simulare. Il primo passo avanti
la creazione di un modello di simulazione è identificare questo sistema target e il livello di dettaglio e
precisione che si desidera riprodurre con la simulazione. Nel caso di un processo logico,
il sistema di destinazione può essere identificato come ''TCP come definito da RFC 793.'' In questo caso, esso
sarà probabilmente auspicabile creare un modello che riproduca completamente e fedelmente RFC
793. Nel caso di un processo fisico ciò non sarà possibile. Se, per esempio, tu
vorresti simulare una scheda di rete wireless, potresti determinare che hai bisogno di "an
accurata implementazione a livello MAC della specifica 802.11 e [...] non troppo lenta
Modello di livello PHY della specifica 802.11a.''

Una volta fatto ciò, si può sviluppare un modello astratto del sistema di destinazione. Questo è
tipicamente un esercizio nella gestione dei compromessi tra complessità e fabbisogno di risorse
e precisione. Il processo di sviluppo di un modello astratto è stato chiamato modello
maglieria in nella letteratura. Nel caso di un protocollo TCP, questo processo si traduce in a
design per una raccolta di oggetti, interazioni e comportamenti che verranno pienamente implementati
RFC 793 a NS-3. Nel caso della scheda wireless, questo processo si traduce in un numero di
compromessi per consentire la simulazione del livello fisico e la progettazione di un dispositivo di rete
e canale per ns-3, insieme agli oggetti, alle interazioni e ai comportamenti desiderati.

Questo modello astratto viene poi sviluppato in un NS-3 modello che implementa l'astratto
modello come un programma per computer. Il processo per far sì che l'implementazione sia d'accordo con il
viene chiamato modello astratto modello verifica nella letteratura.

Il processo finora è ad anello aperto. Ciò che resta da fare è determinare che un dato ns-3
il modello ha qualche connessione con una certa realtà -- di cui un modello è una rappresentazione accurata
un sistema reale, sia esso un processo logico o un'entità fisica.

Se si utilizza un modello di simulazione per provare a prevedere come sta andando un sistema reale
per comportarsi, ci deve essere qualche ragione per credere ai tuoi risultati, ad esempio, ci si può fidare di questo
un'inferenza ricavata dal modello si traduce in una corretta previsione per il sistema reale.
Il processo per far sì che il comportamento del modello ns-3 sia d'accordo con il sistema di destinazione desiderato
viene chiamato il comportamento definito dal processo di qualificazione del modello modello convalida nel
letteratura. Nel caso di un'implementazione TCP, potresti voler confrontare il comportamento di
il tuo modello TCP ns-3 ad alcune implementazioni di riferimento per convalidare il tuo modello. In
nel caso di una simulazione di livello fisico wireless, potresti voler confrontare il comportamento di
il tuo modello a quello di hardware reale in un ambiente controllato,

Il NS-3 ambiente di test fornisce strumenti per consentire sia la convalida del modello che
test e incoraggia la pubblicazione dei risultati della convalida.

Robustezza
La robustezza è la qualità di poter resistere a sollecitazioni, o cambiamenti negli ambienti,
input o calcoli, ecc. Un sistema o un progetto è "robusto" se può gestirlo
modifiche con una minima perdita di funzionalità.

Questo tipo di test viene solitamente eseguito con un focus particolare. Ad esempio, il sistema come
un intero può essere eseguito su molte diverse configurazioni di sistema per dimostrare che è possibile
funzionare correttamente in un gran numero di ambienti.

Il sistema può anche essere sollecitato operando vicino o oltre la capacità generata
o simulare l'esaurimento delle risorse di vario genere. Questo genere di test è chiamato
''test di stress.''

Il sistema ei suoi componenti possono essere sottoposti ai cosiddetti "test puliti" che lo dimostrano
un risultato positivo, ovvero che il sistema funziona correttamente in risposta a un grande problema
variazione delle configurazioni previste.

Il sistema ei suoi componenti possono anche essere esposti a "test sporchi" che forniscono input
al di fuori dell'intervallo previsto. Ad esempio, se un modulo prevede una stringa con terminazione zero
rappresentazione di un numero intero, un test sporco potrebbe fornire una stringa non terminata di random
caratteri per verificare che il sistema non si arresti in modo anomalo a causa di questo input imprevisto.
Sfortunatamente, il rilevamento di tali input "sporchi" e l'adozione di misure preventive per garantire il
il sistema non fallisce in modo catastrofico può richiedere un'enorme quantità di sovraccarico di sviluppo.
Al fine di ridurre i tempi di sviluppo, all'inizio del progetto è stata presa una decisione
ridurre al minimo la quantità di convalida dei parametri e la gestione degli errori nel file NS-3 base di codice. Per
per questo motivo, non dedichiamo molto tempo a test sporchi - scoprirebbe solo il
risultati della decisione progettuale che sappiamo di aver preso.

Vogliamo dimostrarlo NS-3 il software funziona in alcune serie di condizioni. Noi
prendi in prestito un paio di definizioni per restringere un po 'questo. Il dominio of applicabilità is
un insieme di condizioni prescritte per le quali il modello è stato testato, confrontato
realtà nella misura del possibile e giudicata idonea all'uso. Il gamma of precisione offre
accordo tra il modello informatizzato e la realtà all'interno di un dominio di applicabilità.

Il NS-3 ambiente di test fornisce strumenti per consentire l'impostazione e l'esecuzione di test
ambienti su più sistemi (buildbot) e fornisce classi per incoraggiare la pulizia
prove per verificare il funzionamento del sistema oltre il previsto ''dominio di applicabilità''
e ''gamma di precisione.''

potente
Ok, "performante" non è una vera parola inglese. Si tratta, tuttavia, di un neologismo molto conciso
che è abbastanza spesso usato per descrivere ciò che vogliamo NS-3 essere: potente e abbastanza veloce da
finisci il lavoro.

Si tratta davvero dell'ampio argomento dei test delle prestazioni del software. Una delle chiavi
ciò che viene fatto è confrontare due sistemi per trovare quello che funziona meglio (cfr
punti di riferimenti). Viene utilizzato per dimostrare che, ad esempio, NS-3 può eseguire un tipo di base
di simulazione veloce almeno quanto uno strumento concorrente, o può essere utilizzato per identificare parti di
il sistema che funziona male.

Nel NS-3 framework di test, forniamo supporto per la tempistica di vari tipi di test.

manutenibilità
Un prodotto software deve essere manutenibile. Questa è, ancora, un'affermazione molto ampia, ma a
struttura di test può aiutare con l'attività. Una volta che un modello è stato sviluppato, convalidato e
verificato, possiamo eseguire ripetutamente la suite di test per garantire l'intero sistema
che rimanga valido e verificato per tutta la sua vita.

Quando una funzione smette di funzionare come previsto dopo qualche tipo di modifica al sistema
integrato, si chiama genericamente a regressione. Originariamente il termine regressione
si riferiva a una modifica che causava la ricomparsa di un bug precedentemente corretto, ma il termine ha
evoluto per descrivere qualsiasi tipo di cambiamento che interrompe la funzionalità esistente. Ci sono molti
tipi di regressioni che possono verificarsi nella pratica.

A locale regressione è quello in cui una modifica influisce direttamente sul componente modificato. Per
ad esempio, se un componente viene modificato per allocare e liberare memoria ma i puntatori non aggiornati lo sono
utilizzato, il componente stesso si guasta.

A a distanza regressione è quello in cui una modifica a un componente interrompe la funzionalità
un altro componente. Ciò riflette la violazione di un implicito ma forse non riconosciuto
contratto tra i componenti.

An smascherato regressione è uno che crea una situazione in cui un bug esistente in precedenza
che non ha avuto alcun effetto viene improvvisamente esposto nel sistema. Questo può essere semplice come fare esercizio
un percorso di codice per la prima volta.

A performance regressione è uno che fa sì che i requisiti di prestazione del sistema
essere violato. Ad esempio, eseguire alcuni lavori in una funzione di basso livello che possono essere ripetuti
un gran numero di volte può rendere improvvisamente il sistema inutilizzabile da determinate prospettive.

Il NS-3 framework di test fornisce strumenti per automatizzare il processo utilizzato per convalidare e
verifica il codice nelle suite di test notturne per aiutare a identificare rapidamente possibili regressioni.

Testing contesto
ns-3 è costituito da un motore principale di simulazione, un set di modelli, programmi di esempio e test.
Nel tempo, i nuovi contributori contribuiscono con modelli, test ed esempi. Un programma di test Python
prova.py funge da responsabile dell'esecuzione del test; prova.py può eseguire codice di prova ed esempi su
cercare le regressioni, può restituire i risultati in una serie di moduli e può gestire il codice
strumenti di analisi della copertura. Inoltre, sovrapponiamo Buildbot che sono build automatizzate
robot che eseguono test di robustezza eseguendo il framework di test su sistemi diversi
e con diverse opzioni di configurazione.

BuildBot
Al livello più alto dei test ns-3 ci sono i buildbot (build robot). Se sei
non familiare con questo sistema guarda http://djmitche.github.com/buildbot/docs/0.7.11/.
Questo è un sistema automatizzato open source che consente NS-3 da ricostruire e testare ciascuno
tempo qualcosa è cambiato. Eseguendo i buildbot su diversi sistemi, noi
può garantire che NS-3 compila ed esegue correttamente su tutti i suoi sistemi supportati.

Gli utenti (e gli sviluppatori) in genere non interagiranno con il sistema buildbot se non per
leggere i suoi messaggi sui risultati dei test. Se viene rilevato un errore in uno dei
lavori di compilazione e test automatizzati, il buildbot invierà un'e-mail a ns-sviluppatori
mailing list. Questa email assomiglierà a qualcosa di simile

Nell'URL dei dettagli completi mostrato nell'e-mail, è possibile cercare la parola chiave mancato e
selezionare la stdio collegamento per il passaggio corrispondente per vedere il motivo dell'errore.

Il buildbot farà il suo lavoro in silenzio se non ci sono errori e il sistema subirà
costruire e testare cicli ogni giorno per verificare che tutto vada bene.

Test.py
I buildbot usano un programma Python, prova.py, che è responsabile dell'esecuzione di tutti i
test e raccogliendo i rapporti risultanti in un formato leggibile dall'uomo. Questo programma è
disponibile anche per l'uso da parte di utenti e sviluppatori.

prova.py è molto flessibile nel consentire all'utente di specificare il numero e il tipo di test da eseguire
correre; e anche la quantità e il tipo di output da generare.

Prima di correre prova.py, assicurati che gli esempi e i test di ns3 siano stati creati facendo
di test

$ ./waf configure --enable-examples --enable-tests
$ ./waf

Per impostazione predefinita, prova.py eseguirà tutti i test disponibili e riporterà lo stato in modo molto conciso
modulo. Esecuzione del comando

$ ./prova.py

risulterà in un certo numero di PASSAGGIO, FAIL, CRASH or SALTA indicazioni seguite dal tipo di
test eseguito e il relativo nome visualizzato.

Waf: entrare nella directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: uscita dalla directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' è terminato con successo (0.939s)
FAIL: TestSuite ns3-wifi-propagation-loss-models
PASS: servizio nome oggetto TestSuite
PASS: TestSuite pcap-file-oggetto
SUPERATO: TestSuite ns3-tcp-cwnd
...
PASS: Interoperabilità TestSuite ns3-tcp
PASS: Esempio csma-broadcast
PASS: Esempio csma-multicast

Questa modalità è pensata per essere utilizzata dagli utenti interessati a determinare se il loro
la distribuzione funziona correttamente e dagli sviluppatori interessati a determinare se
le modifiche che hanno apportato hanno causato regressioni.

Sono disponibili diverse opzioni per controllare il comportamento di prova.py. se corri
prova.py --Aiuto dovresti vedere un riepilogo del comando come:

Utilizzo: test.py [opzioni]

Opzioni:
-h, --help mostra questo messaggio di aiuto ed esce
-b PERCORSOBUILD, --buildpath=PERCORSOBUILD
specificare il percorso in cui è stato compilato ns-3 (l'impostazione predefinita è
build directory per la variante corrente)
-c TIPO, --constrain=TIPO
vincolare il corridore del test per tipo di test
-e ESEMPIO, --example=ESEMPIO
specificare un singolo esempio da eseguire (nessun percorso relativo è
necessario)
-g, --grind esegue le suite di test e gli esempi usando valgrind
-k, --kinds stampa i tipi di test disponibili
-l, --list stampa l'elenco dei test conosciuti
-m, --multiple segnala più errori da test suite e test
casi
-n, --nowaf non esegue waf prima di iniziare il test
-p ESEMPIO, --pyesempio=ESEMPIO
specificare un singolo esempio Python da eseguire (con relative
percorso)
-r, --retain conserva tutti i file temporanei (che normalmente sono
cancellato)
-s SUITE DI PROVA, --suite=SUITE DI PROVA
specificare una singola suite di test da eseguire
-t FILE DI TESTO, --text=FILE DI TESTO
scrivere i risultati dettagliati del test in TEXT-FILE.txt
-v, --verbose stampa avanzamento e messaggi informativi
-w FILE-HTML, --web=FILE-HTML, --html=FILE-HTML
scrivi i risultati dettagliati del test in HTML-FILE.html
-x FILE-XML, --xml=FILE-XML
scrivere i risultati dettagliati del test in XML-FILE.xml

Se si specifica uno stile di output opzionale, è possibile generare descrizioni dettagliate di
test e stato. Gli stili disponibili sono testo e HTML. I buildbot selezioneranno l'HTML
opzione per generare report di test HTML per le build notturne utilizzando

$ ./test.py --html=notturno.html

In questo caso, verrebbe creato un file HTML chiamato ''nightly.html'' con un bel riassunto
della prova effettuata. Un formato ''leggibile dall'uomo'' è disponibile per gli utenti interessati al
dettagli.

$ ./test.py --text=risultati.txt

Nell'esempio sopra, la suite di test che controlla il NS-3 perdita di propagazione del dispositivo wireless
modelli falliti. Per impostazione predefinita non vengono fornite ulteriori informazioni.

Per esplorare ulteriormente il fallimento, prova.py consente di specificare una singola suite di test.
Esecuzione del comando

$ ./test.py --suite=ns3-wifi-propagation-loss-models

o in modo equivalente

$ ./test.py -s ns3-wifi-propagation-loss-models

risulta in quella singola suite di test in esecuzione.

FAIL: TestSuite ns3-wifi-propagation-loss-models

Per trovare informazioni dettagliate sull'errore, è necessario specificare il tipo di output
desiderato. Ad esempio, la maggior parte delle persone sarà probabilmente interessata a un file di testo:

$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt

Ciò comporterà l'esecuzione di quella singola suite di test con lo stato del test scritto nel file
file ''results.txt''.

Dovresti trovare qualcosa di simile al seguente in quel file

FAIL: Test Suite ''ns3-wifi-propagation-loss-models'' (real 0.02 utente 0.01 sistema 0.00)
PASS: Test Case "Check ... Friis ... modello ..." (real 0.01 utente 0.00 sistema 0.00)
FAIL: Test Case "Controlla ... Log Distance ... modello" (real 0.01 utente 0.01 sistema 0.00)
Caratteristiche:
Messaggio: ottenuto un valore SNR imprevisto
Condizione: [descrizione lunga di ciò che effettivamente ha fallito]
Corrente: 176.395
Limite: 176.407 +- 0.0005
File: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Linea: 360

Si noti che la Test Suite è composta da due Test Case. Il primo test case ha verificato il
Modello di perdita di propagazione di Friis e superato. Il secondo test case non è riuscito a controllare il registro
Modello di propagazione della distanza. In questo caso è stato trovato un SNR di 176.395 e il test
previsto un valore di 176.407 corretto con tre cifre decimali. Il file che ha implementato
viene elencato il test non riuscito e la riga di codice che ha attivato l'errore.

Se lo desideri, potresti anche facilmente scrivere un file HTML usando il --html opzione
come descritto sopra.

In genere un utente eseguirà tutti i test almeno una volta dopo il download NS-3 per garantire che
il suo ambiente è stato costruito correttamente e sta generando risultati corretti
secondo le suite di test. Gli sviluppatori in genere eseguiranno le suite di test prima e
dopo aver apportato una modifica per assicurarsi di non aver introdotto una regressione con il loro
i cambiamenti. In questo caso, gli sviluppatori potrebbero non voler eseguire tutti i test, ma solo un sottoinsieme. Per
ad esempio, lo sviluppatore potrebbe voler eseguire solo periodicamente gli unit test durante la creazione
modifiche a un repository. In questo caso, prova.py si può dire di vincolare i tipi di
test eseguiti su una particolare classe di test. Il comando seguente risulterà solo
gli unit test in corso:

$ ./test.py --constrain=unità

Allo stesso modo, il comando seguente risulterà nell'esecuzione solo dei test di fumo di esempio:

$ ./test.py --constrain=unità

Per visualizzare un rapido elenco dei tipi legali di vincoli, è possibile richiedere che vengano elencati.
Il seguente comando

$ ./test.py --kinds

comporterà la visualizzazione del seguente elenco:

Waf: entrare nella directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: uscita dalla directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' è terminato con successo (0.939s)Waf: accesso alla directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt: Build Verification Tests (per vedere se la build è stata completata correttamente)
core: esegui tutti i test basati su TestSuite (escludi esempi)
esempio: Esempi (per vedere se i programmi di esempio vengono eseguiti correttamente)
prestazioni: test delle prestazioni (controlla se il sistema è veloce come previsto)
sistema: test di sistema (si estende su moduli per verificare l'integrazione dei moduli)
unità: Unit Test (all'interno dei moduli per verificare la funzionalità di base)

Ognuno di questi tipi di test può essere fornito come vincolo utilizzando il --vincolo opzione.

Per visualizzare un rapido elenco di tutte le suite di test disponibili, puoi chiedere che lo siano
elencato. Il seguente comando,

$ ./test.py --lista

comporterà la visualizzazione di un elenco della suite di test, simile a

Waf: entrare nella directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: uscita dalla directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' è terminato con successo (0.939s)
istogramma
ns3-wifi-interferenza
ns3-tcp-cwnd
ns3-tcp-interoperabilità
campione
dispositivi-mesh-fiamma
dispositivi-mesh-dot11s
dispositivi-mesh
...
servizio-nome-oggetto
richiama
gli attributi
config
valore globale
Da riga di comando
numero-casuale-base
oggetto

Ognuna di queste suite elencate può essere selezionata per essere eseguita da sola utilizzando il --suite opzione come
sopra riportati.

Analogamente alle suite di test, è possibile eseguire un singolo programma di esempio C++ utilizzando il file --esempio
opzione. Nota che il percorso relativo per l'esempio non ha bisogno di essere incluso e quello
gli eseguibili creati per gli esempi C++ non hanno estensioni. Entrando

$ ./test.py --example=udp-echo

risulta in quell'unico esempio in esecuzione.

PASS: Esempi di esempi/udp/udp-echo

È possibile specificare la directory in cui è stato creato ns-3 utilizzando il file --buildpath opzione come
segue.

$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc

Si può eseguire un singolo programma di esempio Python usando il file --pyesempio opzione. Nota che il
deve essere incluso il percorso relativo per l'esempio e che gli esempi Python ne hanno bisogno
estensioni. Entrando

$ ./test.py --pyexample=esempi/tutorial/first.py

risulta in quell'unico esempio in esecuzione.

PASS: Esempi di esempi/tutorial/first.py

Poiché gli esempi Python non vengono compilati, non è necessario specificare la directory in cui ns-3
è stato costruito per eseguirli.

Normalmente, quando vengono eseguiti programmi di esempio, scrivono una grande quantità di dati di file di traccia.
Questo viene normalmente salvato nella directory di base della distribuzione (ad es.
/home/utente/ns-3-dev). quando prova.py fa un esempio, è davvero completamente indifferente
con i file di traccia. Vuole solo determinare se l'esempio può essere compilato ed eseguito
senza errori. Poiché questo è il caso, i file di traccia vengono scritti in a
/tmp/tracce-non controllate directory. Se esegui l'esempio sopra, dovresti essere in grado di trovare
l'associato udp-echo.tr e udp-echo-n-1.pcap file lì.

L'elenco degli esempi disponibili è definito dal contenuto della directory ''examples'' in
la distribuzione. Se si seleziona un esempio per l'esecuzione utilizzando il file --esempio opzione,
prova.py non farà alcun tentativo di decidere se l'esempio è stato configurato o meno, esso
proverà semplicemente a eseguirlo e riporterà il risultato del tentativo.

Quando prova.py viene eseguito, per impostazione predefinita si assicurerà prima che il sistema sia stato completamente
costruito. Questo può essere sconfitto selezionando il --ora opzione.

$ ./test.py --list --nowaf

comporterà la visualizzazione di un elenco delle suite di test attualmente create, simile a:

ns3-wifi-propagation-loss models
ns3-tcp-cwnd
ns3-tcp-interoperabilità
pcap-oggetto-file
servizio-nome-oggetto
generatori di numeri casuali

Da notare l'assenza del Waf costruire messaggi.

prova.py supporta anche l'esecuzione delle suite di test e degli esempi in valgrind. Valgrind è un
programma flessibile per il debug e la profilazione di eseguibili Linux. Per impostazione predefinita, viene eseguito valgrind
uno strumento chiamato memcheck, che esegue una serie di funzioni di controllo della memoria, tra cui
rilevamento di accessi alla memoria non inizializzata, uso improprio della memoria allocata (doppie liberazioni,
access after free, ecc.) e il rilevamento di perdite di memoria. Questo può essere selezionato utilizzando il
--macinare opzione.

$ ./test.py --grind

Mentre corre, prova.py e i programmi che esegue indirettamente, generano un gran numero di
file temporanei. Di solito, il contenuto di questi file non è interessante, tuttavia in alcuni
casi può essere utile (a scopo di debug) visualizzare questi file. prova.py fornisce un
--conservare opzione che farà sì che questi file temporanei vengano conservati dopo l'esecuzione
completato. I file vengono salvati in una directory denominata testpy-output sotto una sottodirectory
chiamato secondo l'attuale Coordinated Universal Time (noto anche come Greenwich Mean
Tempo).

$ ./test.py --retain

Infine, prova.py fornisce un --verboso opzione che stamperà grandi quantità di informazioni
sui suoi progressi. Non ci si aspetta che questo sarà terribilmente utile a meno che non ci sia
un errore. In questo caso, è possibile accedere allo standard output e all'errore standard
segnalati eseguendo suite di test ed esempi. Seleziona dettagliata nel modo seguente:

$ ./test.py --verbose

Tutte queste opzioni possono essere mescolate e abbinate. Ad esempio, per eseguire tutto il core ns-3
suite di test in valgrind, in modalità dettagliata, generando un file di output HTML, one
farebbe:

$ ./test.py --verbose --grind --constrain=core --html=risultati.html

Test Tassonomia
Come accennato in precedenza, i test sono raggruppati in una serie di classificazioni ampiamente definite
consentire agli utenti di eseguire test in modo selettivo per affrontare i diversi tipi di test necessari
da fare.

· Crea test di verifica

· Test unitari

· Test di sistema

· Esempi

· Prove di prestazione

BuildVerificationTest
Questi sono test relativamente semplici costruiti insieme alla distribuzione e utilizzati
per assicurarsi che la build funzioni praticamente. I nostri attuali test unitari vivono nel
file sorgente del codice che testano e sono integrati nei moduli ns-3; e quindi adatta il
descrizione delle BVT. I BVT risiedono nello stesso codice sorgente integrato nel codice ns-3.
I nostri test attuali sono esempi di questo tipo di test.

Unità Test
Gli unit test sono test più coinvolti che entrano nei dettagli per assicurarsi che un pezzo di codice
funziona come pubblicizzato in isolamento. Non c'è davvero alcun motivo per questo tipo di test
integrato in un modulo ns-3. Si scopre, ad esempio, che l'unità verifica l'oggetto
name service hanno all'incirca le stesse dimensioni del codice del servizio nome oggetto stesso. Test unitari
sono test che controllano un singolo bit di funzionalità che non è integrato nel codice ns-3,
ma risiedono nella stessa directory del codice che verifica. È possibile che questi test
verificare anche l'integrazione di più file di implementazione in un modulo. Il file
src/core/test/names-test-suite.cc è un esempio di questo tipo di test. Il file
src/network/test/pcap-file-test-suite.cc è un altro esempio che utilizza un noto pcap valido
file come file vettoriale di prova. Questo file è memorizzato localmente nella directory src/network.

Sistema Test
I test di sistema sono quelli che coinvolgono più di un modulo nel sistema. Abbiamo un sacco di
questo tipo di test viene eseguito nel nostro attuale framework di regressione, ma in genere lo sono
esempi sovraccarichi. Forniamo un nuovo posto per questo tipo di test nella directory
sorgente/prova. Il file src/test/ns3tcp/ns3-interop-test-suite.cc è un esempio di questo tipo
di prova. Utilizza NSC TCP per testare l'implementazione di ns-3 TCP. Spesso ci saranno prove
vettori necessari per questo tipo di test e sono memorizzati nella directory in cui il file
prova la vita. Ad esempio, ns3tcp-interop-response-vectors.pcap è un file costituito da un
numero di intestazioni TCP utilizzate come risposte previste del TCP ns-3 in prova
a uno stimolo generato dal TCP NSC che viene utilizzato come implementazione "noto bene".

Esempi
Gli esempi vengono testati dal framework per assicurarsi che siano stati creati e funzionino. Niente è
selezionato, e attualmente i file pcap sono appena scritti in / Tmp da scartare. Se
gli esempi vengono eseguiti (non si bloccano) superano questo test del fumo.

Performance Test
I test di prestazione sono quelli che esercitano una parte particolare del sistema e determinano
se le prove sono state completate in un tempo ragionevole.

corsa Test
I test vengono in genere eseguiti utilizzando il livello alto prova.py programma. Per ottenere un elenco dei
opzioni della riga di comando disponibili, esegui prova.py --Aiuto

Il programma di prova prova.py eseguirà entrambi i test e quegli esempi che sono stati aggiunti
l'elenco da controllare. La differenza tra test ed esempi è la seguente. Prove
in genere verificare che l'output o gli eventi specifici della simulazione siano conformi al comportamento previsto.
Al contrario, l'output degli esempi non viene verificato e il programma di test verifica semplicemente il
stato di uscita del programma di esempio per assicurarsi che venga eseguito senza errori.

In breve, per eseguire tutti i test, è necessario prima configurare i test durante la fase di configurazione e
anche (facoltativamente) esempi se gli esempi devono essere verificati:

$ ./waf --configure --enable-examples --enable-test

Quindi, compila ns-3 e, dopo averlo compilato, esegui prova.py. prova.py -h mostrerà un numero
di opzioni di configurazione che modificano il comportamento di test.py.

Il programma prova.py richiama, per test ed esempi C++, un programma C++ di livello inferiore chiamato
corridore per eseguire effettivamente i test. Come discusso di seguito, questo corridore può essere un
modo utile per eseguire il debug dei test.

Debug Test
Il debug dei programmi di test viene eseguito al meglio eseguendo il test-runner di basso livello
programma. Il test-runner è il ponte dal codice Python generico a NS-3 codice. è
scritto in C++ e utilizza il processo di rilevamento automatico dei test nel file NS-3 codice per trovare e
consentire l'esecuzione di tutti i vari test.

Il motivo principale per cui prova.py non è adatto per il debug è che non è consentito
la registrazione da attivare utilizzando il NS_LOG variabile ambientale durante l'esecuzione di test.py. Questo
la limitazione non si applica all'eseguibile del test-runner. Quindi, se vuoi vedere la registrazione
output dei tuoi test, devi eseguirli utilizzando direttamente il test-runner.

Per eseguire il test-runner, lo esegui come qualsiasi altro eseguibile ns-3, usando
waf. Per ottenere un elenco delle opzioni disponibili, puoi digitare:

$ ./waf --run "test-runner --help"

Dovresti vedere qualcosa di simile al seguente

Waf: entrare nella directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: uscita dalla directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'build' è terminato con successo (0.353s)
--assert: indica ai test di segfault (come assert) se viene rilevato un errore
--basedir=dir: imposta la directory di base (dove trovare src) su ''dir''
--tempdir=dir: imposta la directory temporanea (dove trovare i file di dati) su ''dir''
--constrain=test-type: vincola i controlli alle suite di test di tipo ''test-type''
--help: stampa questo messaggio
--kinds: elenca tutti i tipi di test disponibili
--list: elenca tutte le suite di test (facoltativamente vincolate dal tipo di test)
--out=nome-file: imposta il file di output dello stato del test su ''nome-file''
--suite=nome-suite: esegue la suite di test denominata ''nome-suite''
--verbose: attiva i messaggi nelle suite di test di esecuzione

Ci sono un certo numero di cose a tua disposizione che ti saranno familiari se lo hai
guardò prova.py. Questo dovrebbe essere previsto poiché il testrunner è solo un'interfaccia
fra prova.py e NS-3. Potresti notare che i comandi relativi all'esempio mancano qui.
Questo perché gli esempi non lo sono davvero NS-3 test. prova.py li esegue come se lo fossero
per presentare un ambiente di test unificato, ma sono davvero completamente diversi e non
si trova qui.

La prima nuova opzione che appare qui, ma non in test.py è la --affermare opzione. Questo
L'opzione è utile durante il debug di un test case durante l'esecuzione con un debugger come gdb. Quando
selezionata, questa opzione indica al test case sottostante di causare una violazione della segmentazione se
viene rilevato un errore. Questo ha il piacevole effetto collaterale di interrompere l'esecuzione del programma
(irrompere nel debugger) quando viene rilevato un errore. Se stai usando gdb, potresti usare
questa opzione qualcosa come,

$ ./conchiglia
$ cd build/debug/utils
$ gdb corridore del test
$ run --suite=valore-globale --assert

Se viene quindi rilevato un errore nella suite di test del valore globale, verrà generato un segfault
e il debugger (livello sorgente) si fermerebbe a NS_TEST_ASSERT_MSG che ha rilevato il
errore.

Un'altra nuova opzione che appare qui è il --basato opzione. Si scopre che alcuni
test potrebbe dover fare riferimento alla directory di origine del file NS-3 distribuzione per trovare locale
data, quindi è sempre necessaria una directory di base per eseguire un test.

Se esegui un test da test.py, il programma Python fornirà l'opzione basedir per
voi. Per eseguire uno dei test direttamente dal test-runner utilizzando waf, avrai bisogno di
specificare la suite di test da eseguire insieme alla directory di base. Quindi potresti usare la shell
e fai:

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"

Nota le virgolette ''indietro'' sul file pwd comando.

Se stai eseguendo la suite di test da un debugger, può essere piuttosto doloroso da ricordare
e digitare costantemente il percorso assoluto della directory di base di distribuzione. Per colpa di
questo, se ometti il ​​basedir, il test-runner cercherà di trovarne uno per te. Esso
inizia nella directory di lavoro corrente e risale l'albero delle directory cercando a
file di directory con i file denominati VERSIONE e LICENZA. Se ne trova uno, lo presume
deve essere il baseir e te lo fornisce.

Test produzione
Molte suite di test richiedono la scrittura di file temporanei (come i file pcap) durante il processo di
eseguire le prove. I test necessitano quindi di una directory temporanea in cui scrivere. Il Pitone
l'utilità di test (test.py) fornirà automaticamente un file temporaneo, ma se eseguito in modo autonomo
questa directory temporanea deve essere fornita. Proprio come nel caso di base, può essere
fastidioso dover continuamente fornire a --tempdir, quindi il corridore del test ne troverà uno
fuori per te se non ne fornisci uno. Per prima cosa cerca le variabili di ambiente denominate TMP
e TEMP e li usa. Se nessuno dei due TMPTEMP sono definiti seleziona / Tmp. Il codice
quindi vira su un identificatore che indica cosa ha creato la directory (ns-3), quindi l'ora
(hh.mm.ss) seguito da un numero casuale grande. Il corridore del test crea una directory di quello
nome da utilizzare come directory temporanea. I file temporanei vanno quindi in una directory che
sarà chiamato qualcosa di simile

/tmp/ns-3.10.25.37.61537845

Il tempo viene fornito come suggerimento in modo da poter ricostruire in modo relativamente semplice cosa
directory è stata utilizzata se è necessario tornare indietro e guardare i file che sono stati inseriti in quella
directory.

Un'altra classe di output è l'output di test come le tracce pcap generate per il confronto
uscita di riferimento. Il programma di test in genere li eliminerà tutti dopo le suite di test
correre. Per disabilitare l'eliminazione dell'output di prova, eseguire prova.py con l'opzione "mantieni":

$ ./test.py -r

e l'output del test può essere trovato in testpy-output/ directory.

Reportistica of test fallimenti
Quando esegui una suite di test utilizzando il test-runner, il test verrà eseguito in modo silenzioso per impostazione predefinita.
L'unica indicazione che otterrai che il test ha superato è il assenza di un messaggio
da waf dicendo che il programma ha restituito qualcosa di diverso da un codice di uscita zero. Ottenere
alcuni output del test, è necessario specificare un file di output in cui verranno inviati i test
scrivi il loro stato XML usando il file --fuori opzione. Devi stare attento a interpretare il
risultati perché le suite di test lo faranno aggiungere risultati su questo file. Provare,

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"

Se guardi il file miofile.xml dovresti vedere qualcosa come


pcap-oggetto-file

Verifica che PcapFile::Open con la modalità ''w'' funzioni
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Verifica che PcapFile::Open con la modalità ''r'' funzioni
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Verifica che PcapFile::Open con la modalità ''a'' funzioni
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Verificare che PcapFileHeader sia gestito correttamente
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Verificare che PcapRecordHeader sia gestito correttamente
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Verificare che PcapFile sia in grado di leggere un file pcap noto e valido
PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00

PASSAGGIO
reale 0.00 utente 0.00 sistema 0.00


Se hai familiarità con XML, questo dovrebbe essere abbastanza autoesplicativo. Inoltre non è un
file XML completo poiché le suite di test sono progettate per avere il loro output aggiunto a un master
File di stato XML come descritto in prova.py .

Debug test suite fallimenti
Per eseguire il debug di arresti anomali del test, ad esempio

CRASH: Interferenza Wi-Fi TestSuite ns3

Puoi accedere al programma di test-runner sottostante tramite gdb come segue, quindi superare il
"--basedir=`pwd`" argomento da eseguire (puoi anche passare altri argomenti se necessario, ma
basedir è il minimo necessario):

$ ./waf --command-template="gdb %s" --run "test-runner"
Waf: Accesso alla directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf: Uscita dalla directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
'build' è terminato con successo (0.380s)
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+: GNU GPL versione 3 o successivehttp://gnu.org/licenses/gpl.html>
Questo è un software gratuito: sei libero di modificarlo e ridistribuirlo.
NON C'è ALCUNA GARANZIA, nella misura consentita dalla legge. Digita "mostra copia"
e "mostra garanzia" per i dettagli.
Questo GDB è stato configurato come "x86_64-linux-gnu"...
(gdb) r --basedir=`pwd`
Programma di partenza: <..>/build/debug/utils/test-runner --basedir=`pwd`
[Debug dei thread utilizzando libthread_db abilitato]
asserire fallito. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...

Ecco un altro esempio di come utilizzare valgrind per eseguire il debug di un problema di memoria come:

VALGR: dispositivi TestSuite-mesh-dot11s-regression

$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --run test-runner

Classe TestRunner
Gli eseguibili che eseguono programmi di test dedicati utilizzano una classe TestRunner. Questa classe
fornisce la registrazione e l'elenco automatico dei test, nonché un modo per eseguire il
prove individuali. Le singole suite di test utilizzano costruttori globali C++ per aggiungersi
una raccolta di suite di test gestite dal corridore del test. Il corridore del test viene utilizzato per elencare
tutti i test disponibili e per selezionare un test da eseguire. Questa è una classe abbastanza semplice
che fornisce tre metodi statici per fornire o aggiungere e ottenere suite di test a
raccolta di prove. Vedi il doxygen per la classe ns3::TestRunner per i dettagli.

Test Suite
Tutto NS-3 i test sono classificati in Test Suite e Test Case. Una suite di test è a
raccolta di casi di test che esercitano completamente un determinato tipo di funzionalità. Come
descritto sopra, le suite di test possono essere classificate come

· Crea test di verifica

· Test unitari

· Test di sistema

· Esempi

· Prove di prestazione

Questa classificazione viene esportata dalla classe TestSuite. Questa classe è abbastanza semplice,
esistente solo come luogo per esportare questo tipo e per accumulare casi di test. Da un utente
prospettiva, per creare una nuova TestSuite nel sistema è sufficiente definirne una nuova
classe che eredita dalla classe Suite di prova e svolgere questi due compiti.

Il codice seguente definirà una nuova classe che può essere eseguita da prova.py come test di "unità".
con il nome visualizzato, il mio-nome-serie-di-test.

classe MySuite : TestSuite pubblico
{
pubblico:
MiaTestSuite ();
};

MyTestSuite::MyTestSuite ()
: TestSuite ("nome-suite-di-test", UNIT)
{
AddTestCase (nuovo MyTestCase);
}

MiaTestSuite miaTestSuite;

La classe base si occupa di tutte le registrazioni e i rapporti necessari per essere un bene
cittadino nel quadro del test.

Test Custodie
I singoli test vengono creati utilizzando una classe TestCase. Modelli comuni per l'uso di un test
caso includono "un test case per funzionalità" e "un test case per metodo". Miscele di
questi modelli possono essere utilizzati.

Per creare un nuovo test case nel sistema, tutto ciò che si deve fare è ereditare dal
Caso di prova classe base, eseguire l'override del costruttore per assegnare un nome al test case ed eseguire l'override
, il DoCorri metodo per eseguire il test.

classe MyTestCase : TestCase pubblico
{
Il mio caso di prova ();
vuoto virtuale DoRun (vuoto);
};

Il mioTestCase::Il mioTestCase ()
: TestCase ("Verifica un po' di funzionalità")
{
}

nulla
MyTestCase::DoRun (vuoto)
{
NS_TEST_ASSERT_MSG_EQ (true, true, "Alcuni messaggi di errore");
}

Elettricita, Gas Ed Acqua
Ci sono una serie di utilità di vario tipo che fanno anche parte del test
struttura. Gli esempi includono un file pcap generalizzato utile per memorizzare i vettori di test; un
contenitore generico utile per la memorizzazione transitoria dei vettori di test durante l'esecuzione del test; e
strumenti per la generazione di presentazioni basate sui risultati dei test di convalida e verifica.

Queste utilità non sono documentate qui, ma, ad esempio, vedere come viene eseguito il test TCP
trovato in src/test/ns3tcp/ utilizzare i file pcap e l'output di riferimento.

Come a scrivere test
Un obiettivo primario del progetto ns-3 è aiutare gli utenti a migliorare la validità e
credibilità dei loro risultati. Ci sono molti elementi per ottenere modelli validi e
simulazioni e il test è una componente importante. Se contribuisci con modelli o esempi
ns-3, ti potrebbe essere chiesto di contribuire con il codice di test. Verranno utilizzati i modelli con cui contribuisci
per molti anni da altre persone, che probabilmente non hanno idea a prima vista se il
il modello è corretto. Il codice di test che scrivi per il tuo modello ti aiuterà a evitare il futuro
regressioni nell'output e aiuterà gli utenti futuri a comprendere la verifica e
limiti di applicabilità dei vostri modelli.

Esistono molti modi per verificare la correttezza dell'implementazione di un modello. In questo
sezione, speriamo di coprire alcuni casi comuni che possono essere usati come guida per scrivere di nuovo
test.

Campione Suite di prova scheletro
Quando si parte da zero (cioè non si aggiunge un TestCase a un TestSuite esistente), questi
le cose devono essere decise in anticipo:

· Come si chiamerà la suite di test

· Che tipo di test sarà (Build Verification Test, Unit Test, System Test, o
Test della prestazione)

· Dove vivrà il codice di test (o in un modulo ns-3 esistente o separatamente in
directory src/test/). Dovrai modificare il file wscript in quella directory in
compila il tuo nuovo codice, se è un nuovo file.

Un programma chiamato src/crea-module.py è un buon punto di partenza. Questo programma può essere
invocato come crea-modulo.py router per un ipotetico nuovo modulo chiamato router. Una volta
fai questo, vedrai a router directory, e a test/router-test-suite.cc suite di prova.
Questo file può essere un punto di partenza per il test iniziale. Questa è una suite di test funzionante,
anche se i test effettivi eseguiti sono banali. Copialo nel test del tuo modulo
directory, ed eseguire una sostituzione globale di "Router" in quel file per qualcosa di pertinente
al modello che si desidera testare. Puoi anche modificare cose come una più descrittiva
nome del caso di prova.

Devi anche aggiungere un blocco nel tuo wscript per far compilare questo test:

modulo_test.source = [
'test/router-test-suite.cc',
]

Prima di iniziare effettivamente a fare cose utili, può essere utile provare a eseguire il
scheletro. Assicurati che ns-3 sia stato configurato con l'opzione "--enable-tests".
Supponiamo che la tua nuova suite di test sia chiamata "router" come qui:

RouterTestSuite::RouterTestSuite ()
: TestSuite ("router", UNIT)

Prova questo comando:

$ ./test.py -s router

Dovrebbe essere prodotto un output come quello di seguito:

PASS: router TestSuite
1 test su 1 superato (1 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Vedere src/lte/test/test-lte-antenna.cc per un esempio funzionante.

Test macro
Sono disponibili numerose macro per il controllo dell'output del programma di test con previsto
produzione. Queste macro sono definite in sorgente/core/modello/test.h.

Il set principale di macro utilizzate include quanto segue:

NS_TEST_ASSERT_MSG_EQ(effettivo, limite, msg)
NS_TEST_ASSERT_MSG_NE(effettivo, limite, msg)
NS_TEST_ASSERT_MSG_LT(effettivo, limite, msg)
NS_TEST_ASSERT_MSG_GT(effettivo, limite, msg)
NS_TEST_ASSERT_MSG_EQ_TOL(effettivo, limite, tol, msg)

Il primo argomento presenti è il valore in prova, il secondo valore limitare è il previsto
value (o il valore su cui eseguire il test) e l'ultimo argomento msg è il messaggio di errore a
stampare se il test fallisce.

Le prime quattro macro precedenti verificano l'uguaglianza, la disuguaglianza, minore o maggiore di,
rispettivamente. La quinta macro sopra verifica l'uguaglianza, ma entro una certa tolleranza.
Questa variante è utile quando si testano numeri in virgola mobile per l'uguaglianza rispetto a un limite,
dove si desidera evitare un test non riuscito a causa di errori di arrotondamento.

Infine, ci sono varianti di quanto sopra in cui la parola chiave ASSERIRE è sostituito da ASPETTARSI.
Queste varianti sono progettate appositamente per l'uso nei metodi (soprattutto callback) di ritorno
vuoto. Riserva il loro utilizzo per le richiamate che utilizzi nei tuoi programmi di test; altrimenti usa
, il ASSERIRE varianti.

Come a aggiungere an esempio Programma a , il test suite
Si può "fumare test" che gli esempi vengono compilati ed eseguiti con successo fino al completamento (senza
perdite di memoria) utilizzando il esempi-to-run.py script che si trova nella directory di test del modulo.
In breve, includendo un'istanza di questo file nella directory di test, puoi causare il
corridore del test per eseguire gli esempi elencati. Di solito è meglio assicurarsi che tu
selezionare esempi che abbiano tempi di esecuzione ragionevolmente brevi in ​​modo da non impantanare i test. Vedere
l'esempio in sorgente/lte/test/ directory.

Testing per booleano risultati
Testing risultati quando casualità is coinvolto
Testing produzione dati contro a conosciuto distribuzione
Fornitura non banale ingresso vettori of dati
Memorizzazione e riferimento non banale produzione dati
presentando il tuo produzione test dati
Assistenza
Creazione a nuovi NS-3 modello
Questo capitolo illustra il processo di progettazione di un NS-3 modello. In molti casi di ricerca,
gli utenti non saranno soddisfatti di adattare semplicemente i modelli esistenti, ma potrebbero voler estendere il
nucleo del simulatore in un modo nuovo. Useremo l'esempio dell'aggiunta di un ErrorModel a
semplice NS-3 link come esempio motivante di come si potrebbe affrontare questo problema e
procedere attraverso una progettazione e realizzazione.

NOTA:
Documentazione

Qui ci concentriamo sul processo di creazione di nuovi modelli e nuovi moduli, e alcuni dei
scelte progettuali coinvolte. Per ragioni di chiarezza, rimandiamo la discussione del meccanica
di documentazione dei modelli e del codice sorgente al Documentazione capitolo.

Progettazione Approccio
Considera come vuoi che funzioni; cosa dovrebbe fare. Pensa a queste cose:

· funzionalità: Che funzionalità dovrebbe avere? Che cosa sono gli attributi o la configurazione
esposto all'utente?

· riutilizzabilità: Quanto dovrebbero essere in grado gli altri di riutilizzare il mio design? Posso riutilizzare il codice da
NS-2 per iniziare? In che modo un utente integra il modello con il resto di un altro
simulazione?

· dipendenze: Come posso ridurre l'introduzione di dipendenze esterne sul mio nuovo codice
il più possibile (per renderlo più modulare)? Ad esempio, dovrei evitarne uno
dipendenza da IPv4 se voglio che venga utilizzato anche da IPv6? Dovrei evitare qualsiasi dipendenza
su IP?

Non esitare a contattare il ns-3-utenti or ns-sviluppatori elenca se hai domande.
In particolare, è importante pensare all'API pubblica del tuo nuovo modello e chiedere
feedback. Aiuta anche a far conoscere agli altri il tuo lavoro nel caso ti interessi
collaboratori.

Esempio: ErrorModel
Esiste un modello di errore in NS-2. Consente di passare i pacchetti a un oggetto con stato che
determina, in base a una variabile casuale, se il pacchetto è danneggiato. Il chiamante può
quindi decidi cosa fare con il pacchetto (rilascialo, ecc.).

L'API principale del modello di errore è una funzione a cui passare un pacchetto e il valore di ritorno di
questa funzione è un valore booleano che dice al chiamante se si è verificata una corruzione. Nota
che, a seconda del modello di errore, il buffer dei dati del pacchetto potrebbe essere danneggiato o meno.
Chiamiamo questa funzione "IsCorrupt()".

Finora, nel nostro design, abbiamo:

classe ErrorModel
{
pubblico:
/ **
* \restituisce true se il pacchetto deve essere considerato errato/corrotto
* \param pkt Pacchetto a cui applicare il modello di errore
*/
bool IsCorrupt (Ptr pacchetto);
};

Si noti che non si passa un puntatore const, consentendo così alla funzione di modificare il
pacchetto se IsCorrupt() restituisce true. Non tutti i modelli di errore modificheranno effettivamente il pacchetto;
deve essere documentato se il buffer di dati del pacchetto è danneggiato o meno.

Potremmo anche volere versioni specializzate di questo, come in NS-2, quindi anche se non è il
unica scelta progettuale per il polimorfismo, assumiamo di sottoclassare una classe base
ErrorModel per classi specializzate, come RateErrorModel, ListErrorModel e così via, come
è fatto in NS-2.

A questo punto potresti pensare: "Perché non rendere IsCorrupt() un metodo virtuale?". Questo è
un approccio; l'altro è rendere indiretta la funzione pubblica non virtuale attraverso a
funzione virtuale privata (questo in C++ è noto come idioma dell'interfaccia non virtuale ed è
adottato nel NS-3 classe ErrorModel).

Quindi, questo dispositivo dovrebbe avere dipendenze da IP o altri protocolli? Noi non vogliamo
per creare dipendenze dai protocolli Internet (il modello di errore dovrebbe essere applicabile a
anche protocolli non Internet), quindi lo terremo a mente in seguito.

Un'altra considerazione riguarda il modo in cui gli oggetti includeranno questo modello di errore. Immaginiamo di mettere
un setter esplicito in alcune implementazioni NetDevice, ad esempio.:

/ **
* Allega un ErrorModel di ricezione al PointToPointNetDevice.
*
* Il PointToPointNetDevice può opzionalmente includere un ErrorModel in
* la catena di ricezione del pacchetto.
*
* @vedi ErrorModel
* @param em Ptr a ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr em);

Ancora una volta, questa non è l'unica scelta che abbiamo (i modelli di errore potrebbero essere aggregati a molti
altri oggetti), ma soddisfa il nostro caso d'uso principale, ovvero consentire a un utente di forzare
errori su trasmissioni di pacchetti altrimenti riuscite, a livello di NetDevice.

Dopo aver riflettuto e osservato l'esistenza NS-2 codice, ecco un'API di esempio di una base
classe e prima sottoclasse che potrebbero essere pubblicate per la revisione iniziale:

classe ErrorModel
{
pubblico:
ModelloErrore ();
~ErrorModel virtuale ();
bool IsCorrupt (Ptr pacchetto);
nullo Reset (vuoto);
void Abilita (non valido);
void Disabilita (non valido);
bool IsEnabled (void) const;
privato:
bool virtuale DoCorrupt (Ptr pacchetto) = 0;
vuoto virtuale DoReset (vuoto) = 0;
};

enum ErrorUnit
{
UE_BIT,
EU_BYTE,
EU_PKT
};

// Determina quali pacchetti sono in errore corrispondenti a un sottostante
// distribuzione di variabili casuali, un tasso di errore e unità per il tasso.
classe RateErrorModel : Public ErrorModel
{
pubblico:
TassoErrorModel ();
virtuale ~RateErrorModel ();
enum ErrorUnit GetUnit (void) const;
void SetUnit (enum ErrorUnit error_unit);
doppio GetRate (vuoto) const;
void SetRate (doppio tasso);
void SetRandomVariable (const RandomVariable &ranvar);
privato:
bool virtuale DoCorrupt (Ptr pacchetto);
vuoto virtuale DoReset (vuoto);
};

impalcatura
Diciamo che sei pronto per iniziare l'implementazione; hai un quadro abbastanza chiaro di
quello che vuoi costruire e potresti aver sollecitato una recensione o dei suggerimenti iniziali
la lista. Un modo per avvicinarsi al passaggio successivo (implementazione) è creare impalcature e
compilare i dettagli man mano che il design matura.

Questa sezione illustra molti dei passaggi da considerare per definire l'impalcatura o
uno scheletro non funzionale di ciò che il tuo modello alla fine implementerà. Di solito è buono
esercitarsi a non aspettare per ottenere questi dettagli integrati alla fine, ma invece a piombo a
scheletro del tuo modello nel sistema in anticipo e quindi aggiungere funzioni in seguito una volta che l'API e
l'integrazione sembra giusta.

Nota che vorrai modificare alcune cose nella presentazione seguente per il tuo modello
poiché se segui testualmente il modello di errore, il codice che produci entrerà in collisione con il
modello di errore esistente. Quanto segue è solo uno schema di come ErrorModel è stato creato da te
può adattarsi ad altri modelli.

Review , il NS-3 codifica Style funzionalità di
A questo punto, potresti voler mettere in pausa e leggere il NS-3 documento di stile di codifica, in particolare
se stai pensando di contribuire con il tuo codice al progetto. Lo stile di codifica
il documento è collegato alla pagina principale del progetto: NS-3 codifica style.

Decide Dove in , il Fonte Albero , il Modello Qualora Risiedere
Tutto il NS-3 il codice sorgente del modello è nella directory src /. Dovrai scegliere quale
sottodirectory in cui risiede. Se si tratta di un nuovo codice modello di qualche tipo, ha senso metterlo
nella src / directory da qualche parte, in particolare per facilitare l'integrazione con la build
.

Nel caso del modello di errore, è molto correlato alla classe del pacchetto, quindi ha senso
per implementarlo nel sorgente/rete/ modulo dove NS-3 i pacchetti sono implementati.

waf e WScript
NS-3 utilizza l' Waf sistema di costruzione. Avrai voglia di integrare il tuo nuovo NS-3 usa il Waf
sistema di costruzione. Ti consigliamo di integrare i tuoi nuovi file di origine in questo sistema. Questo
richiede che tu aggiunga i tuoi file al file WScript file trovato in ogni directory.

Iniziamo con i file vuoti error-model.h e error-model.cc e aggiungiamo questo a
src/rete/wscript. Si tratta davvero solo di aggiungere il file .cc al resto del file
file di origine e il file .h nell'elenco dei file di intestazione.

Ora, apri la directory di livello superiore e digita "./test.py". Non avresti dovuto rompere
nulla con questa operazione.

Includere Guardie ✔
Quindi, aggiungiamone alcuni includere guardie nel nostro file di intestazione.:

#ifndef ERRORE_MODELLO_H
#define ERROR_MODEL_H
...
#endif

namespace ns3
NS-3 utilizza l' NS-3 namespace per isolare i suoi simboli da altri namespace. Tipicamente, un
l'utente metterà successivamente un NS-3 blocco dello spazio dei nomi in entrambi i file cc e h.:

spazio dei nomi ns3 {
...
}

A questo punto, abbiamo alcuni file scheletrici in cui possiamo iniziare a definire le nostre nuove classi.
Il file di intestazione ha questo aspetto:

#ifndef ERRORE_MODELLO_H
#define ERROR_MODEL_H

spazio dei nomi ns3 {

} // spazio dei nomi ns3
#endif

mentre errore-modello.cc il file è semplicemente simile a questo:

#include "modello-errore.h"

spazio dei nomi ns3 {

} // spazio dei nomi ns3

Questi file dovrebbero essere compilati poiché in realtà non hanno alcun contenuto. Ora siamo pronti per
inizia ad aggiungere classi.

Iniziale Implementazione/Attuazione
A questo punto stiamo ancora lavorando su alcune impalcature, ma possiamo iniziare a definire la nostra
classi, con la funzionalità da aggiungere in seguito.

Ereditare da , il Oggetto Classe?
Questa è una fase di progettazione importante; se usare la classe Oggetto come classe base per il tuo nuovo
classi.

Come descritto nel capitolo sulla NS-3 Modello a oggetti, classi che ereditano da class
Oggetto ottieni proprietà speciali:

· il NS-3 tipo e sistema di attributi (vedi attributi)

· un sistema di aggregazione di oggetti

· un sistema di conteggio dei riferimenti smart-pointer (classe Ptr)

Classi che derivano da class BaseOggetto} ottiene le prime due proprietà sopra, ma non lo fa
ottenere puntatori intelligenti. Classi che derivano da class RefCountBase ottieni solo il puntatore intelligente
sistema di conteggio di riferimento.

In pratica, classe Oggetto è la variante delle tre sopra che la NS-3 lo sviluppatore lo farà
più comunemente incontro.

Nel nostro caso, vogliamo utilizzare il sistema di attributi e passeremo le istanze
di questo oggetto attraverso il NS-3 API pubblica, quindi classe Oggetto è appropriato per noi.

Iniziale Classi
Un modo per procedere è iniziare definendo le funzioni minime e vedere se lo faranno
compilare. Esaminiamo ciò che è necessario implementare quando deriviamo dalla classe Object.:

#ifndef ERRORE_MODELLO_H
#define ERROR_MODEL_H

#include "ns3/object.h"

spazio dei nomi ns3 {

classe ErrorModel : oggetto pubblico
{
pubblico:
TypeId statico GetTypeId (vuoto);

ModelloErrore ();
~ErrorModel virtuale ();
};

classe RateErrorModel : Public ErrorModel
{
pubblico:
TypeId statico GetTypeId (vuoto);

TassoErrorModel ();
virtuale ~RateErrorModel ();
};
#endif

Alcune cose da notare qui. Dobbiamo includere oggetto.h. La convenzione nel NS-3 è che se
il file di intestazione si trova nella stessa directory, può essere incluso senza alcun percorso
prefisso. Pertanto, se stessimo implementando ErrorModel in sorgente/core/modello directory, noi
avrebbe potuto solo dire "#includere "oggetto.h"". Ma ci siamo sorgente/rete/modello, quindi dobbiamo
includilo come "#includere "ns3/oggetto.h"". Nota anche che questo va al di fuori dello spazio dei nomi
dichiarazione.

In secondo luogo, ogni classe deve implementare una funzione membro pubblica statica chiamata Ottieni IDTipo (vuoto).

Terzo, è una buona idea implementare costruttori e distruttori piuttosto che lasciare che il
compilatore li genera e per rendere virtuale il distruttore. In C++, nota anche quella copia
l'operatore di assegnazione e i costruttori di copia vengono generati automaticamente se non sono definiti, quindi
se non li vuoi, dovresti implementarli come membri privati. Questo aspetto di
Il C++ è discusso nel libro Effective C++ di Scott Meyers. voce 45.

Diamo ora un'occhiata al codice di implementazione scheletrico corrispondente nel file .cc.:

#include "modello-errore.h"

spazio dei nomi ns3 {

NS_OBJECT_ENSURE_REGISTERED (ModelloErrore);

TypeId ErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ErrorModel")
.SetParent ()
;
ritorno a mare;
}

ModelloErrore::ModelloErrore ()
{
}

ModelloErrore::~ModelloErrore ()
{
}

NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);

TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.Imposta padre ()
.Aggiungi costruttore ()
;
ritorno a mare;
}

RateErrorModel::RateErrorModel ()
{
}

RateErrorModel::~RateErrorModel ()
{
}

Qual è la Ottieni IDTipo (vuoto) funzione? Questa funzione fa alcune cose. Registra un
stringa univoca nel sistema TypeId. Stabilisce la gerarchia degli oggetti nel file
sistema di attributi (tramite Imposta genitore). Dichiara inoltre che alcuni oggetti possono essere creati tramite
il framework per la creazione di oggetti (Aggiungi costruttore).

La macro NS_OBJECT_ENSURE_REGISTERED (nome della classe) è necessario anche una volta per ogni classe che
definisce un nuovo metodo GetTypeId ed esegue la registrazione effettiva della classe in
sistema. Il capitolo sul modello a oggetti ne discute in modo più dettagliato.

Compreso Esterno File
Registrazione Assistenza
Qui, scrivere a bit circa l'aggiunta di |ns3| registrazione macro. Note: che LOG_COMPONENT_DEFINE is
fatto al di fuori , il namespace ns3

Costruttore, Vuoto Funzione prototipi
Le Variabili (Predefinito I valori, attributi)
Test Programma 1
Oggetto Contesto
Aggiunta a Campione Copione
A questo punto, si potrebbe provare a prendere l'impalcatura di base definita sopra e aggiungerla
nel sistema. L'esecuzione di questo passaggio ora consente di utilizzare un modello più semplice durante l'impianto idraulico
nel sistema e può anche rivelare se è necessario apportare modifiche al design o all'API
fatto. Fatto ciò, torneremo a sviluppare le funzionalità del
ErrorModels stessi.

Aggiungi Basic Assistenza in , il Classe
/* dispositivo-di-rete-punto-punto.h */
classe ErrorModel;

/ **
* Modello di errore per eventi di pacchetti di ricezione
*/
pt m_receiveErrorModel;

Aggiungi Accessorio
nulla
PointToPointNetDevice::SetReceiveErrorModel (Ptr em)
{
NS_LOG_FUNCTION (questo << em);
m_receiveErrorModel = em;
}

.AddAttribute ("ReceiveErrorModel",
"Il modello di errore del ricevitore utilizzato per simulare la perdita di pacchetti",
ValorePuntatore (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker ())

Filo a piombo In , il Sistema
void PointToPointNetDevice::Receive (Ptr pacchetto)
{
NS_LOG_FUNCTION (questo << pacchetto);
protocollo uint16_t = 0;

if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (pacchetto) )
{
//
// Se abbiamo un modello di errore e indica che è ora di perdere a
// pacchetto danneggiato, non inoltrare questo pacchetto, lascialo andare.
//
m_dropTrace (pacchetto);
}
altro
{
//
// Premi l'hook della traccia di ricezione, rimuovi l'intestazione del protocollo point-to-point
// e inoltra questo pacchetto nello stack del protocollo.
//
m_rxTrace (pacchetto);
ProcessHeader(pacchetto, protocollo);
m_rxCallback (questo, pacchetto, protocollo, GetRemote());
se (!m_promiscCallback.IsNull ())
{ m_promiscCallback (questo, pacchetto, protocollo, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}

Creare Nullo Funzionale Copione
/* modello-errore-semplice.cc */

// Modello di errore
// Vogliamo aggiungere un modello di errore al NetDevice del nodo 3
// Possiamo ottenere un handle per NetDevice tramite il canale e il nodo
// puntatori
pt nd3 = PointToPointTopology::GetNetDevice
(n3, canale2);
pt em = Crea ();
nd3->SetReceiveErrorModel (em);

bool
ErrorModel::DoCorrupt (pacchetto& p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("Corrotto!");
return false;
}

A questo punto, possiamo eseguire il programma con il nostro banale ErrorModel inserito nella ricezione
percorso del PointToPointNetDevice. Stampa la stringa "Corrupt!" per ogni pacchetto
ricevuto al nodo n3. Successivamente, torniamo al modello di errore per aggiungere una sottoclasse che funzioni
modellazione degli errori più interessante.

Aggiungi a sottoclasse
La banale classe base ErrorModel non fa nulla di interessante, ma fornisce a
utile interfaccia di classe base (Corrupt() e Reset()), inoltrata a funzioni virtuali che
possono essere sottoclassi. Consideriamo quindi quello che chiamiamo BasicErrorModel su cui si basa
, il NS-2 Classe ErrorModel (in ns-2/queue/errmodel.{cc,h}).

Quali proprietà vogliamo che questo abbia, dal punto di vista dell'interfaccia utente? Vorremmo
affinché l'utente sia in grado di sostituire banalmente il tipo di ErrorModel utilizzato nel file
NetDevice. Vorremmo anche la possibilità di impostare parametri configurabili.

Ecco alcuni semplici requisiti che prenderemo in considerazione:

· Possibilità di impostare la variabile casuale che governa le perdite (di default è UniformVariable)

· Possibilità di impostare l'unità (bit, byte, pacchetto, tempo) di granularità su cui si verificano gli errori
applicato.

· Possibilità di impostare il tasso di errori (es. 10^-3) corrispondente all'unità di
granularità.

· Possibilità di abilitare/disabilitare (l'impostazione predefinita è abilitata)

Come a sottoclasse
Dichiariamo BasicErrorModel come una sottoclasse di ErrorModel come segue:

classe BasicErrorModel : pubblico ErrorModel
{
pubblico:
TypeId statico GetTypeId (vuoto);
...
privato:
// Implementa le funzioni virtuali pure della classe base
bool virtuale DoCorrupt (Ptr p);
bool virtuale DoReset (nullo);
...
}

e configurare la funzione GetTypeId della sottoclasse impostando una stringa TypeId univoca e
impostando il genitore su ErrorModel:

TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.Imposta padre ()
.Aggiungi costruttore ()
...

Silhouette Nucleo funzioni e Unità Test
Affermare Macro
scrittura Unità Test
Aggiunta a Nuovo Moduli a NS-3
Quando hai creato un gruppo di classi, esempi e test correlati, possono esserlo
combinati insieme in un NS-3 modulo in modo che possano essere utilizzati con esistenti NS-3 moduli
e da altri ricercatori.

Questo capitolo illustra i passaggi necessari per aggiungere un nuovo modulo NS-3.

step 0 - Moduli disposizione
Tutti i moduli possono essere trovati in src directory. Ciascun modulo può essere trovato in una directory
che ha lo stesso nome del modulo. Ad esempio, il spettro modulo può essere trovato qui:
sorgente/spettro. Citeremo dal spettro modulo per l'illustrazione.

Un modulo prototipo ha la seguente struttura di directory e file richiesti:

src /
nome-modulo/
attacchi/
documento/
esempi /
WScript
aiutante/
modello/
test/
esempi-to-run.py
WScript

Non tutte le directory saranno presenti in ogni modulo.

step 1 - Creare a Moduli Scheletro
Un programma Python è fornito nella directory di origine che creerà uno scheletro per un nuovo
modulo. Ai fini di questa discussione, assumeremo che il tuo nuovo modulo sia chiamato
nuovo modulo. Dal src directory, procedere come segue per creare il nuovo modulo:

$ ./create-module.py nuovo-modulo

Il prossimo, cd ai miglioramenti nuovo modulo; troverai questo layout di directory:

$ cd nuovo modulo
$ l
doc esempi modello di supporto test wscript

Più in dettaglio, il crea-modulo.py script creerà le directory così come l'iniziale
scheletro WScript, .h, . Cc e .primo File. Il modulo completo con i file scheletro appare
come questo:

src /
nuovo modulo/
documento/
nuovo-modulo.rst
esempi /
esempio-nuovo-modulo.cc
WScript
aiutante/
nuovo-module-helper.cc
nuovo-modulo-helper.h
modello/
nuovo-modulo.cc
nuovo-modulo.h
test/
nuovo-modulo-test-suite.cc
WScript

(Se richiesto il attacchi/ directory elencata in Passo 0 verrà creato automaticamente durante
la costruzione.)

Passiamo ora a come personalizzare questo modulo. Informare waf sui file che
comporre il tuo modulo è fatto modificando i due WScript File. Cammineremo attraverso il
passaggi principali di questo capitolo.

Tutto NS-3 i moduli dipendono dal core modulo e solitamente su altri moduli. Questa dipendenza
è specificato nel WScript file (al livello superiore del modulo, non il file separato WScript
file nella Esempi directory!). Nello scheletro WScript la chiamata che dichiarerà il tuo
nuovo modulo a waf sarà simile a questo (prima della modifica):

def build(bld):
module = bld.create_ns3_module('new-module', ['core'])

Supponiamo che nuovo modulo dipende dal Internet, mobilità e avv moduli. Dopo
modificandolo il WScript il file dovrebbe essere simile a:

def build(bld):
module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv'])

Nota che dovrebbero essere elencate solo le dipendenze del modulo di primo livello, motivo per cui abbiamo rimosso
core; il Internet modulo a sua volta dipende core.

Molto probabilmente il tuo modulo avrà file di origine del modello. Scheletri iniziali (che lo faranno
compilare correttamente) vengono creati in modello/nuovo-modulo.cc e modello/nuovo-modulo.h.

Se il tuo modulo avrà file sorgente di supporto, andranno in aiutante/
directory; di nuovo, gli scheletri iniziali vengono creati in quella directory.

Infine, è buona norma scrivere test ed esempi. Quasi sicuramente lo saranno
necessario per l'accettazione di nuovi moduli nell'ufficiale NS-3 albero di origine. Uno scheletro
la suite di test e il test case vengono creati nel file test/ directory. La suite per il test dello scheletro lo farà
contengono il costruttore seguente, che dichiara un nuovo unit test denominato nuovo modulo, Con
singolo test case costituito dalla classe NuovoModuloTestCase1:

NewModuleTestSuite::NewModuleTestSuite ()
: TestSuite ("nuovo modulo", UNIT)
{
AddTestCase (nuovo NewModuleTestCase1);
}

step 3 - Dichiarare Fonte File
L'intestazione pubblica e i file del codice sorgente per il tuo nuovo modulo devono essere specificati in
WScript file modificandolo con il tuo editor di testo.

Ad esempio, dopo aver dichiarato il spettro modulo, il src/spettro/wscript specifica il
file di codice sorgente con il seguente elenco:

def build(bld):

module = bld.create_ns3_module('spettro', ['internet', 'propagazione', 'antenna', 'applicazioni'])

sorgente.modulo = [
'modello/modello-spettro.cc',
'modello/valore-spettro.cc',
.
.
.
'modello/forno-microonde-spettro-valore-helper.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
'helper/waveform-generator-helper.cc',
'helper/analizzatore-di-spettro-helper.cc',
]

Gli oggetti risultanti dalla compilazione di questi sorgenti verranno assemblati in una libreria di link,
che sarà collegato a tutti i programmi che si basano su questo modulo.

Ma come fanno questi programmi ad apprendere l'API pubblica del nostro nuovo modulo? Continuare a leggere!

step 4 - Dichiarare Pubblico testata File
Dovrebbero esserlo anche i file di intestazione che definiscono l'API pubblica del modello e gli helper
specificato in WScript file.

Continuando con il spettro illustrazione del modello, vengono specificati i file di intestazione pubblici
con la seguente strofa. (Si noti che l'argomento al bld la funzione dice waf a
installa le intestazioni di questo modulo con l'altro NS-3 intestazioni):

headers = bld(features='ns3header')

headers.module = 'spettro'

intestazioni.source = [
'modello/modello-spettro.h',
'modello/valore-spettro.h',
.
.
.
'modello/forno-microonde-spettro-valore-helper.h',
'helper/spectrum-helper.h',
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
'helper/waveform-generator-helper.h',
'helper/analizzatore-di-spettro-helper.h',
]

Le intestazioni rese pubbliche in questo modo saranno accessibili agli utenti del tuo modello con include
affermazioni come

#include "ns3/spectrum-model.h"

Le intestazioni utilizzate rigorosamente internamente nell'implementazione non devono essere incluse qui. Essi
sono ancora accessibili alla tua implementazione includendo istruzioni come

#include "my-module-implementation.h"

step 5 - Dichiarare Test
Se il tuo nuovo modulo ha dei test, allora devono essere specificati nel tuo WScript file per
modificandolo con il tuo editor di testo.

Il spettro i test di modello sono specificati con la seguente stanza:

module_test = bld.create_ns3_module_test_library('spettro')

modulo_test.source = [
'test/spettro-interferenza-test.cc',
'test/valore-spettro-test.cc',
]

See Test per ulteriori informazioni su come scrivere casi di test.

step 6 - Dichiarare Esempi
Se il tuo nuovo modulo ha esempi, allora devono essere specificati nel tuo esempi/wscript
file. (Lo scheletro di primo livello WScript includerà ricorsivamente esempi/wscript solo se
gli esempi sono stati abilitati al momento della configurazione.)

Il spettro model definisce il suo primo esempio in src/spettro/esempi/wscript con

def build(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideale-phy',
['spettro', 'mobilità'])
obj.source = 'adhoc-aloha-ideale-phy.cc'

Si noti che il secondo argomento della funzione create_ns3_programma() è l'elenco dei moduli
da cui dipende il programma in fase di creazione; ancora una volta, non dimenticare di includere nuovo modulo in
la lista. È buona norma elencare solo le dipendenze dirette del modulo e lasciare waf
dedurre l'albero delle dipendenze completo.

Occasionalmente, per chiarezza, potresti voler dividere l'implementazione per il tuo esempio tra
diversi file di origine. In questo caso, includi semplicemente quei file come espliciti aggiuntivi
fonti dell'esempio:

obj = bld.create_ns3_program('nuovo-modulo-esempio', [nuovo-modulo])
obj.source = ['new-module-example.cc', 'new-module-example-part.cc']

Gli esempi Python sono specificati usando la seguente chiamata di funzione. Si noti che il secondo
argomento per la funzione Register_ns3_script() è l'elenco dei moduli che Python
esempio dipende da:

bld.register_ns3_script('nuovo-modulo-esempio.py', ['nuovo-modulo'])

step 7 - Esempi Correre as Test
Oltre all'esecuzione di codice di test esplicito, è anche possibile strumentare il framework di test
eseguire programmi di esempio completi per cercare di catturare le regressioni negli esempi. Tuttavia, non tutti
gli esempi sono adatti per i test di regressione. Il file test/esempi-da-eseguire.py controlla il
invocazione degli esempi durante l'esecuzione del framework di test.

Il spettro esempi di modelli gestiti da prova.py sono specificati in
src/spectrum/test/examples-to-run.py utilizzando i seguenti due elenchi di C++ e Python
esempi:

# Un elenco di esempi C++ da eseguire per garantire che rimangano
# costruibile e percorribile nel tempo. Ogni tupla nell'elenco contiene
#
# (nome_esempio, do_run, do_valgrind_run).
#
# Vedere test.py per ulteriori informazioni.
cpp_esempi = [
("adhoc-aloha-ideale-phy", "Vero", "Vero"),
("adhoc-aloha-ideale-phy-with-forno-microonde", "True", "True"),
("adhoc-aloha-ideale-phy-matrix-propagation-loss-model", "Vero", "Vero"),
]

# Un elenco di esempi Python da eseguire per assicurarsi che rimangano
# percorribile nel tempo. Ogni tupla nell'elenco contiene
#
# (nome_esempio, esegui_esegui).
#
# Vedere test.py per ulteriori informazioni.
esempi_python = [
("sample-simulator.py", "True"),
]

Come indicato nel commento, ogni voce nell'elenco di esempi C++ da eseguire contiene il file
tupla (nome_esempio, fare_correre, do_valgrind_run), Dove

· nome_esempio è l'eseguibile da eseguire,

· fare_correre è una condizione in cui eseguire l'esempio, e

· do_valgrind_run è una condizione in cui eseguire l'esempio in valgrind. (Questo
è necessario perché NSC provoca arresti anomali di istruzioni illegali con alcuni test quando
sono eseguiti sotto valgrind.)

Nota che le due condizioni sono istruzioni Python da cui possono dipendere waf configurazione
variabili. Per esempio,

("tcp-nsc-lfn", "NSC_ENABLED == Vero", "NSC_ENABLED == Falso"),

Ogni voce nell'elenco Python di esempi da eseguire contiene la tupla (nome_esempio,
fare_correre), dove, come per gli esempi C++,

· nome_esempio è lo script Python da eseguire e

· fare_correre è una condizione in cui eseguire l'esempio.

Ancora una volta, la condizione è un'istruzione Python da cui può dipendere waf variabili di configurazione.
Per esempio,

("realtime-udp-echo.py", "ENABLE_REAL_TIME == Falso"),

step 8 - Configurazione e Silhouette
Ora puoi configurare, costruire e testare il tuo modulo normalmente. È necessario riconfigurare il
progetto come primo passo in modo che waf memorizza nella cache le nuove informazioni nel tuo WScript file o
altrimenti il ​​tuo nuovo modulo non sarà incluso nella build.

$ ./waf configure --enable-examples --enable-tests
$ ./waf build
$ ./prova.py

Cerca la suite di test del tuo nuovo modulo (e i programmi di esempio, se il tuo modulo ne ha
abilitato) nell'uscita di test.

step 9 - Python Associazioni
L'aggiunta di collegamenti Python al modulo è facoltativa e il passaggio è commentato da
predefinito nel crea-modulo.py script.

# bld.ns3_python_bindings()

Se vuoi includere i collegamenti Python (necessari solo se vuoi scrivere Python ns-3
programmi invece dei programmi C++ ns-3), è necessario decommentare quanto sopra e installare il file
Sistema di scansione dell'API Python (trattato altrove in questo manuale) e scansiona il tuo modulo su
generare nuove associazioni.

Creazione Documentazione
NS-3 fornisce due tipi di documentazione: capitoli espositivi in ​​stile "guida per l'utente" e
documentazione dell'API del codice sorgente.

I capitoli della "guida per l'utente" sono scritti a mano testoristrutturato formato (.primo), che è
elaborato dal sistema di documentazione Python Sfinge per generare pagine web e file pdf.
La documentazione dell'API viene generata dal codice sorgente stesso, utilizzando Doxygen, generare
pagine web con collegamenti incrociati. Entrambi sono importanti: i capitoli Sfinge spiegano il perché
e panoramica dell'utilizzo di un modello; la documentazione dell'API spiega il come dettagli.

Questo capitolo fornisce una rapida panoramica di questi strumenti, sottolineando l'utilizzo preferito e
personalizzazioni per NS-3.

Per costruire tutta la documentazione standard:

$ ./waf documenti

Per opzioni più specializzate, continua a leggere.

Documentare con Sfinge
Usiamo Sfinge generare capitoli espositivi che descrivono il design e l'utilizzo di ciascuno
modulo. In questo momento stai leggendo il Documentazione Capitolo. Il Mostra Fonte anello della
la barra laterale ti mostrerà il sorgente di reStructuredText per questo capitolo.

Aggiunta Nuovo capitoli
L'aggiunta di un nuovo capitolo richiede tre passaggi (descritti più dettagliatamente di seguito):

1. Scegliere Dove? i file di documentazione rimarranno attivi.

2. Link da una pagina esistente alla nuova documentazione.

3. Aggiungi il nuovo file a Makefile.

Dove?
Documentazione per un modulo specifico, foo, dovrebbe normalmente entrare src/pippo/doc/. Per esempio
src/foo/doc/foo.rst sarebbe il documento di primo livello per il modulo. Il
src/crea-module.py script creerà questo file per te.

Alcuni modelli ne richiedono diversi .primo fascicoli e figure; questi dovrebbero andare tutti nel
src/pippo/doc/ directory. I documenti sono in realtà creati da un Makefile Sphinx. Per specialmente
documentazione coinvolta, può essere utile avere un locale Makefile nel src/pippo/doc/
directory per semplificare la creazione della documentazione per questo modulo (Antenna è un esempio).
L'impostazione non è particolarmente difficile, ma va oltre lo scopo di questo capitolo.

In alcuni casi, la documentazione copre più modelli; il Network NetPoulSafe il capitolo è un esempio. In
questi casi aggiungendo il .primo file direttamente in doc/modelli/fonte/ potrebbe essere appropriato.

Link
La sfinge deve sapere where il tuo nuovo capitolo dovrebbe apparire. Nella maggior parte dei casi, un nuovo modello
il capitolo dovrebbe apparire in Modelli prenotare. Per aggiungere il tuo capitolo lì, modifica
doc/models/source/index.rst

..toctree::
:profondità massima: 1

organizzazione
animazione
antenna
avv
applicazioni
...

Aggiungi il nome del tuo documento (senza il .primo estensione) a questo elenco. Si prega di conservare il
Modella i capitoli in ordine alfabetico, per facilitare la scansione visiva di capitoli specifici.

Makefile
Devi anche aggiungere il tuo documento all'appropriato Makefile, Così make sa controllarlo
per gli aggiornamenti. Il libro dei modelli Makefile è doc/modelli/Makefile, il Makefile del libro manuale è
doc/manuale/Makefile.

# elenca tutti i file .rst della libreria di modelli che devono essere copiati in $SOURCETEMP
FONTI = \
sorgente/conf.py \
sorgente/_statico \
sorgente/indice.primo \
sorgente/sostituisci.txt \
fonte/organizzazione.prima \
...
$(SRC)/antenna/doc/sorgente/antenna.rst \
...

Tu aggiungi il tuo .primo file al FONTI variabile. Per aggiungere cifre, leggere i commenti in
Makefile per vedere quale variabile dovrebbe contenere i tuoi file di immagine. Ancora una volta, per favore conserva questi
in ordine alfabetico.

Costruzione Sfinge Docs
Costruire la documentazione di Sphinx è piuttosto semplice. Per costruire tutta la Sfinge
documentazione:

$ ./sfinge waf

Per creare solo la documentazione dei modelli:

$ make -C doc/modelli

Per vedere la documentazione generata, punta il tuo browser a doc/modelli/build/html.

Come puoi vedere, Sphinx usa Make per guidare il processo. La destinazione predefinita compila tutto
moduli di output abilitati, che in NS-3 sono le multipagina html, pagina singola singolohtml e
PDF (latice). Per creare solo l'html multipagina, aggiungi il file html bersaglio:

$ make -C doc/modelli html

Questo può essere utile per ridurre il tempo di costruzione (e la dimensione delle chiacchiere di costruzione) come te
stanno scrivendo il tuo capitolo

Prima di inviare la documentazione al repository, verifica che venga compilata senza
errori o avvisi. Il processo di compilazione genera molto output (per lo più normali chatter
da LaTeX), il che può rendere difficile vedere se sono presenti avvisi Sphinx o
errori. Per trovare avvisi ed errori importanti, crea solo il file html versione, quindi cerca
il registro di compilazione per identificazione dei warning or errore.

NS-3 Specifiche
La Sfinge documentazione e lezione sono abbastanza buoni. Non duplichiamo le basi
qui, concentrandosi invece sull'uso preferito per NS-3.

· Inizia i documenti con queste due righe:

.. include:: sostituisci.txt
.. evidenzia:: cpp

La prima riga consente alcune semplici sostituzioni. Ad esempio, digitando |ns3| rende come
NS-3. Il secondo imposta esplicitamente la lingua di evidenziazione del codice sorgente per il
file, poiché l'ipotesi del parser non è sempre accurata. (È anche possibile impostare il
lingua in modo esplicito per un singolo blocco di codice, vedere di seguito.)

· Sezioni:

Sphinx è piuttosto liberale nel contrassegnare i titoli delle sezioni. Per convenzione preferiamo questo
gerarchia:

.. gerarchia delle intestazioni:
------------- Capitolo
************* Sezione (#.#)
============== Sottosezione (#.#.#)
############## Sottosottosezione

· Evidenziazione della sintassi:

Per utilizzare l'evidenziatore di sintassi predefinito, avvia semplicemente un blocco di codice sorgente:

┌ronicheranno ───────────────────────────────┐
│Sorgente Sfinge │ Output renderizzato │
├ronicheranno ───────────────────────────────┤
│ │ Il Frobnitz vi accede: │
│ A ``Frobnitz`` si accede da: │ │
│ │ Foo::Frobnitz frob; │
│ Foo::Frobnitz frob; │ frob.Insieme (...); │
│ frob.Insieme (...); │ │
└ronicheranno ───────────────────────────────┘

Per utilizzare un evidenziatore di sintassi specifico, ad esempio, bash comandi della shell:

┌ronicheranno ───┐
│Sorgente Sfinge │ Output renderizzato │
├ronicheranno ───┤
│ │ │
│ .. codice sorgente:: bash │ $ ls │
│ │ │
│ $ ls │ │
└ronicheranno ───┘

· Notazioni abbreviate:

Queste abbreviazioni sono definite:

┌───────────────────────┬───────────────────────────
│Sorgente Sfinge │ Output renderizzato │
├───────────────────────┼───────────────────────────
│ │ NS-3
│ |ns3| │ │
├───────────────────────┼───────────────────────────
│ │ NS-2
│ |ns2| │ │
├───────────────────────┼───────────────────────────
│ │ │
│ |verifica| │ │
├───────────────────────┼───────────────────────────
│ │ RFC 6282
│ :rfc:`6282` │ │
└───────────────────────┴───────────────────────────────

Documentare con Doxygen
Usiamo Doxygen generare sfogliabile Documentazione API. Doxygen fornisce un certo numero di
caratteristiche utili:

· Tabella riassuntiva di tutti i membri della classe.

· Grafici di eredità e collaborazione per tutte le classi.

· Collegamenti al codice sorgente che implementa ciascuna funzione.

· Collegamenti a ogni luogo in cui viene utilizzato un membro.

· Collegamenti a ogni oggetto utilizzato nell'implementazione di una funzione.

· Raggruppamento di classi correlate, come tutte le classi relative a un protocollo specifico.

Inoltre, utilizziamo il ID tipo sistema da aggiungere alla documentazione per ogni classe

· Il Config percorsi attraverso i quali tali oggetti possono essere raggiunti.

· Documentazione per eventuali Attributi, Compreso Attributi definiti nelle classi madri.

· Documentazione per eventuali Traccia sorgenti definite dalla classe.

Doxygen opera scansionando il codice sorgente, cercando commenti appositamente contrassegnati. Esso
crea anche un riferimento incrociato, indicando where ogni file, classe, metodo e variabile è
Usato.

Preferito Style
Lo stile preferito per i commenti Doxygen è lo stile JavaDoc:

/ **
* Breve descrizione di questa classe o metodo.
* Le righe adiacenti diventano un unico paragrafo.
*
* Descrizione più lunga, con molti dettagli.
*
* Le righe vuote separano i paragrafi.
*
* Spiega cosa fa la classe o il metodo, usando quale algoritmo.
* Spiega le unità degli argomenti e restituisce i valori.
*
* \note Nota eventuali limitazioni o trucchi.
*
* (Per funzioni con argomenti o valore restituito :)
* \param foo Breve frase nominale che descrive questo argomento.
* \param bar Nota Frase maiuscola e periodo di terminazione.
* \return Breve frase nominale che descrive il valore.
*
* \interno
*
* Puoi anche discutere i dettagli di implementazione interna.
* La comprensione di questo materiale non dovrebbe essere necessaria per l'utilizzo
* la classe o il metodo.
*/
classe Esempio

In questo stile il blocco dei commenti Doxygen inizia con due caratteri `*': / **, e precede
l'oggetto da documentare.

Per gli articoli che richiedono solo una breve descrizione, è appropriato uno di questi brevi moduli:

/** Implementazione del distruttore. */
void DoDispose ();

int m_count; //!< Conteggio di ...

Nota la forma speciale del commento di fine riga, //!, indicando che si riferisce al
precedente articolo.

Alcuni elementi da notare:

· Utilizzare la frase maiuscola, inclusa la maiuscola iniziale.

· Usa la punteggiatura, in particolare `.'s alla fine di frasi o frasi.

· Il \breve il tag non è necessario; la prima frase sarà usata come riassunto
descrizione.

Ogni classe, metodo, typedef, variabile membro, argomento di funzione e valore restituito dovrebbe
essere documentato in tutti i file di codice sorgente che costituiscono l'API formale e l'implementazione per
NS-3, come fonte/ /modello/*, fonte/ /aiutante/* e fonte/ /utility/*.
Documentazione per gli articoli in fonte/ /test/* e fonte/ /esempi/* è preferito,
ma non richiesto.

Utile Caratteristiche
· I membri ereditati erediteranno automaticamente i documenti dal genitore (ma possono essere sostituiti
dalla documentazione locale).

1. Documenta la classe base.

2. Nella sottoclasse contrassegna le funzioni ereditate con un commento ordinario:

// Metodi ereditati
vuoto virtuale FooBar (vuoto);
int virtuale BarFoo (doppio baz);

Nota che le firme devono corrispondere esattamente, quindi includi l'argomento formale (vuoto)

Questo non funziona per le funzioni statiche; vedere Ottieni IDTipo, di seguito, per un esempio.

Costruzione Doxygen Docs
Costruire la documentazione di Doxygen è piuttosto semplice:

$ ./waf dossigeno

Questo viene compilato utilizzando la configurazione predefinita, che genera sezioni di documentazione per
contro tutti i elementi, anche se non hanno blocchi di documentazione con commenti espliciti. Questo ha il
effetto della soppressione degli avvisi per gli articoli non documentati, ma assicura che tutto appaia
nell'output generato.

Quando si scrive documentazione, è spesso più utile vedere quali elementi stanno generando
avvisi, in genere sulla documentazione mancante. Per visualizzare l'elenco completo degli avvisi, utilizzare il
doc/doxygen.warnings.report.sh sceneggiatura:

$ doc/doxygen.warnings.report.sh
Waf: entrare nella directory "build"
...
Waf: uscita dalla directory "build"
'build' è terminato con successo (3m24.094s)

Ricostruzione di documenti doxygen con errori completi... Fatto.

Rapporto sugli avvisi di Doxygen
----------------------------------------

(Tutti i conteggi sono limiti inferiori.)

Avvisi per modulo/directory:

Conte Directory
----- -----------------------------------
3844 src/lte/modello
1718 src/wimax/modello
1423 src/core/modello
....
138 parametri aggiuntivi non documentati.
----------------------------------------
15765 avvisi totali
126 directory con avvisi

Avvisi per file (in ordine alfabetico)

Conte file
----- -----------------------------------
17 doc/introspected-doxygen.h
15 esempi/routing/manet-routing-compare.cc
26 esempi/stats/wifi-example-apps.h
....
----------------------------------------
967 file con avvisi

Avvisi per file (numerico)

Conte file
----- -----------------------------------
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 file con avvisi

Riepilogo Avvertenze Doxygen
----------------------------------------
126 elenchi
File 967
15765 avvisi

Lo script modifica la configurazione per mostrare tutti gli avvisi e per ridurre il tempo di esecuzione.
Come puoi vedere, in questo momento abbiamo a lotto di oggetti non documentati. Il rapporto
riassume gli avvisi per modulo sorgente/*/*, e per file, in ordine alfabetico e numerico.

Lo script ha alcune opzioni per ridurre le cose e renderlo più gestibile. Per un aiuto,
Usa il -h opzione. Dopo averlo eseguito una volta per eseguire la build Doxygen e generare il completo
registro degli avvisi, è possibile rielaborare il file di registro con vari "filtri", senza doverlo fare
la build completa di Doxygen, sempre utilizzando il -s opzione. È possibile escludere avvisi da
*/esempi/* File (-e opzione) e/o */test/* File (-t).

Forse l'opzione più utile quando si scrivono commenti sulla documentazione è -m , quale
limiterà il rapporto alla sola corrispondenza dei file fonte/ /*e segui il rapporto con
le linee di avviso effettive. Unisci con -e e puoi concentrarti sugli avvisi che sono
più urgenti in un unico modulo:

$ doc/doxygen.warnings.report.sh -m mesh/helper
...
Riepilogo Avvertenze Doxygen
----------------------------------------
1 elenchi
File 3
149 avvisi

Avvisi filtrati
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: avviso: il membro m_root (variabile) della classe ns3::Dot11sStack non è documentato.
src/mesh/helper/dot11s/dot11s-installer.h:35: avviso: il tipo restituito del membro ns3::Dot11sStack::GetTypeId non è documentato
src/mesh/helper/dot11s/dot11s-installer.h:56: avviso: il tipo restituito del membro ns3::Dot11sStack::InstallStack non è documentato
src/mesh/helper/flame/lfame-installer.h:40: avviso: il membro GetTypeId() (funzione) della classe ns3::FlameStack non è documentato.
src/mesh/helper/flame/flame-installer.h:60: avviso: il tipo restituito del membro ns3::FlameStack::InstallStack non è documentato
src/mesh/helper/mesh-helper.h:213: avviso: il membro m_nInterfaces (variabile) della classe ns3::MeshHelper non è documentato.
src/mesh/helper/mesh-helper.h:214: avviso: Il membro m_spreadChannelPolicy (variabile) della classe ns3::MeshHelper non è documentato.
src/mesh/helper/mesh-helper.h:215: avviso: Il membro m_stack (variabile) della classe ns3::MeshHelper non è documentato.
src/mesh/helper/mesh-helper.h:216: avviso: il membro m_stackFactory (variabile) della classe ns3::MeshHelper non è documentato.
src/mesh/helper/mesh-helper.h:209: avviso: i parametri del membro ns3::MeshHelper::CreateInterface non sono (tutti) documentati
src/mesh/helper/mesh-helper.h:119: avviso: i parametri del membro ns3::MeshHelper::SetStandard non sono (tutti) documentati

Ora è solo questione di capire il codice e scrivere alcuni documenti!

NS-3 Specifiche
Per quanto riguarda la Sfinge, il Doxygen docs e riferimento sono abbastanza buoni. Non duplichiamo il
nozioni di base qui, concentrandosi invece sull'uso preferito per NS-3.

· Utilizzare Doxygen moduli per raggruppare elementi correlati.

Nell'intestazione principale di un modulo, crea un gruppo Doxgyen:

/ **
* \defgroup foo Protocollo Foo.
*/

Contrassegna ogni classe associata come appartenente al gruppo:

/ **
* \ingroup pippo
*
* Tipo di pacchetto Foo.
*/
classe Foo

· Lo sapevate typedef può avere argomentazioni formali? Ciò consente la documentazione della funzione
firme del puntatore:

/ **
* Barra della firma della funzione di richiamata.
*
* \param ale La dimensione di una pinta di birra, in once imperiali.
*/
typedef void (* BarCallback)(const int ale);

· Copiare il Attributo stringhe di aiuto dal Ottieni IDTipo metodo da utilizzare come brief
descrizioni dei membri associati.

· \bugid{298} creerà un collegamento al bug 298 nel nostro Bugzilla.

· \pname{pippo} in una descrizione verrà formattata foo come \param foo parametro, rendendolo chiaro
che ti riferisci a un argomento reale.

· \RFC{301} creerà un collegamento a RFC 301.

· \interno dovrebbe essere utilizzato solo per avviare una discussione sui dettagli di attuazione, non per
marchio un bagno funzioni (sono già contrassegnate, come un bagno!)

· Non creare classi con nomi banali, come classe A, anche nelle suite di test. Queste
fare in modo che tutte le istanze del nome di classe letterale "A" vengano visualizzate come collegamenti.

Come notato sopra, le funzioni statiche non ereditano la documentazione delle stesse funzioni in
la classe madre. NS-3 utilizza alcune funzioni statiche in modo ubiquo; il suggerito
blocco di documentazione per questi casi è:

· Costruttore/distruttore predefinito:

La mia classe (); //!< Costruttore predefinito
~MiaClasse (); //!< Distruttore

· Dummy distruttore e DoDispose:

/** Distruttore fittizio, vedere DoDispose. */
~MiaClasse ();

/** Implementazione del distruttore */
vuoto virtuale DoDispose ();

· GetTypeId:

/ **
* Registra questo tipo.
* \return L'oggetto TypeId.
*/
TypeId statico GetTypeId (vuoto);

Abilitare Sottoinsiemi of NS-3 moduli
Come con la maggior parte dei progetti software, NS-3 è sempre più grande in termini di numero di moduli,
righe di codice e footprint di memoria. Gli utenti, tuttavia, possono utilizzare solo alcuni di questi moduli
Al tempo. Per questo motivo, gli utenti potrebbero voler abilitare in modo esplicito solo il sottoinsieme di
possibile NS-3 moduli di cui hanno effettivamente bisogno per la loro ricerca.

In questo capitolo viene descritto come abilitare solo il file NS-3 moduli che ti interessano
utilizzando.

Come a enable a sottoinsieme of NS-3's moduli
Se vengono create librerie condivise, l'abilitazione di un modulo ne causerà almeno uno
libreria da costruire:

libns3-nomemodulo.so

Se il modulo ha una libreria di test e le librerie di test sono in fase di compilazione, allora

libns3-nomemodulo-test.so

verrà costruito anch'esso. Altri moduli da cui dipende il modulo e le relative librerie di test
sarà anche costruito.

Per impostazione predefinita, tutti i moduli sono integrati NS-3. Ci sono due modi per abilitare un sottoinsieme di questi
moduli:

1. Usando l'opzione --enable-modules di waf

2. Usando il NS-3 file di configurazione

Consentire a tutti moduli utilizzando waf --enable-moduli opzione
Per abilitare solo il modulo principale con esempio e test, ad esempio, prova questi comandi:

$ ./waf pulito
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf build
$ cd build/debug/
$ l

e dovrebbero essere presenti le seguenti librerie:

binding libns3-core.so ns3 scratch utils
esempi libns3-core-test.so campioni src

Notare la ./waf cavedano passo viene fatto qui solo per rendere più ovvio quali librerie di moduli
sono stati costruiti. Non devi fare ./waf cavedano per abilitare sottoinsiemi di moduli.

L'esecuzione di test.py causerà l'esecuzione solo di quei test che dipendono dal core del modulo:

24 test su 24 superato (24 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Ripetere i passaggi precedenti per il modulo "network" anziché per il modulo "core" e il file
verrà costruito quanto segue, poiché la rete dipende dal core:

binding libns3-core.so libns3-network.so ns3 scratch utils
esempi libns3-core-test.so libns3-network-test.so campioni src

L'esecuzione di test.py causerà quei test che dipendono solo dai moduli core e di rete
essere eseguito:

31 test su 31 superato (31 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Consentire a tutti moduli utilizzando , il NS-3 configurazione filetto
È stato aggiunto un file di configurazione, .ns3rc NS-3 che consente agli utenti di specificare quale
i moduli devono essere inclusi nella build.

Quando si abilita un sottoinsieme di NS-3 moduli, le regole di precedenza sono le seguenti:

1. la stringa di configurazione --enable-modules sovrascrive qualsiasi file .ns3rc

2. il file .ns3rc nel livello superiore NS-3 successivamente viene consultata la directory, se presente

3. il sistema cerca ~/.ns3rc se i due precedenti non sono specificati

Se nessuno dei precedenti limita i moduli da costruire, tutti i moduli di cui siamo a conoscenza lo faranno
essere costruito.

La versione mantenuta del file .ns3rc in NS-3 il repository del codice sorgente risiede in
, il utils directory. La ragione di ciò è se si trovasse nella directory di primo livello del file
repository, sarebbe soggetto a check-in accidentali da parte dei manutentori che abilitano il
moduli che vogliono usare. Pertanto, gli utenti devono copiare manualmente il file .ns3rc dal file
utils directory nella loro posizione preferita (directory di livello superiore o home directory) a
abilitare la configurazione di compilazione modulare persistente.

Supponendo che tu sia al livello più alto NS-3 directory, puoi ottenere una copia del file .ns3rc
file che si trova nel utils directory come segue:

$cputils/.ns3rc .

Il file .ns3rc dovrebbe ora essere nel tuo livello superiore NS-3 directory, e contiene il file
seguenti:

#! /usr/bin/env python

# Un elenco dei moduli che saranno abilitati all'esecuzione di ns-3.
# Verranno abilitati anche i moduli che dipendono dai moduli elencati.
#
# Tutti i moduli possono essere abilitati scegliendo 'all_modules'.
module_enabled = ['all_modules']

# Impostalo uguale a true se vuoi che gli esempi vengano eseguiti.
examples_enabled = Falso

# Impostalo su true se vuoi che i test vengano eseguiti.
tests_enabled = Falso

Usa il tuo editor preferito per modificare il file .ns3rc per abilitare solo il modulo principale con
esempi e test come questo:

#! /usr/bin/env python

# Un elenco dei moduli che saranno abilitati all'esecuzione di ns-3.
# Verranno abilitati anche i moduli che dipendono dai moduli elencati.
#
# Tutti i moduli possono essere abilitati scegliendo 'all_modules'.
module_enabled = ['core']

# Impostalo uguale a true se vuoi che gli esempi vengano eseguiti.
examples_enabled = Vero

# Impostalo su true se vuoi che i test vengano eseguiti.
tests_enabled = Vero

Solo il modulo principale sarà abilitato ora se provi questi comandi:

$ ./waf pulito
$ ./waf configura
$ ./waf build
$ cd build/debug/
$ l

e dovrebbero essere presenti le seguenti librerie:

binding libns3-core.so ns3 scratch utils
esempi libns3-core-test.so campioni src

Notare la ./waf cavedano passo viene fatto qui solo per rendere più ovvio quali librerie di moduli
sono stati costruiti. Non devi fare ./waf cavedano per abilitare sottoinsiemi di moduli.

L'esecuzione di test.py causerà l'esecuzione solo di quei test che dipendono dal core del modulo:

24 test su 24 superato (24 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Ripetere i passaggi precedenti per il modulo "network" anziché per il modulo "core" e il file
verrà costruito quanto segue, poiché la rete dipende dal core:

binding libns3-core.so libns3-network.so ns3 scratch utils
esempi libns3-core-test.so libns3-network-test.so campioni src

L'esecuzione di test.py causerà quei test che dipendono solo dai moduli core e di rete
essere eseguito:

31 test su 31 superato (31 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Abilitazione/disabilitazione NS-3 Test e Esempi
Il NS-3 La distribuzione include molti esempi e test che vengono utilizzati per convalidare il NS-3
sistema. Gli utenti, tuttavia, potrebbero non desiderare sempre che questi esempi e test vengano eseguiti per il loro
installazione di NS-3.

Questo capitolo spiega come costruire NS-3 con o senza i suoi esempi e test.

Come a abilitare / disabilitare Esempi e test in NS-3
Ci sono 3 modi per abilitare/disabilitare esempi e test in NS-3:

1. Usando build.py quando NS-3 viene costruito per la prima volta

2. Usando waf una volta NS-3 è stato costruito

3. Usando il NS-3 file di configurazione una volta NS-3 è stato costruito

Abilitare / disabilitare Esempi e test utilizzando build.py
Puoi usare build.py per abilitare/disabilitare esempi e test quando NS-3 è costruito per il primo
tempo.

Per impostazione predefinita, esempi e test non sono integrati NS-3.

Dalla directory ns-3-allinone, puoi costruire NS-3 senza esempi o prove semplicemente
facendo:

$ ./build.py

Esecuzione di test.py nel livello superiore NS-3 directory ora non causerà esempi o test
correre:

0 test su 0 superato (0 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Se vuoi costruire NS-3 con esempi e test, quindi eseguire quanto segue dal file
directory ns-3-allinone:

$ ./build.py --enable-examples --enable-test

Esecuzione di test.py nel livello superiore NS-3 directory causerà tutti gli esempi e i test
da eseguire:

170 test su 170 superato (170 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Abilitare / disabilitare Esempi e test utilizzando waf
Puoi usare waf per abilitare/disabilitare esempi e test una volta NS-3 è stato costruito.

Per impostazione predefinita, esempi e test non sono integrati NS-3.

Dal livello superiore NS-3 directory, puoi costruire NS-3 senza esempi o prove semplicemente
facendo:

$ ./waf configura
$ ./waf build

L'esecuzione di test.py ora non causerà l'esecuzione di esempi o test:

0 test su 0 superato (0 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Se vuoi costruire NS-3 con esempi e test, quindi procedere come segue partendo dall'alto
livello NS-3 directory:

$ ./waf configure --enable-examples --enable-tests
$ ./waf build

L'esecuzione di test.py causerà l'esecuzione di tutti gli esempi e i test:

170 test su 170 superato (170 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Abilitare / disabilitare Esempi e test utilizzando , il NS-3 configurazione filetto
È stato aggiunto un file di configurazione, .ns3rc NS-3 che consente agli utenti di specificare se
esempi e test dovrebbero essere costruiti o meno. È possibile utilizzare questo file per abilitare/disabilitare
esempi e test una volta NS-3 è stato costruito.

Quando si abilita la disabilitazione di esempi e test, le regole di precedenza sono le seguenti:

1. le stringhe di configurazione --enable-examples/--disable-examples sovrascrivono qualsiasi file .ns3rc

2. le stringhe di configurazione --enable-tests/--disable-tests sovrascrivono qualsiasi file .ns3rc

3. il file .ns3rc nel livello superiore NS-3 successivamente viene consultata la directory, se presente

4. il sistema cerca ~/.ns3rc se il file .ns3rc non è stato trovato nel passaggio precedente

Se nessuno dei precedenti esiste, gli esempi e i test non verranno creati.

La versione mantenuta del file .ns3rc in NS-3 il repository del codice sorgente risiede in
, il utils directory. La ragione di ciò è se si trovasse nella directory di primo livello del file
repository, sarebbe soggetto a check-in accidentali da parte dei manutentori che abilitano il
moduli che vogliono usare. Pertanto, gli utenti devono copiare manualmente il file .ns3rc dal file
utils directory nella loro posizione preferita (directory di livello superiore o home directory) a
abilitare l'abilitazione persistente di esempi e test.

Supponendo che tu sia al livello più alto NS-3 directory, puoi ottenere una copia del file .ns3rc
file che si trova nel utils directory come segue:

$cputils/.ns3rc .

Il file .ns3rc dovrebbe ora essere nel tuo livello superiore NS-3 directory, e contiene il file
seguenti:

#! /usr/bin/env python

# Un elenco dei moduli che saranno abilitati all'esecuzione di ns-3.
# Verranno abilitati anche i moduli che dipendono dai moduli elencati.
#
# Tutti i moduli possono essere abilitati scegliendo 'all_modules'.
module_enabled = ['all_modules']

# Impostalo uguale a true se vuoi che gli esempi vengano eseguiti.
examples_enabled = Falso

# Impostalo su true se vuoi che i test vengano eseguiti.
tests_enabled = Falso

Dal livello superiore NS-3 directory, puoi costruire NS-3 senza esempi o prove semplicemente
facendo:

$ ./waf configura
$ ./waf build

L'esecuzione di test.py ora non causerà l'esecuzione di esempi o test:

0 test su 0 superato (0 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Se vuoi costruire NS-3 con esempi e test, usa il tuo editor preferito per cambiare
i valori nel file .ns3rc per i file examples_enabled e tests_enabled devono essere True:

#! /usr/bin/env python

# Un elenco dei moduli che saranno abilitati all'esecuzione di ns-3.
# Verranno abilitati anche i moduli che dipendono dai moduli elencati.
#
# Tutti i moduli possono essere abilitati scegliendo 'all_modules'.
module_enabled = ['all_modules']

# Impostalo uguale a true se vuoi che gli esempi vengano eseguiti.
examples_enabled = Vero

# Impostalo su true se vuoi che i test vengano eseguiti.
tests_enabled = Vero

Dal livello superiore NS-3 directory, puoi costruire NS-3 con esempi e test semplicemente da
facendo:

$ ./waf configura
$ ./waf build

L'esecuzione di test.py causerà l'esecuzione di tutti gli esempi e i test:

170 test su 170 superato (170 superato, 0 saltato, 0 fallito, 0 bloccato, 0 errori valgrind)

Troubleshooting
Questo capitolo riporta alcune informazioni su possibili errori comuni nella compilazione o nell'esecuzione
NS-3 programmi.

Si prega di notare che il wiki (http://www.nsnam.org/wiki/Troubleshooting) potrebbe aver contribuito
articoli.

Silhouette errori
Run-time errori
A volte, possono verificarsi errori con un programma dopo una compilazione riuscita. Questi sono in fase di esecuzione
errori e può verificarsi comunemente quando la memoria è danneggiata o i valori dei puntatori sono inaspettatamente
nullo.

Ecco un esempio di ciò che potrebbe verificarsi:

$ ./waf --run tcp-punto-punto
Entrando nella directory '/home/tomh/ns-3-nsc/build'
Compilazione terminata con successo
Il comando ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] è terminato con il codice -11

Il messaggio di errore dice che il programma è terminato senza successo, ma non è chiaro
da queste informazioni cosa potrebbe essere sbagliato. Per esaminare più da vicino, prova a eseguirlo sotto
, il gdb debugger:

$ ./waf --run tcp-punto-punto --command-template="gdb %s"
Entrando nella directory '/home/tomh/ns-3-nsc/build'
Compilazione terminata con successo
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Copyright 2004 Fondazione Software Libero, Inc.
GDB è un software libero, coperto dalla GNU General Public License, e tu lo sei
benvenuto per cambiarlo e/o distribuirne copie a determinate condizioni.
Digita "mostra copia" per vedere le condizioni.
Non c'è assolutamente alcuna garanzia per GDB. Digita "mostra garanzia" per i dettagli.
Questo GDB è stato configurato come "i386-redhat-linux-gnu"...Utilizzando l'host libthread_db
libreria "/lib/libthread_db.so.1".

(gdb) corri
Programma di avvio: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Lettura dei simboli dall'oggetto condiviso letto dalla memoria di destinazione... fatto.
Il sistema caricato ha fornito DSO a 0xf5c000

Segnale ricevuto dal programma SIGSEGV, Errore di segmentazione.
0x0804aa12 nel principale (argc=1, argv=0xbfdfefa4)
in ../examples/tcp-point-to-point.cc:136
136 Ptr localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
$1 = {m_ptr = 0x3c5d65}
(gdb) p socketFactory
$2 = {m_ptr = 0x0}
(gdb) esci
Il programma è in esecuzione. Uscire comunque? (y o n) y

Nota prima il modo in cui il programma è stato richiamato: passa il comando da eseguire come argomento a
modello di comando "gdb %s".

Questo ci dice che c'è stato un tentativo di dereferenziare un puntatore null socketFactory.

Diamo un'occhiata alla riga 136 di tcp-point-to-point, come suggerisce gdb:

Ptr socketFactory = n2->GetObject (Tcp::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();

Il colpevole qui è che il valore restituito di GetObject non viene verificato e potrebbe esserlo
nullo.

A volte potrebbe essere necessario utilizzare il valgrind memoria checker per errori più sottili. Di nuovo,
invochi l'uso di valgrind in modo simile:

$ ./waf --run tcp-point-to-point --command-template="valgrind %s"

FONTE


Questo documento è scritto in testoristrutturato per Sfinge ed è mantenuto nel
doc/manuale directory del codice sorgente di ns-3.

Utilizzare ns-3-manual online utilizzando i servizi onworks.net


Server e workstation gratuiti

Scarica app per Windows e Linux

  • 1
    Responsabile PAC
    Responsabile PAC
    PAC è un sostituto di Perl/GTK
    SecureCRT/Putty/ecc (linux
    ssh/telnet/... gui)... Fornisce una GUI
    per configurare le connessioni: utenti,
    password, ASPETTATE regole...
    Scarica Gestore PAC
  • 2
    GeoServer
    GeoServer
    GeoServer è un software open source
    server scritto in Java che consente agli utenti
    per condividere e modificare i dati geospaziali.
    Progettato per l'interoperabilità, è
    pubblica da...
    Scarica Geoserver
  • 3
    Lucciola III
    Lucciola III
    Una finanza personale gratuita e open-source
    gestore. Firefly III dispone di un
    sistema di contabilità a partita doppia. Puoi
    inserisci e organizza rapidamente il tuo
    transazioni io...
    Scarica Firefly III
  • 4
    Estensioni di Apache OpenOffice
    Estensioni di Apache OpenOffice
    Il catalogo ufficiale di Apache
    Estensioni di OpenOffice. Lo troverai
    estensioni che vanno dai dizionari a
    strumenti per importare file PDF e per connettersi
    con est...
    Scarica le estensioni di Apache OpenOffice
  • 5
    mantideBT
    mantideBT
    Mantis è un web facilmente distribuibile
    bugtracker basato per aiutare il bug del prodotto
    tracciamento. Richiede PHP, MySQL e a
    server web. Dai un'occhiata alla nostra demo e ospitata
    offrendo...
    Scarica MantisBT
  • 6
    LAN Messenger
    LAN Messenger
    LAN Messenger è un'applicazione di chat p2p
    per la comunicazione intranet e non
    richiedono un server. Una varietà di a portata di mano
    le funzionalità sono supportate tra cui
    notifica...
    Scarica LAN Messenger
  • Di Più "

Comandi Linux

  • 1
    abidw
    abidw
    abidw - serializza l'ABI di un ELF
    il file abidw legge una libreria condivisa in ELF
    formato ed emette una rappresentazione XML
    del suo ABI all’output standard. IL
    emesso...
    Corri costantemente
  • 2
    abile
    abile
    abilint: convalida un ABI abigail
    la capacità di rappresentazione analizza il nativo
    Rappresentazione XML di un'ABI emessa
    da abidw. Una volta analizzato il file XML
    rappresentare...
    Esegui abilint
  • 3
    coresendmsg
    coresendmsg
    coresendmsg: invia un messaggio API CORE
    al demone core-daemon...
    Esegui coresendmsg
  • 4
    core_server
    core_server
    core_server - Il server primario per
    SpamBayes. DESCRIZIONE: Attualmente serve
    solo l'interfaccia web. Collegamento
    listener per vari protocolli è TBD.
    Questo ...
    Eseguire core_server
  • 5
    flash
    flash
    fwflash - programma per eseguire il flashing del file immagine
    a un dispositivo NXT connesso...
    Esegui fwflash
  • 6
    fwts-collezione
    fwts-collezione
    fwts-collect - raccoglie i log per fwts
    segnalazione bug. ...
    Eseguire fwts-collect
  • Di Più "

Ad