InglésFrancésEspañol

icono de página de OnWorks

ns-3-manual: en línea en la nube

Ejecute ns-3-manual en el proveedor de alojamiento gratuito OnWorks sobre Ubuntu Online, Fedora Online, el emulador en línea de Windows o el emulador en línea de MAC OS

Este es el comando ns-3-manual que se puede ejecutar en el proveedor de alojamiento gratuito OnWorks utilizando una de nuestras múltiples estaciones de trabajo en línea gratuitas, como Ubuntu Online, Fedora Online, el emulador en línea de Windows o el emulador en línea de MAC OS.

PROGRAMA:

NOMBRE


ns-3-manual - ns-3 Manual

Este es el ns-3 Manual. La documentación principal para el proyecto ns-3 está disponible en cinco
formas:

· ns-3 Doxygen: Documentación de las API públicas del simulador

· Tutoría, Manual (esta documento)y Biblioteca de modelos para el más reciente , y
Desarrollo tree

· ns-3 wiki

CONTENIDO


Organización
Este capítulo describe el conjunto ns-3 organización de software y la correspondiente
organización de este manual.

ns-3 es un simulador de red de eventos discretos en el que el núcleo de simulación y los modelos son
implementado en C++. ns-3 se construye como una biblioteca que puede ser estática o dinámicamente
vinculado a un programa principal C++ que define la topología de simulación e inicia la
simulador. ns-3 también exporta casi toda su API a Python, lo que permite que los programas Python
importar un módulo "ns3" de la misma manera que el ns-3 la biblioteca está vinculada por ejecutables
en C ++.
[imagen] Organización del software de ns-3.SIN SANGRÍA

El código fuente de ns-3 se organiza mayoritariamente en el src directorio y se puede describir
por el diagrama en Software organización of ns-3. Trabajaremos nuestro camino desde abajo
arriba; en general, los módulos solo tienen dependencias de los módulos debajo de ellos en la figura.

Primero describimos el núcleo del simulador; aquellos componentes que son comunes a todos
protocolo, hardware y modelos ambientales. El núcleo de simulación se implementa en
src / core. Los paquetes son objetos fundamentales en un simulador de red y se implementan en
origen/red. Estos dos módulos de simulación por sí mismos están destinados a comprender un
Núcleo de simulación genérico que puede ser utilizado por diferentes tipos de redes, no solo
Redes basadas en Internet. Los módulos anteriores de ns-3 son independientes de la red específica
y modelos de dispositivos, que se tratan en las partes siguientes de este manual.

Además de lo anterior ns-3 núcleo, introducimos, también en la parte inicial de la
manual, otros dos módulos que complementan la API central basada en C++. ns-3 los programas pueden
acceder a toda la API directamente o puede hacer uso de un llamado ayudante API que proporciona
envoltorios convenientes o encapsulación de llamadas API de bajo nivel. El hecho de que ns-3 programas
se puede escribir en dos API (o una combinación de las mismas) es un aspecto fundamental de la
simulador. También describimos cómo se admite Python en ns-3 antes de pasar a específicos
modelos de relevancia para la simulación de redes.

El resto del manual se enfoca en documentar los modelos y apoyar
capacidades. La siguiente parte se enfoca en dos objetos fundamentales en ns-3: el Nodo y
dispositivo de red. Dos tipos especiales de NetDevice están diseñados para admitir el uso de emulación de red
casos, y la emulación se describe a continuación. El siguiente capítulo está dedicado a
Modelos relacionados con Internet, incluida la API de sockets utilizada por las aplicaciones de Internet. El
el próximo capítulo cubre aplicaciones, y el siguiente capítulo describe soporte adicional
para simulación, como animadores y estadísticas.

El proyecto mantiene un manual separado dedicado a la prueba y validación de ns-3 código
(Véase el ns-3 Pruebas y Validación manual).

Aleatorio Variables
ns-3 contiene un generador de números pseudoaleatorios incorporado (PRNG). Es importante para
usuarios serios del simulador para comprender la funcionalidad, la configuración y el uso
de este PRNG, y decidir si es suficiente para su uso en investigación.

Búsqueda Resumen
ns-3 Los números aleatorios se proporcionan a través de instancias de ns3::Flujo de variable aleatoria.

· por defecto, ns-3 las simulaciones usan una semilla fija; si hay alguna aleatoriedad en el
simulación, cada ejecución del programa producirá resultados idénticos a menos que la semilla y/o
se cambia el número de ejecución.

· en ns-3.3 y antes, ns-3 las simulaciones usaron una semilla aleatoria por defecto; esto marca un
cambio de política a partir de ns-3.4.

· en ns-3.14 y antes, ns-3 Las simulaciones utilizaron una clase contenedora diferente llamada
ns3::Variable aleatoria. A partir de ns-3.15, esta clase ha sido reemplazada por
ns3::Flujo de variable aleatoria; el generador de números pseudoaleatorios subyacente no ha
cambiado.

· para obtener aleatoriedad en varias ejecuciones de simulación, debe establecer la semilla
diferente o establezca el número de ejecución de manera diferente. Para establecer una semilla, llame
ns3::RngSeedManager::SetSeed() al comienzo del programa; para establecer un número de ejecución con
la misma semilla, llama ns3::RngSeedManager::SetRun() al comienzo del programa; ver
Creamos azar las variables.

· cada RandomVariableStream utilizado en ns-3 tiene un generador de números aleatorios virtual asociado
con eso; todas las variables aleatorias usan una semilla fija o aleatoria basada en el uso de la
semilla global (viñeta anterior);

· si tiene la intención de realizar varias ejecuciones del mismo escenario, con diferentes aleatorios
números, asegúrese de leer la sección sobre cómo realizar replicaciones independientes:
Creamos azar las variables.

Lea más para obtener más explicaciones sobre la función de números aleatorios para ns-3.

Antecedentes
Las simulaciones usan muchos números aleatorios; un estudio encontró que la mayoría de las simulaciones de red
gastar hasta el 50% de la CPU generando números aleatorios. Los usuarios de la simulación deben ser
preocupada por la calidad de los (pseudo) números aleatorios y la independencia entre
diferentes secuencias de números aleatorios.

Los usuarios deben preocuparse por algunos problemas, como:

· la siembra del generador de números aleatorios y si un resultado de simulación es
determinista o no,

· cómo adquirir diferentes flujos de números aleatorios que son independientes de uno
otro, y

· cuánto tardan los flujos en completar un ciclo

Introduciremos algunos términos aquí: un RNG proporciona una larga secuencia de (pseudo) aleatorios
números. La longitud de esta sucesión se llama Cycle de largo or período, después de lo cual
el RNG se repetirá. Esta secuencia se puede dividir en disjuntos corrientes. La
El flujo de un RNG es un subconjunto o bloque contiguo de la secuencia del RNG. Por ejemplo, si el
El período RNG tiene una longitud N, y se proporcionan dos flujos desde este RNG, luego el primero
el flujo podría usar los primeros N/2 valores y el segundo flujo podría producir el segundo N/2
valores. Una propiedad importante aquí es que los dos flujos no están correlacionados. Igualmente,
cada flujo se puede dividir inconexamente en un número de no correlacionados subtransmisiones.
RNG subyacente con suerte produce una secuencia pseudoaleatoria de números con un muy largo
duración del ciclo, y lo divide en secuencias y subsecuencias de manera eficiente.

ns-3 utiliza el mismo generador de números aleatorios subyacente que ns-2: el MRG32k3a
generador de Pierre L'Ecuyer. Se puede encontrar una descripción detallada en
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. El generador MRG32k3a
proporciona 1.8x10 ^ {19} flujos independientes de números aleatorios, cada uno de los cuales consta de
2.3x10^{15} subtransmisiones. Cada subflujo tiene un punto (es decir,, el número de números aleatorios
antes de la superposición) de 7.6x10^{22}. El período de todo el generador es 3.1x10^{57}.

Clase ns3::Flujo de variable aleatoria es la interfaz pública de este número aleatorio subyacente
generador. Cuando los usuarios crean nuevas variables aleatorias (como ns3::Variable aleatoria uniforme,
ns3::Variable aleatoria exponencial, etc.), crean un objeto que utiliza uno de los
flujos distintos e independientes del generador de números aleatorios. Por lo tanto, cada objeto de
tipo ns3::Flujo de variable aleatoria tiene, conceptualmente, su propio RNG "virtual". Además,
cada una ns3::Flujo de variable aleatoria se puede configurar para usar uno del conjunto de subtransmisiones dibujadas
de la corriente principal.

Una implementación alternativa sería permitir que cada RandomVariable tenga su propia
(con semillas diferentes) RNG. Sin embargo, no podemos garantizar tan fuertemente que los diferentes
las secuencias no estarían correlacionadas en tal caso; por lo tanto, preferimos usar un solo RNG y
flujos y subflujos de él.

Creamos azar las variables
ns-3 admite una serie de objetos de variables aleatorias de la clase base
Flujo variable aleatorio. Estos objetos se derivan de ns3 :: Objeto y son manejados por smart
punteros

La forma correcta de crear estos objetos es usar la plantilla CrearObjeto<> método,
como:

ptr x = CrearObjeto ();

luego puede acceder a los valores llamando a métodos en el objeto como:

myRandomNo = x->GetInteger ();

Si intentas hacer algo como esto:

myRandomNo = UniformRandomVariable().GetInteger ();

su programa encontrará una falla de segmentación, porque la implementación se basa en
alguna construcción de atributo que ocurre sólo cuando CreateObject se llama.

Gran parte del resto de este capítulo analiza ahora las propiedades de la corriente de
números pseudoaleatorios generados a partir de tales objetos, y cómo controlar la siembra de tales
objetos.

Siembra y independiente replicaciones
ns-3 las simulaciones se pueden configurar para producir resultados deterministas o aleatorios. Si el
ns-3 la simulación está configurada para usar una semilla fija y determinista con el mismo número de ejecución,
debería dar el mismo resultado cada vez que se ejecute.

De forma predeterminada, ns-3 las simulaciones utilizan una semilla y un número de ejecución fijos. Estos valores se almacenan en
two ns3::ValorGlobal instancias: g_rngSemilla y g_rngCorre.

Un caso de uso típico es ejecutar una simulación como una secuencia de ensayos independientes, para
calcular estadísticas sobre un gran número de ejecuciones independientes. El usuario puede cambiar el
semilla global y volver a ejecutar la simulación, o puede avanzar el estado de subflujo del RNG, que
se conoce como incrementar el número de ejecución.

Una clase ns3::RngSeedManager proporciona una API para controlar la siembra y el número de ejecución
conducta. Esta configuración de estado de inicialización y transmisión secundaria debe llamarse antes de cualquier
se crean variables; p.ej:

RngSeedManager::SetSeed (3); // Cambia la semilla del valor predeterminado de 1 a 3
RngSeedManager::SetRun (7); // Cambia el número de ejecución del valor predeterminado de 1 a 7
// Ahora, crea variables aleatorias
ptr x = CrearObjeto ();
ptr y = CrearObjeto ();
...

¿Qué es mejor, establecer una nueva semilla o avanzar en el estado de subtransmisión? No hay
Garantizar que las corrientes producidas por dos semillas aleatorias no se superpongan. La única manera de
garantizar que dos flujos no se superpongan es usar la capacidad de subflujo proporcionada por
la implementación del RNG. De esta manera, las use los Sub corriente capacidad a producir una variedad
independiente corre of los mismo simulación. En otras palabras, cuanto más riguroso estadísticamente
forma de configurar múltiples replicaciones independientes es usar una semilla fija y avanzar
el número de ejecución. Esta implementación permite un máximo de 2.3x10^{15} independientes
replicaciones utilizando las subsecuencias.

Para facilitar el uso, no es necesario controlar la semilla y el número de corrida desde dentro del
programa; el usuario puede establecer la NS_VALOR_GLOBAL variable de entorno de la siguiente manera:

$ NS_GLOBAL_VALUE="RngRun=3" ./waf --ejecutar programa-nombre

Otra forma de controlar esto es pasar un argumento de línea de comandos; ya que este es un ns-3
instancia de GlobalValue, se hace de manera equivalente de la siguiente manera:

$ ./waf --command-template="%s --RngRun=3" --ejecutar programa-nombre

o, si está ejecutando programas directamente fuera de waf:

$ ./build/optimized/scratch/program-name --RngRun = 3

Las variantes de línea de comandos anteriores facilitan la ejecución de muchas ejecuciones diferentes desde un shell
script simplemente pasando un índice RngRun diferente.

Clase Flujo variable aleatorio
Todas las variables aleatorias deben derivar de la clase. Variable aleatoria. Esta clase base proporciona una
pocos métodos para configurar globalmente el comportamiento del generador de números aleatorios. Derivado
Las clases proporcionan API para dibujar variantes aleatorias de la distribución particular que se
soportado.

Cada RandomVariableStream creado en la simulación recibe un generador que es un nuevo
RNGStream del PRNG subyacente. Usada de esta manera, la implementación de L'Ecuyer
permite un máximo de 1.8x10^19 variables aleatorias. Cada variable aleatoria en un solo
la replicación puede producir hasta 7.6x10^22 números aleatorios antes de superponerse.

El pareo de bases clase público API
A continuación se extraen algunos métodos públicos de clase Flujo variable aleatorio que acceden al
siguiente valor en la subsecuencia.

/ **
* \brief Devuelve un doble aleatorio de la distribución subyacente
* \return Un valor aleatorio de punto flotante
*/
doble GetValue (void) const;

/ **
* \brief Devuelve un número entero aleatorio de la distribución subyacente
* \ return Número entero de :: GetValue ()
*/
uint32_t GetInteger (vacío) const;

Ya hemos descrito la configuración de siembra arriba. Variable aleatoria diferente
las subclases pueden tener una API adicional.

Tipos of Variables aleatorias
Se proporcionan los siguientes tipos de variables aleatorias y se documentan en el ns-3
Doxygen o leyendo src/core/model/random-variable-stream.h. Los usuarios también pueden crear
sus propias variables aleatorias personalizadas al derivar de la clase Flujo variable aleatorio.

· clase UniformeAleatorioVariable

· clase Variable aleatoria constante

· clase SecuencialAleatorioVariable

· clase ExponencialAleatorioVariable

· clase ParetoVariableAleatoria

· clase WeibullVariableAleatoria

· clase NormalAleatorioVariable

· clase RegistroNormalAleatorioVariable

· clase GammaAleatorioVariable

· clase ErlangVariableAleatoria

· clase TriangularAleatorioVariable

· clase ZipfVariableAleatoria

· clase ZetaVariableAleatoria

· clase Variable aleatoria determinista

· clase EmpíricoAleatorioVariable

Semántica of Flujo variable aleatorio objetos
Los objetos RandomVariableStream se derivan de ns3 :: Objeto y son manejados por punteros inteligentes.

Las instancias de RandomVariableStream también se pueden usar en ns-3 atributos, lo que significa que
los valores se pueden establecer para ellos a través de la ns-3 sistema de atributos. Un ejemplo está en el
modelos de propagación para WifiNetDevice:

ID de tipo
RandomPropagationDelayModel::GetTypeId (vacío)
{
TypeId estático tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent ()
.AddConstructor ()
.AddAttribute ("Variable",
"La variable aleatoria que genera retrasos aleatorios (s).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
HacerPointerChecker ())
;
volver tid;
}

Aquí el ns-3 el usuario puede cambiar la variable aleatoria predeterminada para este modelo de retraso (que es
una UniformRandomVariable que va de 0 a 1) a través del sistema de atributos.

Gracias a other PRNG
Actualmente no hay soporte para sustituir un número aleatorio subyacente diferente
generador (por ejemplo, la Biblioteca Científica GNU o el paquete Akaroa). Los parches son bienvenidos.

Fijar los stream número
El generador MRG32k3a subyacente proporciona 2^64 flujos independientes. En ns-3, estos son
asignado secuencialmente a partir de la primera transmisión como nuevas instancias de RandomVariableStream
hacen su primera llamada a GetValue().

Como resultado de cómo estos objetos RandomVariableStream se asignan a flujos subyacentes,
la asignación es sensible a las perturbaciones de la configuración de la simulación. El
consecuencia es que si se cambia algún aspecto de la configuración de la simulación, el mapeo
de RandomVariables a flujos puede (o no) cambiar.

Como ejemplo concreto, un usuario que realiza un estudio comparativo entre protocolos de enrutamiento puede
encontrará que el acto de cambiar un protocolo de enrutamiento por otro notará que el
patrón de movilidad subyacente también cambió.

A partir de ns-3.15, se ha proporcionado cierto control a los usuarios para permitirles
corrija opcionalmente la asignación de objetos RandomVariableStream seleccionados a subyacentes
arroyos Este es el Transmite atributo, parte de la clase base RandomVariableStream.

Particionando la secuencia existente de flujos de antes:

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

en dos conjuntos de igual tamaño:

<------------------------------------------------- ------------------------->
^ ^^ ^
| || |
corriente 0 corriente (2^63 - 1) corriente 2^63 corriente (2^64 - 1)
<- asignado automáticamente -----------><- asignado por el usuario ----------------->

Los primeros 2^63 flujos continúan asignándose automáticamente, mientras que los últimos 2^63 se
índices de flujo dados que comienzan con cero hasta 2^63-1.

La asignación de flujos a un número de flujo fijo es opcional; instancias de
RandomVariableStream que no tiene un valor de flujo asignado se le asignará el siguiente
uno del grupo de secuencias automáticas.

Para fijar un RandomVariableStream a un flujo subyacente en particular, asigne su Transmite
atributo a un entero no negativo (el valor predeterminado de -1 significa que un valor será
asignado automáticamente).

DTP a tu manera dE TRATAMIENTOS
Cuando publica los resultados de la simulación, una parte clave de la información de configuración que
siempre debe indicar cómo utilizó el generador de números aleatorios.

· qué semillas usaste,

· qué RNG usaste si no es el predeterminado,

· cómo se realizaron las carreras independientes,

· para simulaciones grandes, ¿cómo comprobó que no hizo un ciclo?

Corresponde al investigador que publica los resultados incluir suficiente información para
permitir que otros reproduzcan sus resultados. También le corresponde al investigador
convencerse de que los números aleatorios utilizados eran estadísticamente válidos y afirmar en
el documento por qué se supone tal confianza.

Resum
Revisemos qué cosas debe hacer al crear una simulación.

· Decide si estás ejecutando con una semilla fija o una semilla aleatoria; una semilla fija es la
defecto

· Decida cómo va a administrar las replicaciones independientes, si corresponde,

· Convénzase de que no está extrayendo más valores aleatorios que la duración del ciclo, si
está ejecutando una simulación muy larga, y

· Cuando publiques, sigue las pautas anteriores sobre cómo documentar tu uso del formato aleatorio
generador de números.

Hash Funciones
ns-3 proporciona una interfaz genérica para funciones hash de propósito general. en lo mas simple
uso, la función hash devuelve el hash de 32 o 64 bits de un búfer de datos o una cadena.
La función hash subyacente predeterminada es murmurar3, elegido porque tiene una buena función hash
propiedades y ofrece una versión de 64 bits. el venerable FNV1a hash también está disponible.

Existe un mecanismo sencillo para agregar (o proporcionar en tiempo de ejecución) hash alternativo
implementaciones de funciones.

Basic Uso
La forma más sencilla de obtener un valor hash de un búfer de datos o cadena es simplemente:

#incluir "ns3/hash.h"

usando el espacio de nombres ns3;

carácter * búfer = ...
tamaño_t tamaño_búfer = ...

uint32_t buffer_hash = Hash32 (buffer, buffer_size);

estándar::cadena s;
uint32_t string_hash = Hash32(s);

Se definen funciones equivalentes para valores hash de 64 bits.

Incremental Hashing
En algunas situaciones, es útil calcular el hash de múltiples búferes, como si tuvieran
se han unido. (Por ejemplo, es posible que desee el hash de un flujo de paquetes, pero no
desea ensamblar un solo búfer con el contenido combinado de todos los paquetes).

Esto es casi tan sencillo como el primer ejemplo:

#incluir "ns3/hash.h"

usando el espacio de nombres ns3;

char * búfer;
tamaño_t tamaño_búfer;

hasher hasher; // Usa la función hash por defecto

por ( )
{
búfer = get_next_buffer ();
hasher (búfer, buffer_size);
}
uint32_t combinado_hash = hasher.GetHash32 ();

Por defecto Picador conserva el estado interno para permitir el hashing incremental. Si quieres
reutilizar un Picador objeto (por ejemplo, porque está configurado con un hash no predeterminado
función), pero no desea agregar al hash calculado previamente, debe claro()
primero:

hasher.clear ().GetHash32 (búfer, buffer_size);

Esto reinicializa el estado interno antes de codificar el búfer.

Gracias a an Alternative Hash Función
La función hash predeterminada es murmurar3. FNV1a también está disponible. Para especificar el hash
función explícitamente, utilice este constructor:

Hasher hasher = Hasher (Crear () );

Adición Nuevo Hash Función Implementaciones
Para agregar la función hash foo, siga la hash-murmullo3.h/. CC patrón:

· Crear una declaración de clase (.h) y definición (. CC) heredando de
Hachís::Implementación.

· incluir la declaración en hash.h (en el punto donde hash-murmullo3.h está incluido.

· En su propio código, cree una instancia Picador objeto a través del constructor Picador
(Patrón ())

Si su función hash es una función única, por ejemplo hashf, ni siquiera necesita crear un
nueva clase derivada de HashImplementation:

hasher hasher =
Hasher (Crear (&hashf) );

Para que esto se compile, su hashf tiene que coincidir con una de las firmas del puntero de función:

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

Fuentes for Hash Funciones
Las fuentes para otras implementaciones de funciones hash incluyen:

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

· Arash Partow: http://www.partow.net/programming/hashfunctions/index.html

· SM Hasher: http://code.google.com/p/smhasher/

· Sanmayce: http://www.sanmayce.com/Fastest_Hash/index.html

Eventos y Simulador
ns-3 es un simulador de red de eventos discretos. Conceptualmente, el simulador realiza un seguimiento de un
número de eventos que están programados para ejecutarse en un tiempo de simulación especificado. el trabajo de
el simulador debe ejecutar los eventos en orden de tiempo secuencial. Una vez finalizada la
ocurre un evento, el simulador se moverá al siguiente evento (o saldrá si no hay
más eventos en la cola de eventos). Si, por ejemplo, un evento programado para el tiempo de simulación
se ejecuta "100 segundos", y el siguiente evento no se programa hasta "200 segundos", el
simulador saltará inmediatamente de 100 segundos a 200 segundos (de tiempo de simulación) a
ejecutar el siguiente evento. Esto es lo que se entiende por simulador de "eventos discretos".

Para que todo esto suceda, el simulador necesita algunas cosas:

1. un objeto simulador que puede acceder a una cola de eventos donde se almacenan los eventos y que puede
gestionar la ejecución de eventos

2. un planificador responsable de insertar y eliminar eventos de la cola

3. una forma de representar el tiempo de simulación

4. los eventos mismos

Este capítulo del manual describe estos objetos fundamentales (simulador, programador,
tiempo, evento) y cómo se utilizan.

Evento
A be terminado

Simulador
La clase Simulator es el punto de entrada público para acceder a las funciones de programación de eventos. Una vez
se han programado un par de eventos para iniciar la simulación, el usuario puede comenzar a
ejecutarlos ingresando al bucle principal del simulador (llamar Simulador::Ejecutar). Una vez que el bucle principal
comienza a ejecutarse, ejecutará secuencialmente todos los eventos programados en orden del más antiguo al
más reciente hasta que no queden más eventos en la cola de eventos o
Simulator::Stop ha sido llamado.

Para programar eventos para que los ejecute el bucle principal del simulador, la clase Simulator proporciona
la familia de funciones Simulator::Schedule*.

1. Manejo de controladores de eventos con diferentes firmas

Estas funciones se declaran e implementan como plantillas de C++ para manejar automáticamente el
amplia variedad de firmas de controladores de eventos de C++ utilizadas en la naturaleza. Por ejemplo, para programar una
evento para ejecutar 10 segundos en el futuro, e invocar un método o función de C++ con
argumentos específicos, puede escribir esto:

controlador vacío (int arg0, int arg1)
{
std::cout << "controlador llamado con el argumento arg0=" << arg0 << " y
arg1=" << arg1 << std::endl;
}

Simulador::Programa(Segundos(10), &manejador, 10, 5);

Que dará como resultado:

controlador llamado con el argumento arg0=10 y arg1=5

Por supuesto, estas plantillas de C ++ también pueden manejar métodos de miembros de forma transparente en C ++
objetos:

A be terminado: miembro Método (aqui)

Notas:

· los métodos de programación ns-3 reconocen automáticamente funciones y métodos solo si
tomar menos de 5 argumentos. Si necesita que respalden más argumentos, presente una
informe de error.

· Los lectores familiarizados con el término 'funtores completamente enlazados' reconocerán la
Simulator::Schedule métodos como una forma de construir automáticamente dichos objetos.

2. Operaciones comunes de programación

La API del Simulador se diseñó para que sea realmente sencillo programar la mayoría de los eventos. Eso
proporciona tres variantes para hacerlo (ordenadas de las más utilizadas a las menos utilizadas):

· Métodos de programación que le permiten programar un evento en el futuro al proporcionar la
retraso entre el tiempo de simulación actual y la fecha de vencimiento del evento de destino.

· Métodos ScheduleNow que le permiten programar un evento para la simulación actual
tiempo: se ejecutarán _después_ de que el evento actual termine de ejecutarse pero _antes_ del
el tiempo de simulación se cambia para el próximo evento.

· Métodos ScheduleDestroy que le permiten engancharse en el proceso de apagado del Simulador
para limpiar los recursos de simulación: cada evento de 'destrucción' se ejecuta cuando el usuario llama
el método Simulator::Destroy.

3. Mantener el contexto de la simulación

Hay dos formas básicas de programar eventos, con y sin contexto. Que hace
significa?

Simulador::Programa (Const. de tiempo y tiempo, MEM mem_ptr, OBJ obj);

vs

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

Lectores que invierten tiempo y esfuerzo en desarrollar o utilizar un modelo de simulación no trivial
conocerá el valor del marco de registro ns-3 para depurar simulaciones simples y complejas
similar. Una de las características importantes que proporciona este marco de registro es la
visualización automática de la identificación del nodo de red asociada con el evento en ejecución 'actualmente'.

De hecho, el simulador rastrea la identificación del nodo de red que se está ejecutando actualmente.
clase. Se puede acceder con el método Simulator::GetContext que devuelve el
'contexto' (un entero de 32 bits) asociado y almacenado en el evento que se está ejecutando actualmente. En
algunos casos raros, cuando un evento no está asociado con un nodo de red específico, su
'contexto' se establece en 0xffffffff.

Para asociar un contexto a cada evento, los métodos Schedule y ScheduleNow automáticamente
reutilizar el contexto del evento que se está ejecutando actualmente como el contexto del evento programado
para su ejecución más tarde.

En algunos casos, sobre todo cuando se simula la transmisión de un paquete desde un nodo a
otro, este comportamiento no es deseable ya que el contexto esperado del evento de recepción es
la del nodo receptor, no la del nodo emisor. Para evitar este problema, el Simulador
class proporciona un método de programación específico: ScheduleWithContext que permite proporcionar
explícitamente el ID de nodo del nodo receptor asociado con el evento de recepción.

XXX: código (aqui)

En algunos casos muy raros, los desarrolladores pueden necesitar modificar o comprender cómo el contexto
(id de nodo) del primer evento se establece en el de su nodo asociado. esto se logra
por la clase NodeList: cada vez que se crea un nuevo nodo, la clase NodeList usa
ScheduleWithContext para programar un evento de 'inicializar' para este nodo. El evento 'inicializar'
por lo tanto, se ejecuta con un contexto establecido en el de la identificación del nodo y puede usar la variedad normal de
Programar métodos. Invoca el método Node::Initialize que propaga la 'inicialización'
evento llamando al método DoInitialize para cada objeto asociado con el nodo. El
Método DoInitialize invalidado en algunos de estos objetos (sobre todo en la aplicación
clase base) programará algunos eventos (sobre todo Application::StartApplication) que
programará a su vez eventos de generación de tráfico que a su vez programarán
eventos a nivel de red.

Notas:

· Los usuarios deben tener cuidado de propagar los métodos DoInitialize entre objetos llamando
Inicializar explícitamente en sus objetos miembros

· La identificación de contexto asociada con cada método ScheduleWithContext tiene otros usos más allá
registro: es utilizado por una rama experimental de ns-3 para realizar una simulación paralela en
sistemas multinúcleo que utilizan subprocesos múltiples.

Las funciones Simulator :: * no saben cuál es el contexto: simplemente se aseguran de que
cualquier contexto que especifique con ScheduleWithContext está disponible cuando el correspondiente
el evento se ejecuta con ::GetContext.

Depende de los modelos implementados sobre Simulator::* interpretar el valor del contexto.
En ns-3, los modelos de red interpretan el contexto como la identificación del nodo que
generó un evento. Por eso es importante llamar a ScheduleWithContext en
subclases ns3::Channel porque estamos generando un evento desde el nodo i al nodo j y
quiere asegurarse de que el evento que se ejecutará en el nodo j tenga el contexto correcto.

Hora
A be terminado

Programador
A be terminado

Devoluciones de llamada
Algunos usuarios nuevos para ns-3 no están familiarizados con un lenguaje de programación muy utilizado
a lo largo del código: el ns-3 llamar de vuelta. Este capítulo proporciona algunas motivaciones sobre la
devolución de llamada, orientación sobre cómo usarlo y detalles sobre su implementación.

Devoluciones de llamada Necesidades
Considere que tiene dos modelos de simulación A y B, y desea que pasen
información entre ellos durante la simulación. Una forma de hacerlo es que usted
puede hacer que A y B conozcan explícitamente al otro, de modo que puedan invocar
métodos entre sí:

clase A {
pública:
void Recibir entrada ( // parámetros );
...
}

(en otro archivo fuente :)

clase B {
pública:
vacío hacer algo (vacío);
...

privada:
A* una_instancia; // puntero a una A
}

vacío
B::HazAlgo()
{
// Dile a una_instancia que algo sucedió
a_instance->ReceiveInput ( // parámetros);
...
}

Esto ciertamente funciona, pero tiene el inconveniente de que introduce una dependencia de A y B.
saber sobre el otro en tiempo de compilación (esto hace que sea más difícil tener
unidades de compilación en el simulador) y no es generalizado; si en un escenario de uso posterior,
B necesita hablar con un objeto C completamente diferente, el código fuente de B debe ser
cambiado para agregar un c_instancia Etcétera. Es fácil ver que esto es una fuerza bruta.
mecanismo de comunicación que puede conducir a la programación cruft en los modelos.

Esto no quiere decir que los objetos no deban conocerse entre sí si existe una
dependencia entre ellos, pero que a menudo el modelo se puede hacer más flexible si su
las interacciones están menos restringidas en tiempo de compilación.

Este no es un problema abstracto para la investigación de simulación de redes, sino que ha sido un
fuente de problemas en simuladores anteriores, cuando los investigadores quieren ampliar o modificar el
sistema para hacer cosas diferentes (como suelen hacer en la investigación). Considere, por ejemplo,
un usuario que desea agregar una subcapa de protocolo de seguridad IPsec entre TCP e IP:

------------ -----------
| TCP | | TCP |
------------ -----------
| se convierte en -> |
----------- -----------
| PI | | IPsec |
----------- -----------
|
-----------
| PI |
-----------

Si el simulador ha hecho suposiciones y está codificado en el código, esa IP siempre habla
a un protocolo de transporte anterior, el usuario puede verse obligado a piratear el sistema para obtener el
interconexiones deseadas. Claramente, esta no es una forma óptima de diseñar un genérico
simulador.

Devoluciones de llamada Antecedentes
NOTA:
Los lectores familiarizados con la programación de devoluciones de llamadas pueden omitir esta sección del tutorial.

El mecanismo básico que permite abordar el problema anterior se conoce como llamar de vuelta.
El objetivo final es permitir que una pieza de código llame a una función (o método en C++)
sin ninguna dependencia específica entre módulos.

En última instancia, esto significa que necesita algún tipo de indirección: trata la dirección de la
llamada función como una variable. Esta variable se denomina variable puntero a función.
La relación entre función y puntero a función realmente no es diferente
el de objeto y puntero a objeto.

En C, el ejemplo canónico de un puntero a función es un
puntero-a-función-retornando-entero (PFI). Para un PFI que toma un parámetro int, esto
podría declararse como:

int(*pfi)(intarg) = 0;

Lo que obtienes de esto es una variable nombrada simplemente Pfi que se inicializa al valor 0.
Si desea inicializar este puntero a algo significativo, debe tener un
función con una firma coincidente. En este caso:

int MiFunción (int arg) {}

Si tiene este objetivo, puede inicializar la variable para que apunte a su función como:

pfi = MiFuncion;

Luego puede llamar a MyFunction indirectamente usando la forma más sugerente de la llamada:

resultado int = (*pfi) (1234);

Esto es sugerente ya que parece que está eliminando la referencia al puntero de función solo
como si quitaras la referencia a cualquier puntero. Por lo general, sin embargo, las personas se aprovechan de la
hecho de que el compilador sabe lo que está pasando y simplemente usará una forma más corta:

resultado int = pfi (1234);

Observe que el puntero de función obedece a la semántica de valores, por lo que puede pasarlo como cualquier
otro valor. Por lo general, cuando usa una interfaz asíncrona, pasará alguna entidad
así a una función que realizará una acción y llamar al back para hacértelo saber
terminado. Devuelve la llamada siguiendo el direccionamiento indirecto y ejecutando la función proporcionada.

En C ++ tienes la complejidad añadida de los objetos. La analogía con el PFI anterior significa que
tener un puntero a una función miembro que devuelve un int (PMI) en lugar del puntero a
función que devuelve un int (PFI).

La declaración de la variable que proporciona la indirección se ve ligeramente diferente:

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

Esto declara una variable llamada pmi tal como el ejemplo anterior declaró una variable llamada
Pfi. Dado que el será llamar a un método de una instancia de una clase particular, uno debe
declarar ese método en una clase:

clase MiClase {
pública:
int MiMetodo (int arg);
};

Dada esta declaración de clase, uno inicializaría esa variable así:

pmi = &MiClase::MiMetodo;

Esto asigna la dirección del código que implementa el método a la variable, completando
la indirección. Para llamar a un método, el código necesita un este vídeo puntero. Esto a su vez,
significa que debe haber un objeto de MyClass al que hacer referencia. Un ejemplo simplista de esto es simplemente
llamando a un método indirectamente (piense en una función virtual):

int (MiClase::*pmi) (int arg) = 0; // Declarar un PMI
pmi = &MiClase::MiMetodo; // Señalar el código de implementación

MyClass myClass; // Necesita una instancia de la clase
(miClase.*pmi) (1234); // Llamar al método con un objeto ptr

Al igual que en el ejemplo de C, puede usar esto en una llamada asíncrona a otro módulo
que lo hará llamar al back utilizando un método y un puntero de objeto. La extensión directa
uno podría considerar es pasar un puntero al objeto y la variable PMI. El módulo
solo haría:

(*objetoPtr.*pmi) (1234);

para ejecutar la devolución de llamada en el objeto deseado.

Uno podría preguntarse en este momento, lo que es los punto? El módulo llamado deberá comprender
el tipo concreto del objeto que llama para realizar correctamente la devolución de llamada. Por qué no
simplemente acepte esto, pase el puntero de objeto correctamente escrito y haga objeto->Método(1234) in
el código en lugar de la devolución de llamada? Este es precisamente el problema descrito anteriormente. Qué es
necesario es una forma de desacoplar completamente la función de llamada de la clase llamada. Esta
requisito condujo al desarrollo de la funtor.

Un funtor es el resultado de algo inventado en la década de 1960 llamado cierre. Está
básicamente solo una llamada de función empaquetada, posiblemente con algún estado.

Un funtor tiene dos partes, una parte específica y una parte genérica, relacionadas por herencia.
El código de llamada (el código que ejecuta la devolución de llamada) ejecutará una sobrecarga genérica
operador () de un funtor genérico para hacer que se llame a la devolución de llamada. El código llamado (el
código que quiere ser llamado de vuelta) tendrá que proporcionar una implementación especializada de
los operador () que realiza el trabajo específico de la clase que causó el acoplamiento cercano
problema de arriba.

Con el funtor específico y su sobrecargado operador () creado, el código llamado entonces
da el código especializado al módulo que ejecutará la devolución de llamada (la llamada
código).

El código de llamada tomará un functor genérico como parámetro, por lo que se realiza una conversión implícita
en la llamada de función para convertir el funtor específico en un funtor genérico. Esto significa
que el módulo de llamada solo necesita comprender el tipo de funtor genérico. esta desacoplado
completamente del código de llamada.

La información que se necesita para hacer un funtor específico es el puntero del objeto y el
dirección de puntero a método.

La esencia de lo que debe suceder es que el sistema declara una parte genérica de la
funtor:

plantilla
función de clase
{
pública:
operador virtual int() (T arg) = 0;
};

La persona que llama define una parte específica del functor que realmente está ahí para implementar
lo especifico operador() método:

plantilla
clase SpecificFunctor: public Functor
{
pública:
Funciónespecífica(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}

operador virtual int() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
privada:
int(T::*m_pmi)(ARGarg);
T* m_p;
};

A continuación, se muestra un ejemplo de uso:

clase A
{
pública:
A (int a0): a (a0) {}
int hola (int b0)
{
std::cout << "Hola desde A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};

int main ()
{
A a(10);
EspecíficoFunctor sf (& a, & A :: Hola);
sf(5);
}

NOTA:
El código anterior no es un código ns-3 real. Es un código de ejemplo simplista que se usa solo para
ilustrar los conceptos involucrados y ayudarlo a comprender mejor el sistema. No
espere encontrar este código en cualquier parte del árbol ns-3.

Observe que hay dos variables definidas en la clase anterior. La variable m_p es la
puntero de objeto y m_pmi es la variable que contiene la dirección de la función a
ejecutar.

Note que cuando operador() es llamado, a su vez llama al método proporcionado con el
puntero de objeto utilizando la sintaxis PMI de C++.

Para usar esto, uno podría declarar algún código modelo que tome un funtor genérico como un
parámetro:

void LibraryFunction (Functor funtor);

El código que hablará con el modelo construiría un funtor específico y se lo pasaría a
Función Biblioteca:

MiClase miClase;
EspecíficoFunctor functor (& myclass, MyClass :: MyMethod);

Cuándo Función Biblioteca está hecho, ejecuta la devolución de llamada usando el operador() en el genérico
functor se pasó, y en este caso particular, proporciona el argumento entero:

vacío
LibraryFunction (Funtor funtor)
{
// Ejecutar la función de biblioteca
funtor(1234);
}

Darse cuenta de Función Biblioteca está completamente desvinculado del tipo específico del cliente.
La conexión se realiza a través del polimorfismo Funtor.

La API de devolución de llamada en ns-3 implementa devoluciones de llamada orientadas a objetos utilizando el mecanismo funtor.
Esta API de devolución de llamada, que se basa en plantillas de C++, es de tipo seguro; es decir, realiza estática
Comprobaciones de tipo para hacer cumplir la compatibilidad de firma adecuada entre las personas que llaman y las personas a las que se llama. Está
por lo tanto, es más seguro para usar que los punteros de función tradicionales, pero la sintaxis puede
parecer imponente al principio. Esta sección está diseñada para guiarlo a través del sistema de devolución de llamada.
para que pueda sentirse cómodo usándolo en ns-3.

Gracias a los Devolución de llamada API
La API de devolución de llamada es bastante mínima y proporciona solo dos servicios:

1. declaración de tipo de devolución de llamada: una forma de declarar un tipo de devolución de llamada con una firma dada,
y,

2. instanciación de devolución de llamada: una forma de instanciar una devolución de llamada de reenvío generada por plantilla
que puede reenviar cualquier llamada a otro método miembro de la clase C++ o función C++.

Esto se observa mejor caminando a través de un ejemplo, basado en muestras/main-callback.cc.

Gracias a los Devolución de llamada API con estático funciones
Considere una función:

doble estático
CbOne (doble a, doble b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
devolver a;
}

Considere también el siguiente fragmento de programa principal:

int principal (int argc, char *argv[])
{
// tipo de retorno: doble
// primer tipo de argumento: doble
// segundo tipo de argumento: doble
Llamar de vuelta una;
}

Este es un ejemplo de una devolución de llamada de estilo C, que no incluye ni necesita un este vídeo
puntero. La plantilla de funciones Devolución de llamada es esencialmente la declaración de la variable
que contiene el puntero a función. En el ejemplo anterior, mostramos explícitamente un puntero
a una función que devolvía un entero y tomaba un solo entero como parámetro, The
Devolución de llamada La función de plantilla es una versión genérica de eso: se usa para declarar el tipo
de una devolución de llamada.

NOTA:
Los lectores que no estén familiarizados con las plantillas de C++ pueden consultar
http://www.cplusplus.com/doc/tutorial/templates/.

El Devolución de llamada plantilla requiere un argumento obligatorio (el tipo de retorno de la función a
asignarse a esta devolución de llamada) y hasta cinco argumentos opcionales, cada uno de los cuales especifica el
tipo de los argumentos (si su función de devolución de llamada en particular tiene más de cinco argumentos,
entonces esto se puede manejar extendiendo la implementación de devolución de llamada).

Entonces, en el ejemplo anterior, tenemos una devolución de llamada declarada llamada "uno" que eventualmente
mantener un puntero de función. La firma de la función que llevará a cabo debe devolver
double y debe admitir dos argumentos dobles. Si se intenta pasar una función cuya
la firma no coincide con la devolución de llamada declarada, se producirá un error de compilación. También si
uno intenta asignar a una devolución de llamada uno incompatible, la compilación tendrá éxito pero un
se generará NS_FATAL_ERROR en tiempo de ejecución. El programa de muestra
src/core/examples/main-callback.cc demuestra ambos casos de error al final de
los principal() .

Ahora, necesitamos vincular esta instancia de devolución de llamada y la función de destino real
(CbUno). Observe anteriormente que CbOne tiene los mismos tipos de firma de función que la devolución de llamada:
esto es importante. Podemos pasar cualquier función correctamente escrita a esta devolución de llamada.
Veamos esto más de cerca:

CbOne doble estático (doble a, doble b) {}
^ ^ ^
| | |
| | |
Llamar de vuelta una;

Solo puede vincular una función a una devolución de llamada si tienen la firma correspondiente. La primera
el argumento de plantilla es el tipo de retorno, y los argumentos de plantilla adicionales son los tipos
de los argumentos de la firma de la función.

Ahora, vinculemos nuestra devolución de llamada "uno" a la función que coincida con su firma:

// crea una instancia de devolución de llamada que apunta a la función cbOne
one = MakeCallback (&CbOne);

Esta llamada a Hacer devolución de llamada es, en esencia, crear uno de los funtores especializados
mencionado anteriormente. La variable declarada usando el Devolución de llamada la función de plantilla va a
estar jugando el papel del funtor genérico. La asignación one = Hacer devolución de llamada (&CbUno) is
el elenco que convierte el funtor especializado conocido por el destinatario en un funtor genérico
conocido por la persona que llama.

Luego, más adelante en el programa, si se necesita la devolución de llamada, se puede usar de la siguiente manera:

NS_ASSERT (! uno. IsNull ());

// invocar la función cbOne a través de la instancia de devolución de llamada
doble retOne;
retUno = uno (10.0, 20.0);

El cheque para Es nulo() asegura que la devolución de llamada no sea nula, que haya una función
llamar detrás de esta devolución de llamada. Entonces, uno() ejecuta el genérico operador() que es realmente
sobrecargado con una implementación específica de operador() y devuelve el mismo resultado que si
CbUno() había sido llamado directamente.

Gracias a los Devolución de llamada API con miembro funciones
Por lo general, no llamará a funciones estáticas, sino a funciones miembro públicas de
un objeto. En este caso, se necesita un argumento adicional para la función MakeCallback, para
decirle al sistema en qué objeto se debe invocar la función. Considere este ejemplo,
también de main-callback.cc:

clase MiCb {
pública:
int CbDos (doble a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
devuelve -5;
}
};

int principal ()
{
...
// tipo de retorno: int
// primer tipo de argumento: doble
Llamar de vuelta dos;
MiCb cb;
// construye una instancia de devolución de llamada que apunta a MyCb :: cbTwo
dos = Hacer Devolución de Llamada (&MiCb::CbDos, &cb);
...
}

Aquí, pasamos un puntero de objeto adicional al Hacer devolución de llamada<> función. Recordar de
la sección de fondo encima de eso Operador() utilizará la sintaxis de puntero a miembro cuando
se ejecuta en un objeto:

operador virtual int() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}

Entonces necesitábamos proporcionar las dos variables (m_p y m_pmi) cuando hicimos el específico
functor La línea:

dos = Hacer Devolución de Llamada (&MiCb::CbDos, &cb);

hace precisamente eso. En este caso, cuando two () se invoca:

int resultado = dos (1.0);

resultará en una llamada al CbDos función miembro (método) en el objeto apuntado por
&cb.

Contruyendo Nulo Devoluciones de llamada
Es posible que las devoluciones de llamada sean nulas; por lo tanto, puede ser conveniente verificar antes de usarlos.
Hay una construcción especial para una devolución de llamada nula, que es preferible a simplemente pasar
"0" como argumento; es el HacerNullCallback<> construir:

dos = Hacer una devolución de llamada nula ();
NS_ASSERT (dos.EsNulo());

Invocar una devolución de llamada nula es como invocar un puntero de función nula: se bloqueará en
tiempo de ejecución.

Bound Devoluciones de llamada
Una extensión muy útil del concepto de funtor es la de un Callback Bound. anteriormente
Se mencionó que los cierres eran originalmente llamadas a funciones empaquetadas para más tarde.
ejecución. Tenga en cuenta que en todas las descripciones de devolución de llamada anteriores, no hay forma de
empaqueta cualquier parámetro para usarlo más tarde, cuando el Devolución de llamada se llama a través de operador().
Todos los parámetros son proporcionados por la función de llamada.

¿Qué sucede si se desea permitir que la función de cliente (la que proporciona la devolución de llamada)
proporcionar algunos de los parámetros? alexandrescu llama al proceso de permitir que un cliente
especificar uno de los parámetros "vinculante". Uno de los parámetros de operador() ha sido
obligado (fijado) por el cliente.

Parte de nuestro código de seguimiento de pcap proporciona un buen ejemplo de esto. Hay una función que
necesita ser llamado cada vez que se recibe un paquete. Esta función llama a un objeto que
en realidad escribe el paquete en el disco en el formato de archivo pcap. La firma de uno de estos
funciones serán:

vacío estático DefaultSink (Ptr archivo, punto pags);

La palabra clave static significa que esta es una función estática que no necesita un este vídeo puntero, entonces
utilizará devoluciones de llamada de estilo C. No queremos que el código de llamada tenga que saber sobre
cualquier cosa menos el paquete. Lo que queremos en el código de llamada es solo una llamada que se parece a:

m_promiscSnifferTrace (m_currentPkt);

Lo que queremos hacer es se unen los Ptr presentar a la devolución de llamada específica
implementación cuando se crea y organizar la operador() de la devolución de llamada a
proporcionar ese parámetro de forma gratuita.

Proporcionamos el HacerBoundCallback función de plantilla para ese propósito. se necesita lo mismo
parámetros como el Hacer devolución de llamada función de plantilla, sino que también toma los parámetros para ser
Unido. En el caso del ejemplo anterior:

MakeBoundCallback (&DefaultSink, archivo);

creará una implementación de devolución de llamada específica que sepa agregar el límite adicional
argumentos Conceptualmente, extiende el funtor específico descrito anteriormente con uno o más
argumentos enlazados:

plantilla
clase SpecificFunctor : public Funtor
{
pública:
Funciónespecífica(T* p, int (T::*_pmi)(ARG arg), BOUND_ARGboundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg =boundArg;
}

operador virtual int() (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
privada:
vacío (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};

Puede ver que cuando se crea el funtor específico, el argumento vinculado se guarda en el
funtor/objeto de devolución de llamada en sí mismo. Cuando el operador() se invoca con el solo
parámetro, como en:

m_promiscSnifferTrace (m_currentPkt);

la aplicación de operador() agrega el parámetro enlazado en la llamada de función real:

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

También es posible vincular dos o tres argumentos. Digamos que tenemos una función con
firma:

NotifyEvent vacío estático (Ptr a, Ptr b, MyEventType e);

Uno puede crear un enlace de devolución de llamada enlazado con los dos primeros argumentos como:

MakeBoundCallback (&NotificarEvento, a1, b1);

asumiendo a1 y b1 son objetos de tipo A y B respectivamente. Del mismo modo para tres
argumentos uno tendría función con una firma:

NotifyEvent vacío estático (Ptr a, Ptr b, MyEventType e);

La unión de tres argumentos se hace con:

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

de nuevo asumiendo a1, b1 y c1 son objetos de tipo A, B y C respectivamente.

Este tipo de enlace se puede utilizar para intercambiar información entre objetos en simulación;
específicamente, las devoluciones de llamadas vinculadas se pueden usar como devoluciones de llamadas rastreadas, que se describirán en
la siguiente sección.

trazada Devoluciones de llamada
marcador de posición subsección

Devolución de llamada Ubicaciones in ns-3
¿Dónde se utilizan con frecuencia las devoluciones de llamada en ns-3? Estos son algunos de los más visibles para
usuarios típicos:

· API de zócalo

· API de capa 2/capa 3

· Subsistema de rastreo

· API entre IP y subsistemas de enrutamiento

Implementación detalles
Los fragmentos de código anteriores son simplistas y solo están diseñados para ilustrar el mecanismo.
sí mismo. El código de devolución de llamada real es bastante complicado y muy intensivo en plantilla y un
no se requiere una comprensión profunda del código. Si está interesado, los usuarios expertos pueden encontrar la
siguiente útil.

El código se escribió originalmente en base a las técnicas descritas en
http://www.codeproject.com/cpp/TTLFunction.asp. Posteriormente fue reescrito para seguir
la arquitectura esbozada en Moderno C + + Diseño Generic Programación y Design Patrones
Aplicado, Alexandrescu, capítulo 5, Generalizado Functores.

Este código usa:

· Parámetros de plantilla predeterminados para evitar que los usuarios tengan que especificar parámetros vacíos cuando
el número de parámetros es menor que el número máximo admitido

· el idioma pimpl: la clase Callback se transmite por valor y delega el quid de
el trabajo a su puntero de grano.

· Se pueden usar dos implementaciones de pimpl que se derivan de CallbackImpl FunctorCallbackImpl
con cualquier tipo de functor, mientras que MemPtrCallbackImpl se puede usar con punteros al miembro
funciones.

· una implementación de lista de referencia para implementar la semántica de valor de Callback.

Este código se aparta notablemente de la implementación de Alexandrescu en que no
use listas de tipos para especificar y pasar los tipos de argumentos de devolución de llamada. Por supuesto,
tampoco utiliza la semántica de destrucción de copias y se basa en una lista de referencias en lugar de
autoPtr para mantener el puntero.

Objeto modelo
ns-3 es fundamentalmente un sistema de objetos C++. Los objetos pueden ser declarados e instanciados como
habitual, según las reglas de C++. ns-3 también agrega algunas características a los objetos tradicionales de C++, como
se describe a continuación, para proporcionar una mayor funcionalidad y características. Este capítulo del manual es
pretende introducir al lector en el ns-3 modelo de objeto

Esta sección describe el diseño de la clase C ++ para ns-3 objetos. En resumen, varios diseños
Los patrones en uso incluyen el diseño clásico orientado a objetos (interfaces polimórficas y
implementaciones), separación de interfaz e implementación, el público no virtual
patrón de diseño de interfaz, una función de agregación de objetos y recuento de referencias para
gestión de la memoria. Aquellos familiarizados con modelos de componentes como COM o Bonobo
reconocer elementos del diseño en el ns-3 modelo de agregación de objetos, aunque el ns-3
el diseño no está estrictamente de acuerdo con ninguno de los dos.

Orientado a objetos comportamiento
Los objetos de C++, en general, proporcionan capacidades comunes orientadas a objetos (abstracción,
encapsulación, herencia y polimorfismo) que son parte del clásico orientado a objetos.
diseño. ns-3 los objetos hacen uso de estas propiedades; por ejemplo:

dirección de clase
{
pública:
Habla a ();
Dirección (tipo uint8_t, const uint8_t *buffer, uint8_t len);
Dirección (Dirección constante y dirección);
Dirección &operador = (const Dirección &dirección);
...
privada:
uint8_t m_tipo;
uint8_t m_len;
...
};

Objeto bases privadas
Hay tres clases base especiales usadas en ns-3. Clases que heredan de estas bases.
las clases pueden instanciar objetos con propiedades especiales. Estas clases base son:

· clase Objeto

· clase Base de objetos

· clase RecuentoRefSimple

No se requiere que ns-3 los objetos heredan de esta clase, pero los que sí obtienen
propiedades especiales. Clases derivadas de clase Objeto obtener las siguientes propiedades.

· los ns-3 tipo y sistema de atributos (ver Atributos)

· Un sistema de agregación de objetos

· un sistema de conteo de referencia de puntero inteligente (clase Ptr)

Clases que derivan de class Base de objetos obtener las dos primeras propiedades anteriores, pero no
obtener punteros inteligentes. Clases que derivan de class RecuentoRefSimple: obtener sólo el
sistema de recuento de referencias de puntero inteligente.

En la práctica, la clase Objeto es la variante de las tres anteriores que el ns-3 el desarrollador lo hará
encuentro más común.

Salud Cerebral de enfermedades hepáticas y clase ptr
La gestión de la memoria en un programa C++ es un proceso complejo y, a menudo, se realiza de forma incorrecta o
inconsistentemente Nos hemos decidido por un diseño de conteo de referencia que se describe a continuación.

Todos los objetos que utilizan el recuento de referencias mantienen un recuento de referencias interno para determinar
cuando un objeto puede borrarse a sí mismo de forma segura. Cada vez que se obtiene un puntero a un
interfaz, el número de referencias del objeto se incrementa llamando Árbitro(). Es el
obligación del usuario del puntero de explícitamente No ref () el puntero cuando haya terminado. Cuándo
el recuento de referencias cae a cero, el objeto se elimina.

· Cuando el código del cliente obtiene un puntero del propio objeto a través de la creación del objeto,
o a través de GetObject, no tiene que incrementar el recuento de referencia.

· Cuando el código del cliente obtiene un puntero de otra fuente (por ejemplo, copiando un puntero), debe
llamar al Árbitro() para incrementar el conteo de referencia.

· Todos los usuarios del puntero del objeto deben llamar No ref () para liberar la referencia.

La carga de llamar No ref () se alivia un poco con el uso del conteo de referencia
clase de puntero inteligente que se describe a continuación.

Usuarios que utilizan una API de bajo nivel que desean asignar explícitamente objetos sin recuento de referencia
en el montón, utilizando el operador new, son responsables de eliminar dichos objetos.

Referencias contando inteligente puntero (Parte)
llamar Árbitro() y No ref () todo el tiempo sería engorroso, así que ns-3 proporciona un inteligente
clase de puntero ptr similar a Impulsar :: intrusive_ptr. Esta clase de puntero inteligente asume que
el tipo subyacente proporciona un par de Ref. y Sin referencia métodos que se espera que
incrementa y decrementa el refcount interno de la instancia del objeto.

Esta implementación le permite manipular el puntero inteligente como si fuera un puntero normal
puntero: puede compararlo con cero, compararlo con otros punteros, asignar cero a
eso, etc.

Es posible extraer el puntero en bruto de este puntero inteligente con el ObtenerPuntero()
y PeekPointer() métodos.

Si desea almacenar un objeto nuevo en un puntero inteligente, le recomendamos que utilice el
La plantilla CreateObject funciona para crear el objeto y almacenarlo en un puntero inteligente para
evitar pérdidas de memoria. Estas funciones son realmente pequeñas funciones de conveniencia y su objetivo
es solo para ahorrarle un poco de tipeo.

CreateObject y Crea
Los objetos en C++ se pueden crear de forma estática, dinámica o automática. esto es cierto
for ns-3 también, pero algunos objetos en el sistema tienen algunos marcos adicionales disponibles.
Específicamente, los objetos contados de referencia generalmente se asignan utilizando una plantilla Crear o
método CreateObject, de la siguiente manera.

Para objetos derivados de la clase Objeto:

ptr dispositivo = CreateObject ();

Por favor, no cree tales objetos usando operador new; crearlos usando Crear objeto()
preferiblemente.

Para objetos derivados de la clase RecuentoRefSimple, u otros objetos que admitan el uso de la
clase de puntero inteligente, una función auxiliar con plantilla está disponible y se recomienda su uso:

Ptr b = Crear ();

Esto es simplemente un nuevo operador de envoltura que maneja correctamente el conteo de referencia
.

En resumen, utilice Crear si B no es un objeto sino que solo usa el conteo de referencias (p. ej.
Paquete), y use Crear objeto si B se deriva de ns3 :: Objeto.

Agregación
El ns-3 sistema de agregación de objetos está motivado en gran parte por el reconocimiento de que un
caso de uso común para ns-2 ha sido el uso de la herencia y el polimorfismo para extender
modelos de protocolo. Por ejemplo, las versiones especializadas de TCP como RenoTcpAgent derivan
de (y anular funciones de) clase TcpAgent.

Sin embargo, dos problemas que han surgido en el ns-2 modelo son abatidos y "base débil
clase ". Downcasting se refiere al procedimiento de usar un puntero de clase base a un objeto y
consultarlo en tiempo de ejecución para encontrar información de tipo, utilizada para lanzar explícitamente el puntero
a un puntero de subclase para que se pueda usar la API de subclase. La clase base débil se refiere a la
problemas que surgen cuando una clase no se puede reutilizar efectivamente (derivar de) porque
carece de la funcionalidad necesaria, lo que lleva al desarrollador a tener que modificar la clase base y
provocando la proliferación de llamadas API de clase base, algunas de las cuales pueden no ser semánticamente
correcta para todas las subclases.

ns-3 está utilizando una versión del patrón de diseño de interfaz de consulta para evitar estos problemas.
Este diseño se basa en elementos del Componente Objeto Modelo y GNOME Bonobo aunque
la compatibilidad completa a nivel binario de los componentes reemplazables no es compatible y tenemos
trató de simplificar la sintaxis y el impacto en los desarrolladores de modelos.

Ejemplos
Agregación (aqui)
Nodo es un buen ejemplo del uso de la agregación en ns-3. Nótese que no se derivan
clases de nodos en ns-3 como clase InternetNodo. En cambio, los componentes (protocolos) son
agregado a un nodo. Veamos cómo se agregan algunos protocolos Ipv4 a un nodo:

hoyo estatico
AñadirIpv4Stack(Ptr nodo)
{
ptr ipv4 = CrearObjeto ();
ipv4->EstablecerNodo (nodo);
nodo->AgregateObject (ipv4);
ptr ipv4Impl = CreateObject ();
ipv4Impl->EstablecerIpv4 (ipv4);
nodo->AgregateObject (ipv4Impl);
}

Tenga en cuenta que los protocolos Ipv4 se crean utilizando Crear objeto(). Luego, se agregan
al nodo. De esta manera, la clase base Node no necesita ser editada para permitir a los usuarios
con un puntero de Nodo de clase base para acceder a la interfaz Ipv4; los usuarios pueden pedirle al nodo un
puntero a su interfaz Ipv4 en tiempo de ejecución. Cómo el usuario pregunta al nodo se describe en el
siguiente subsección.

Tenga en cuenta que es un error de programación agregar más de un objeto del mismo tipo a
an ns3 :: Objeto. Entonces, por ejemplo, la agregación no es una opción para almacenar todos los
sockets activos de un nodo.

Getobjeto (aqui)
GetObject es una forma de seguridad de tipos para lograr un downcasting seguro y permitir que las interfaces sean
encontrado en un objeto.

Considere un puntero de nodo nodo_m que apunta a un objeto Node que tiene una implementación de
IPv4 previamente agregado a él. El código de cliente desea configurar una ruta por defecto. A
hacerlo, debe acceder a un objeto dentro del nodo que tiene una interfaz para el reenvío de IP
configuración. Realiza lo siguiente:

ptr ipv4 = m_node->GetObject ();

Si el nodo de hecho no tiene un objeto Ipv4 agregado, entonces el método
devolver nulo. Por lo tanto, es una buena práctica verificar el valor de retorno de dicha función.
llamar. Si tiene éxito, el usuario ahora puede usar el Ptr para el objeto Ipv4 que anteriormente estaba
agregado al nodo.

Otro ejemplo de cómo se podría usar la agregación es agregar modelos opcionales a los objetos. Para
ejemplo, un objeto Nodo existente puede tener un objeto "Modelo de energía" agregado a él en
tiempo de ejecución (sin modificar y recompilar la clase de nodo). Un modelo existente (como un
dispositivo de red inalámbrica) puede luego "GetObject" para el modelo de energía y actuar apropiadamente
si la interfaz se ha integrado en el objeto Nodo subyacente o se ha agregado a
en tiempo de ejecución. Sin embargo, otros nodos no necesitan saber nada sobre modelos energéticos.

Esperamos que este modo de programación requiera mucha menos necesidad de que los desarrolladores modifiquen
las clases base.

Objeto suerte
Un caso de uso común es crear muchos objetos configurados de manera similar. Uno puede repetidamente
llamar al Crear objeto() pero también hay un patrón de diseño de fábrica en uso en el ns-3 .
Se utiliza mucho en la API "auxiliar".

Clase fábrica de objetos se puede utilizar para crear instancias de objetos y configurar los atributos en
esos objetos:

anular SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
ptr Crear (vacío) const;

El primer método permite utilizar el ns-3 Sistema TypeId para especificar el tipo de objetos
creado. El segundo permite establecer atributos en los objetos a crear, y el
el tercero permite crear los propios objetos.

Por ejemplo:

fábrica ObjectFactory;
// Hacer que esta fábrica cree objetos de tipo FriisPropagationLossModel
fábrica.SetTypeId ("ns3::FriisPropagationLossModel")
// Hacer que este objeto de fábrica cambie un valor predeterminado de un atributo, por
// objetos creados posteriormente
factory.Set ("SystemLoss", DoubleValue (2.0));
// Crea uno de esos objetos
ptr objeto = fábrica.Crear ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Crear otro objeto con un SystemLoss diferente
ptr objeto = fábrica.Crear ();

Abatido
Una pregunta que ha surgido varias veces es: "Si tengo un puntero de clase base (Ptr) a un
object y quiero el puntero de clase derivado, ¿debería bajar (a través de C ++ dynamic cast) para
obtener el puntero derivado, o debería usar el sistema de agregación de objetos para ObtenerObjeto<> ()
para encontrar un Ptr a la interfaz de la API de la subclase?"

La respuesta a esto es que, en muchas situaciones, ambas técnicas funcionarán. ns-3 proporciona una
función con plantilla para hacer que la sintaxis de la conversión dinámica de objetos sea mucho más fácil para el usuario
amistoso:

plantilla
ptr
Transmisión Dinámica (Ptr constante y p)
{
volver punto (transmisión_dinámica (PeekPointer (p)));
}

DynamicCast funciona cuando el programador tiene un puntero de tipo base y está probando contra un
puntero de subclase. GetObject funciona cuando busca diferentes objetos agregados, pero también
trabaja con subclases, de la misma forma que DynamicCast. Si no está seguro, el programador debe
use GetObject, ya que funciona en todos los casos. Si el programador conoce la jerarquía de clases de
el objeto bajo consideración, es más directo usar DynamicCast.

Configuration y Atributos
In ns-3 simulaciones, hay dos aspectos principales para la configuración:

· La topología de la simulación y cómo se conectan los objetos.

· Los valores utilizados por los modelos instanciados en la topología.

Este capítulo se centra en el segundo elemento anterior: cómo los muchos valores en uso en ns-3 están
organizado, documentado y modificable por ns-3 usuarios los ns-3 sistema de atributos es también el
la base de cómo se recopilan las trazas y las estadísticas en el simulador.

En el transcurso de este capítulo discutiremos las diversas formas de establecer o modificar los valores
utilizado por ns-3 modelar objetos. En orden creciente de especificidad, estos son:

┌────────────────────────────────┬──────────────── ───────────────────┐
│Método │ Alcance │
├────────────────────────────────┼──────────────── ───────────────────┤
│Valores de atributo predeterminados establecidos │ Afecta a todas las instancias de │
│cuando los atributos se definen en │ clase. │
ObtenerIdTipo (). │ │
└────────────────────────────────┴──────────────── ───────────────────┘

Línea de comando │ Afecta a todas las instancias futuras. │
Configuración::EstablecerPredeterminado() │ │
Tienda de configuración │ │
├────────────────────────────────┼──────────────── ───────────────────┤
fábrica de objetos │ Afecta a todas las instancias creadas │
│ │ con la fábrica. │
├────────────────────────────────┼──────────────── ───────────────────┤
XHelperSetAtributo () │ Afecta a todas las instancias creadas por │
│ │ el ayudante. │
├────────────────────────────────┼──────────────── ───────────────────┤
MiClase::SetX () │ Altera esta instancia en particular. │
Objeto::Establecer Atributo () │ Generalmente esta es la única forma │
Configuración::Establecer() │ que se puede programar para modificar │
│ │ una instancia una vez finalizada la simulación │
│ │ se está ejecutando. │
└────────────────────────────────┴──────────────── ───────────────────┘

Por "especificidad" queremos decir que los métodos en las filas posteriores de la tabla anulan los valores establecidos
por, y normalmente afecta a menos instancias que los métodos anteriores.

Antes de profundizar en los detalles del sistema de valor de atributo, será útil revisar algunos
propiedades basicas de clase Objeto.

Objeto Resumen
ns-3 es fundamentalmente un sistema basado en objetos C++. Con esto queremos decir que las nuevas clases de C++
(tipos) se pueden declarar, definir y subclasificar como de costumbre.

Muchos ns-3 los objetos heredan del Objeto clase básica. Estos objetos tienen algunos adicionales
propiedades que explotamos para organizar el sistema y mejorar la gestión de la memoria
de nuestros objetos:

· Sistema de "metadatos" que vincula el nombre de la clase a una gran cantidad de metainformación sobre el
objeto, incluyendo:

· La clase base de la subclase,

· El conjunto de constructores accesibles en la subclase,

· El conjunto de "atributos" de la subclase,

· Si cada atributo se puede configurar o es de solo lectura,

· El rango de valores permitido para cada atributo.

· Implementación de puntero inteligente de conteo de referencias, para administración de memoria.

ns-3 los objetos que utilizan el sistema de atributos se derivan de Objeto or Base de objetos. Más
ns-3 objetos que discutiremos derivan de Objeto, pero algunos que están fuera del smart
El marco de gestión de memoria de puntero se deriva de Base de objetos.

Repasemos un par de propiedades de estos objetos.

Smart Punteros
Como se introdujo en el ns-3 tutorial, ns-3 los objetos son memoria administrada por un referencia
contando inteligente puntero implementación, clase ptr.

Los punteros inteligentes se utilizan mucho en la ns-3 API, para evitar pasar referencias a
objetos asignados al montón que pueden causar fugas de memoria. Para el uso más básico (sintaxis), trate
un puntero inteligente como un puntero regular:

ptr sf = ...;
nd->LlamarAlgunaFunción ();
// etc

Entonces, ¿cómo se obtiene un puntero inteligente para un objeto, como en la primera línea de este ejemplo?

CreateObject
Como discutimos anteriormente en Memory-management-and-class-Ptr, en la API de nivel más bajo, los objetos
De tipo Objeto no se instancian usando operador new como de costumbre, pero en su lugar por una plantilla
función llamada CreateObject ().

Una forma típica de crear un objeto de este tipo es la siguiente:

ptr nd = CrearObjeto ();

Puedes pensar en esto como funcionalmente equivalente a:

WifiNetDevice* nd = nuevo WifiNetDevice ();

Objetos que derivan de Objeto debe ser asignado en el montón usando CreateObject (). Aquellos
derivados de Base de objetos, Tales como ns-3 funciones auxiliares y encabezados y tráileres de paquetes,
se pueden asignar en la pila.

En algunas secuencias de comandos, es posible que no vea muchas CreateObject () llamadas en el código; esto es
porque hay algunos objetos de ayuda en efecto que están haciendo el CreateObject () llamadas
para ti.

ID de tipo
ns-3 clases que se derivan de clase Objeto puede incluir una clase de metadatos llamada ID de tipo que
registra metainformación sobre la clase, para su uso en la agregación de objetos y componentes
sistemas de gestión:

· Una cadena única que identifica la clase.

· La clase base de la subclase, dentro del sistema de metadatos.

· El conjunto de constructores accesibles en la subclase.

· Una lista de propiedades de acceso público ("atributos") de la clase.

Objeto Resum
Poniendo todos estos conceptos juntos, veamos un ejemplo específico: clase Nodo.

El archivo de encabezado público nodo.h tiene una declaración que incluye una estática ObtenerIdTipo ()
Llamada de función:

Nodo de clase: objeto público
{
pública:
TypeId estático GetTypeId (vacío);
...

Esto se define en el nodo.cc archivo de la siguiente manera:

ID de tipo
Nodo::GetTypeId (vacío)
{
Id de tipo estático tid = Id de tipo ("ns3::Node")
.SetParent ()
.AddConstructor ()
.AddAttribute ("Lista de dispositivos",
"La lista de dispositivos asociados a este Nodo.",
ObjetoVectorValor (),
MakeObjectVectorAccessor (&Nodo::m_dispositivos),
CrearObjetoVectorChecker ())
.AddAttribute ("Lista de aplicaciones",
"La lista de aplicaciones asociadas a este Nodo.",
ObjetoVectorValor (),
MakeObjectVectorAccessor (&Nodo::m_aplicaciones),
CrearObjetoVectorChecker ())
.AddAttribute ("Id",
"El id (entero único) de este nodo.",
TypeId::ATTR_GET, // permite solo obtenerlo.
UvalorEntero (0),
MakeUintegerAccessor (&Nodo::m_id),
HacerUintegerChecker ())
;
volver tid;
}

Considere el gráfico ID de tipo de las ns-3 Objeto clase como una forma extendida de tipo de tiempo de ejecución
información (RTTI). El lenguaje C++ incluye un tipo simple de RTTI para soportar
Dynamic_cast y Typeid operadores.

El Establecer padre () La llamada en la definición anterior se usa junto con nuestro
mecanismos de agregación de objetos para permitir una conversión ascendente y descendente segura en árboles de herencia
during Getobjeto (). También permite que las subclases hereden los atributos de su padre
clase.

El AñadirConstructor () la llamada se usa junto con nuestra fábrica de objetos abstractos
mecanismos que nos permiten construir objetos C++ sin obligar a un usuario a conocer el
clase concreta del objeto que está construyendo.

Las tres llamadas a Agregar atributo () asociar una cadena dada con un valor fuertemente tipado en
la clase. Tenga en cuenta que debe proporcionar una cadena de ayuda que se puede mostrar, por ejemplo,
vía procesadores de línea de comandos. Cada Atributo está asociado con mecanismos para acceder
la variable miembro subyacente en el objeto (por ejemplo, MakeUintegerAccessor () decirles
el genérico Atributo código de cómo llegar al ID de nodo anterior). También hay "Comprobador"
métodos que se utilizan para validar valores frente a limitaciones de rango, como máximo y
valores mínimos permitidos.

Cuando los usuarios quieren crear Nodos, por lo general llamarán a alguna forma de CreateObject (),:

ptr n = CrearObjeto ();

o de manera más abstracta, utilizando una fábrica de objetos, puede crear un Nodo objeto sin siquiera
conociendo el tipo concreto de C++:

fábrica ObjectFactory;
const std::string typeId = "ns3::Nodo'';
fábrica.SetTypeId (tipoId);
ptr nodo = fábrica.Crear ();

Ambos métodos dan como resultado que los atributos completamente inicializados estén disponibles en el
resultante Objeto instancias.

A continuación discutimos cómo los atributos (valores asociados con variables miembro o funciones de
la clase) están conectados a lo anterior ID de tipo.

Atributos
El objetivo del sistema de atributos es organizar el acceso de los objetos miembros internos de un
simulación. Este objetivo surge porque, típicamente en la simulación, los usuarios cortarán y
pegar/modificar scripts de simulación existentes, o usará construcciones de simulación de nivel superior,
pero a menudo estará interesado en estudiar o rastrear variables internas particulares. Para
ejemplo, casos de uso como:

· "I want a rastrear los paquetes on los sin hilos interfaz. only on los first de la máquina punto."

· "I want a rastrear los propuesta de of los TCP congestión ventana (cada time it cambios) on a
particular TCP enchufe."

· "I want a arrojar of all valores que fueron usado in my simulación."

De manera similar, los usuarios pueden desear un acceso detallado a las variables internas en la simulación, o
puede querer cambiar ampliamente el valor inicial usado para un parámetro en particular en todos
objetos creados posteriormente. Finalmente, los usuarios pueden desear saber qué variables se pueden configurar
y recuperable en una configuración de simulación. Esto no es solo para simulación directa.
interacción en la línea de comando; considere también una (futura) interfaz gráfica de usuario que
le gustaría poder proporcionar una función mediante la cual un usuario pueda hacer clic derecho en un nodo en
el lienzo y verá una lista jerárquica y organizada de parámetros que se pueden configurar en el
nodo y sus objetos miembros constituyentes, y texto de ayuda y valores predeterminados para cada
parámetro.

Definición Atributos
Brindamos una manera para que los usuarios accedan a valores en lo profundo del sistema, sin tener que sondear
accesores (punteros) a través del sistema y caminar cadenas de punteros para llegar a ellos. Considere un
clase cola de cola caída que tiene una variable miembro que es un entero sin signo m_maxPaquetes;
esta variable miembro controla la profundidad de la cola.

Si nos fijamos en la declaración de cola de cola caída, vemos lo siguiente:

clase DropTailQueue: cola pública {
pública:
TypeId estático GetTypeId (vacío);
...

privada:
estándar::cola > m_paquetes;
uint32_t m_maxPackets;
};

Consideremos las cosas que un usuario puede querer hacer con el valor de m_maxPaquetes:

· Establecer un valor predeterminado para el sistema, de modo que cada vez que un nuevo cola de cola caída es creado,
este miembro se inicializa con ese valor predeterminado.

· Establecer u obtener el valor en una cola ya instanciada.

Las cosas anteriores generalmente requieren proporcionar Set () y Get () funciones y algún tipo de
valor predeterminado global.

En Los ns-3 sistema de atributos, estas definiciones de valor y registros de funciones de acceso
se trasladan a la ID de tipo clase; e.g..:

NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);

ID de tipo
DropTailQueue::GetTypeId (vacío)
{
TypeId estático tid = TypeId ("ns3::DropTailQueue")
.SetParent ()
.AddConstructor ()
.AddAttribute ("Paquetes máximos",
"El número máximo de paquetes aceptados por este DropTailQueue.",
UvalorEntero (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
HacerUintegerChecker ())
;

volver tid;
}

El Agregar atributo () método está realizando una serie de cosas para el m_maxPaquetes valor:

· Vinculación de la variable miembro (generalmente privada) m_maxPaquetes a una cadena pública
"Paquetes máximos".

· Proporcionar un valor predeterminado (100 paquetes).

· Proporcionar algún texto de ayuda que defina el significado del valor.

· Proporcionar un "Comprobador" (no utilizado en este ejemplo) que se puede utilizar para establecer límites en el
rango permitido de valores.

El punto clave es que ahora el valor de esta variable y su valor predeterminado son accesibles
en el espacio de nombres del atributo, que se basa en cadenas como "Paquetes máximos" y ID de tipo nombre
instrumentos de cuerda. En la siguiente sección, proporcionaremos un script de ejemplo que muestra cómo los usuarios pueden
manipular estos valores.

Tenga en cuenta que la inicialización del atributo se basa en la macro NS_OBJECT_ENSURE_REGISTERED
(Cola de cola desplegable) siendo llamado; si deja esto fuera de la implementación de su nueva clase, su
los atributos no se inicializarán correctamente.

Si bien hemos descrito cómo crear atributos, todavía no hemos descrito cómo acceder
y gestionar estos valores. Por ejemplo, no hay globales.h archivo de encabezado donde están estos
almacenado; Los atributos se almacenan con sus clases. Las preguntas que surgen naturalmente son cómo
¿Los usuarios aprenden fácilmente sobre todos los atributos de sus modelos y cómo un usuario
acceder a estos atributos, o documentar sus valores como parte del registro de sus
¿simulación?

Documentación detallada de los atributos reales definidos para un tipo y una lista global de
todos los atributos definidos están disponibles en la documentación de la API. Por el resto de esto
documento vamos a demostrar las diversas formas de obtener y establecer atributo
valores.

Fijar Predeterminado Valores
Configuración::Establecer por defecto y Línea de comando
Veamos cómo un script de usuario puede acceder a un valor de atributo específico. Iremos a
utilice el src/punto-a-punto/examples/main-attribute-value.cc guión para ilustración, con
algunos detalles eliminados. los principal comienza la función:

// Este es un ejemplo básico de cómo usar el sistema de atributos para
// establecer y obtener un valor en el sistema subyacente; es decir, un sin firmar
// número entero del número máximo de paquetes en una cola
//

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

// Por defecto, el atributo MaxPackets tiene un valor de 100 paquetes
// (este valor predeterminado se puede observar en la función DropTailQueue::GetTypeId)
//
// Aquí, lo configuramos en 80 paquetes. Podríamos usar uno de dos tipos de valores:
// un valor basado en cadenas o un valor Uinteger
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// La siguiente llamada de función es redundante
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));

// Permitir que el usuario anule cualquiera de los valores predeterminados y los anteriores
// SetDefaults () en tiempo de ejecución, a través de argumentos de línea de comandos
// Por ejemplo, a través de "--ns3::DropTailQueue::MaxPackets=80"
Línea de comando cmd;
// Esto proporciona otra forma de establecer el valor desde la línea de comando:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd. Parse (argc, argv);

Lo principal a notar en lo anterior son las dos llamadas equivalentes a Configuración::Establecer por defecto
(). Así es como establecemos el valor predeterminado para todos los instanciados posteriormente
cola de cola caídas. Ilustramos que dos tipos de Value alto clases, un Valor de cadena y
ValorEntero clase, se puede utilizar para asignar el valor al atributo nombrado por
"ns3::DropTailQueue::Paquetes máximos".

También es posible manipular los Atributos usando el Línea de comando; vimos algunos ejemplos
temprano en el Tutorial. En particular, es sencillo agregar un argumento abreviado
nombre, como --maxPackets, para un atributo que es particularmente relevante para su modelo,
en este caso "ns3::DropTailQueue::Paquetes máximos". Esto tiene la característica adicional de que el
La cadena de ayuda para el atributo se imprimirá como parte del mensaje de uso del script.
Para obtener más información, consulte la Línea de comando Documentación de API.

Ahora, crearemos algunos objetos usando la API de bajo nivel. Nuestras colas recién creadas
no tiene m_maxPaquetes inicializado a 100 paquetes, como se define en el
DropTailQueue::GetTypeId () función, pero a 80 paquetes, debido a lo que hicimos anteriormente con
valores predeterminados.:

ptr n0 = CrearObjeto ();

ptr net0 = CrearObjeto ();
n0->AddDevice (net0);

ptr q = CrearObjeto ();
net0->AddQueue(q);

En este punto, hemos creado un único Nodo (n0) y una sola Dispositivo de red punto a punto
(net0), y agregó un cola de cola caída (q) A net0.

constructores, Ayudantes y fábrica de objetos
Se pueden establecer y obtener combinaciones arbitrarias de atributos del ayudante y de bajo nivel.
API; ya sea de los propios constructores:

ptr p =
CrearObjetoConAtributos
("MinX", valor doble (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", valor doble (5.0),
"DeltaY", valor doble (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));

o desde las API auxiliares de nivel superior, como:

movilidad.SetPositionAllocator
("ns3::GridPositionAllocator",
"MinX", valor doble (-100.0),
"MinY", DoubleValue (-100.0),
"DeltaX", valor doble (5.0),
"DeltaY", valor doble (20.0),
"GridWidth", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));

No lo ilustramos aquí, pero también puede configurar un fábrica de objetos con nuevos valores
para atributos específicos. Instancias creadas por el fábrica de objetos tendrá esos
atributos establecidos durante la construcción. Esto es muy similar a usar una de las API auxiliares
para la clase.

Para revisar, hay varias formas de establecer valores para atributos para instancias de clase a be
creado in los futuro:

· Configuración::Establecer por defecto ()

· Línea de Comando::AñadirValor ()

· CrearObjetoConAtributos<> ()

· Varias API auxiliares

Pero, ¿qué sucede si ya ha creado una instancia y desea cambiar el valor de la
¿atributo? En este ejemplo, ¿cómo podemos manipular el m_maxPaquetes valor del ya
instanciado cola de cola caída? Aquí hay varias formas de hacerlo.

Cambiar Valores
Puntero inteligente
Suponga que un puntero inteligente (ptr) a un dispositivo de red relevante está en la mano; en la corriente
ejemplo, es el net0 puntero.

Una forma de cambiar el valor es acceder a un puntero a la cola subyacente y modificar su
atributo.

Primero, observamos que podemos obtener un puntero a la (clase base) Cola vía los
Dispositivo de red punto a punto atributos, donde se llama "TxQueue":

PunteroValor tmp;
net0->GetAttribute ("TxQueue", tmp);
ptr txQueue = tmp.GetObject ();

Usando el Getobjeto () función, podemos realizar un downcast seguro a un cola de cola caída, donde el
"Paquetes máximos" es un atributo:

ptr dtq = txQueue->ObtenerObjeto ();
NS_ASSERT (dtq! = 0);

A continuación, podemos obtener el valor de un atributo en esta cola. Hemos introducido envoltura
Value alto clases para los tipos de datos subyacentes, similares a los contenedores de Java alrededor de estos tipos,
ya que el sistema de atributos almacena valores serializados en cadenas, y no tipos dispares.
Aquí, el valor del atributo se asigna a un ValorEntero, y el Get () método en esto
valor produce el (sin envolver) uint32_t.:

UintegerValue límite;
dtq->GetAttribute ("MaxPackets", límite);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " paquetes");

Tenga en cuenta que el downcast anterior no es realmente necesario; podríamos haber obtenido el atributo
valor directamente de txQueue, que es un Objeto:

txQueue->GetAttribute ("MaxPackets", límite);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " paquetes");

Ahora, establezcamos otro valor (60 paquetes):

txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", límite);
NS_LOG_INFO ("3. Límite txQueue cambiado: " << limit.Get () << " paquetes");

Config Espacio de nombres Path
Una forma alternativa de obtener el atributo es utilizar el espacio de nombres de configuración. Aquí,
este atributo reside en una ruta conocida en este espacio de nombres; este enfoque es útil si uno
no tiene acceso a los punteros subyacentes y le gustaría configurar un
atributo con una sola declaración.:

Configuración::Establecer ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
UvalorEntero (25));
txQueue->GetAttribute ("MaxPackets", límite);
NS_LOG_INFO ("4. El límite de txQueue cambió a través del espacio de nombres: "
<< limit.Get() << "paquetes");

La ruta de configuración a menudo tiene la forma de ".../
nombre>/ /.../ / " para referirse a una instancia específica por índice de un
objeto en el recipiente. En este caso, el primer contenedor es la lista de todos Nodos; la
segundo contenedor es la lista de todos dispositivo de reds en el elegido Nodo. Finalmente, el
ruta de configuración por lo general termina con una sucesión de atributos de miembro, en este caso el
"Paquetes máximos" atributo de la "TxQueue" de los elegidos dispositivo de red.

También podríamos haber usado comodines para establecer este valor para todos los nodos y todos los dispositivos de red.
(que en este ejemplo simple tiene el mismo efecto que el anterior Configuración::Establecer ()):

Configuración::Establecer ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
UvalorEntero (15));
txQueue->GetAttribute ("MaxPackets", límite);
NS_LOG_INFO ("5. El límite de txQueue cambió a través del espacio de nombres con comodines: "
<< limit.Get() << "paquetes");

Objeto Nombre Servicio
Otra forma de obtener el atributo es utilizar la función de servicio de nombres de objetos. los
El servicio de nombres de objetos nos permite agregar elementos al espacio de nombres de configuración bajo el
"/Nombres/" ruta con una cadena de nombre definida por el usuario. Este enfoque es útil si uno no
tener acceso a los punteros subyacentes y es difícil determinar el requerido
ruta de espacio de nombres de configuración concreta.

Nombres::Add ("servidor", n0);
Nombres::Add ("servidor/eth0", net0);

...

Config::Set ("/Nombres/servidor/eth0/TxQueue/MaxPackets", UintegerValue (25));

Aquí hemos añadido los elementos de la ruta. "servidor" y "eth0" bajo el "/Nombres/" espacio de nombres, entonces
usó la ruta de configuración resultante para establecer el atributo.

Consulte Nombres de objetos para un tratamiento más completo de los ns-3 espacio de nombres de configuración.

Implementación Detalles
Value alto Clases
Los lectores notarán la TipoValor clases que son subclases de la Valor de atributo bases
clase. Estos pueden considerarse como clases intermedias que se utilizan para convertir de raw
tipos a la Valor de atributos que son utilizados por el sistema de atributos. Recuerda que esto
La base de datos contiene objetos de muchos tipos serializados en cadenas. Conversiones a este tipo
se puede hacer usando una clase intermedia (como Valor enteroo valor doble for
números de punto flotante) o vía instrumentos de cuerda. Conversión implícita directa de tipos a
Valor de atributo no es realmente práctico. Entonces, en lo anterior, los usuarios tienen la opción de usar
cadenas o valores:

p->Establecer ("cwnd", StringValue ("100")); // establecedor basado en cadenas
p->Establecer ("cwnd", IntegerValue (100)); // establecedor basado en enteros

El sistema proporciona algunas macros que ayudan a los usuarios a declarar y definir nuevos AttributeValue
subclases para nuevos tipos que quieren introducir en el sistema de atributos:

· ATTRIBUTE_HELPER_HEADER

· ATTRIBUTE_HELPER_CPP

Consulte la documentación de la API para estas construcciones para obtener más información.

Inicialización Order
Los atributos en el sistema no deben depender del estado de ningún otro atributo en este
sistema. Esto se debe a que no se especifica un orden de inicialización de atributos, ni
aplicada, por el sistema. Un ejemplo específico de esto se puede ver en la configuración automatizada
programas como Tienda de configuración. Aunque un modelo dado puede arreglarlo para que los Atributos
se inicializan en un orden particular, otro configurador automático puede decidir
independientemente para cambiar los Atributos, por ejemplo, en orden alfabético.

Debido a este orden no específico, ningún Atributo en el sistema puede tener dependencia
en cualquier otro Atributo. Como corolario, los establecedores de atributos nunca deben fallar debido al estado
de otro Atributo. Ningún establecedor de Atributos puede cambiar (establecer) ningún otro valor de Atributo como
resultado de cambiar su valor.

Esta es una restricción muy fuerte y hay casos en los que los Atributos deben establecerse
consistentemente para permitir una operación correcta. Con este fin, permitimos la verificación de consistencia.
when los atributo is usado (cf. NS_ASSERT_MSG or NS_ABORT_MSG).

En general, el código de atributo para asignar valores a las variables de miembro de clase subyacentes
se ejecuta después de construir un objeto. Pero, ¿y si necesita los valores asignados
antes de que se ejecute el cuerpo del constructor, porque los necesita en la lógica del
¿constructor? Hay una manera de hacer esto, usada por ejemplo en la clase Tienda de configuración: llamada
BaseObjeto::ConstructSelf () como sigue:

ConfigStore::ConfigStore ()
{
ObjectBase::ConstructSelf (AttributeConstructionList ());
// continuar con el constructor.
}

Tenga en cuenta que el objeto y todas sus clases derivadas también deben implementar un ObtenerId de tipo de instancia
() método. De lo contrario, el BaseObjeto::ConstructSelf () no será capaz de leer el
los atributos.

Adición Atributos
El ns-3 sistema colocará una serie de valores internos bajo el sistema de atributos, pero
indudablemente, los usuarios querrán extender esto para recoger los que nos hemos perdido, o para agregar su
clases propias al sistema.

Hay tres casos de uso típicos:

· Hacer accesible un miembro de datos de clase existente como un atributo, cuando aún no lo es.

· Crear una nueva clase capaz de exponer algunos miembros de datos como atributos dándole un TypeId.

· Crear un Valor de atributo subclase para una nueva clase para que se pueda acceder a ella como un
Atributo.

Ya eres Titular Variable
Considere esta variable en TcpSocket:

uint32_t m_cWnd; // Ventana de congestión

Supongamos que alguien que trabaja con TCP quisiera obtener o establecer el valor de esa variable
utilizando el sistema de metadatos. Si no estuviera ya proporcionada por ns-3, el usuario podría declarar
la siguiente adición en el sistema de metadatos en tiempo de ejecución (al ObtenerIdTipo() definición para
TcpSocket):

.AddAttribute ("Ventana de congestión",
"Ventana de congestión TCP (bytes)",
UvalorEntero (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
HacerUintegerChecker ())

Ahora, el usuario con un puntero a un TcpSocket instancia puede realizar operaciones tales como
establecer y obtener el valor, sin tener que agregar estas funciones explícitamente.
Además, se pueden aplicar controles de acceso, como permitir que el parámetro sea leído y
no escrito, o se puede aplicar la verificación de límites en los valores permitidos.

Nuevo Clase ID de tipo
Aquí, discutimos el impacto en un usuario que quiere agregar una nueva clase a ns-3. ¿Qué
se deben hacer cosas adicionales para permitir que contenga atributos?

Supongamos que nuestra nueva clase, llamada ns3::Mi Movilidad, es un tipo de modelo de movilidad. Primero,
la clase debe heredar de su clase padre, ns3::Modelo de movilidad. En la mi-movilidad.h
archivo de cabecera:

espacio de nombres ns3 {

clase MyClass: MobilityModel público
{

Esto requiere que declaremos el ObtenerIdTipo () función. Esta es una función pública de una línea.
declaración:

pública:
/ **
* Registre este tipo.
* \return El objeto TypeId.
*/
TypeId estático GetTypeId (vacío);

Ya hemos presentado lo que es un ID de tipo definición se verá como en el mi-movilidad.cc
archivo de implementación:

NS_OBJECT_ENSURE_REGISTERED (MiMovilidad);

ID de tipo
MiMovilidad::GetTypeId (vacío)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.SetParent ()
.SetGroupName ("Movilidad")
.AddConstructor ()
.AddAttribute ("Límites",
"Límites del área para navegar.",
RectangleValue (Rectángulo (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
HacerRectangleChecker ())
.AddAttribute ("Hora",
"Cambiar la dirección y velocidad actual después de moverse por este retraso.",
ValorTiempo (Segundos (1.0)),
MakeTimeAccessor (&MiMovilidad::m_modoTiempo),
MakeTimeChecker ())
// etc (más parámetros).
;
volver tid;
}

Si no queremos crear una subclase de una clase existente, en el archivo de encabezado simplemente heredamos
obtenidos de ns3 :: Objeto, y en el archivo de objeto establecemos la clase principal en ns3 :: Objeto con
.SetParent ().

Los errores típicos aquí implican:

· No llamar NS_OBJECT_ENSURE_REGISTERED ()

· No llamar al Padre de familia () método, o llamándolo con el tipo incorrecto.

· No llamar al AñadirConstructor () método, o llamándolo con el tipo incorrecto.

· Introducir un error tipográfico en el nombre del ID de tipo en su constructor.

· No utilizar el nombre de tipo C++ completo de la clase C++ adjunta como el nombre de la
ID de tipo. Tenga en cuenta que "ns3::" se requiere.

Ninguno de estos errores puede ser detectado por el ns-3 base de código, por lo que se recomienda a los usuarios que verifiquen
cuidadosamente varias veces que lo hicieron bien.

Nuevo Valor de atributo Type
Desde la perspectiva del usuario que escribe una nueva clase en el sistema y quiere que sea
accesible como un atributo, está principalmente la cuestión de escribir las conversiones a/desde
cadenas y valores de atributos. La mayor parte de esto se puede copiar/pegar con código macroizado. Para
ejemplo, considere una declaración de clase para Rectángulo en la categoría Industrial. src/movilidad/modelo directorio:

Encabezamiento Archive
/ **
* \breve un rectángulo 2d
*/
rectángulo de clase
{
...

doble xMin;
doble xmáx;
doble yMin;
doble ymáx;
};

Se debe agregar una llamada a macro y dos operadores debajo de la declaración de clase para
convertir un rectángulo en un valor utilizable por el Atributo sistema:

std::ostream &operator << (std::ostream &os, const Rectangle &rectangle);
std::istream &operator >> (std::istream &is, Rectangle &rectangle);

ATTRIBUTE_HELPER_HEADER (Rectángulo);

Implementación Archive
En la definición de clase (. CC archivo), el código se ve así:

ATTRIBUTE_HELPER_CPP (Rectángulo);

estándar::ostream &
operador << (std::ostream &os, const Rectangle &rectangle)
{
os << rectángulo.xMin << "|" << rectángulo.xMax << "|" << rectángulo.yMin << "|"
<< rectángulo.yMax;
volver os;
}
estándar::istream &
operador >> (std::istream &is, Rectángulo &rectángulo)
{
carácter c1, c2, c3;
es >> rectángulo.xMin >> c1 >> rectángulo.xMax >> c2 >> rectángulo.yMin >> c3
>> rectángulo.yMax;
si (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
es.setstate (std::ios_base::failbit);
}
el retorno es;
}

Estos operadores de flujo simplemente se convierten de una representación de cadena del Rectángulo
("xMin|xMax|yMin|yMax") al rectángulo subyacente. El modelador debe especificar estos
operadores y la representación sintáctica de cadena de una instancia de la nueva clase.

Tienda de configuración
Valores para ns-3 Los atributos pueden almacenarse en un archivo de texto ASCII o XML y cargarse en un
ejecución de simulación futura. Esta característica se conoce como la ns-3 ConfigStore. los Tienda de configuración is
una base de datos especializada para valores de atributos y valores predeterminados.

Aunque es un módulo mantenido por separado en el src/config-tienda/ directorio, nosotros
documentarlo aquí debido a su única dependencia de ns-3 módulo principal y atributos.

Podemos explorar este sistema usando un ejemplo de
src/config-store/examples/config-store-save.cc.

En primer lugar, todos los usuarios de la Tienda de configuración debe incluir la siguiente declaración:

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

A continuación, este programa agrega un objeto de muestra Ejemplo de configuración para mostrar cómo se extiende el sistema:

clase ConfigExample: objeto público
{
pública:
TypeId estático GetTypeId (vacío) {
Id de tipo estático tid = Id de tipo ("ns3::A")
.SetParent ()
.AddAttribute ("TestInt16", "texto de ayuda",
ValorEntero (-2),
MakeIntegerAccessor (&A::m_int16),
HacerIntegerChecker ())
;
volver tid;
}
int16_tm_int16;
};

NS_OBJECT_ENSURE_REGISTERED (Ejemplo de configuración);

A continuación, usamos el subsistema Config para anular los valores predeterminados de varias maneras:

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

ptr a_obj = CrearObjeto ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
"No se puede establecer el atributo entero de ConfigExample a través de Config::SetDefault");

ptr a2_obj = CrearObjeto ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
ValorEntero iv;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv. Obtener () == -3,
"No se puede establecer el atributo entero de ConfigExample a través de SetAttribute");

La siguiente declaración es necesaria para asegurarse de que (uno de) los objetos creados esté rooteado
en el espacio de nombres de configuración como una instancia de objeto. Esto normalmente sucede cuando Ud.
agregar objetos a un ns3::Nodo or ns3::Canal ejemplo, pero aquí, ya que estamos trabajando
en el nivel central, necesitamos crear un nuevo objeto de espacio de nombres raíz:

Configuración::RegisterRootNamespaceObject (a2_obj);

Escribiendo
A continuación, queremos mostrar el almacén de configuración. Los ejemplos muestran cómo hacerlo en dos
formatos, XML y texto sin procesar. En la práctica, se debe realizar este paso justo antes de llamar
Simulador::Ejecutar () para guardar la configuración final justo antes de ejecutar la simulación.

Hay tres atributos que gobiernan el comportamiento de ConfigStore: "Modo",
"Nombre del archivo"y "Formato de archivo". El modo (predeterminado "Ninguna") configura si ns-3 should
cargar la configuración desde un archivo previamente guardado (especificar "Modo=Cargar") o guardarlo en un archivo
(especificar "Modo=Guardar"). El nombre de archivo (predeterminado "") es donde ConfigStore debe leer o
escribir sus datos. El formato de archivo (predeterminado "Texto sin procesar") determina si el formato de ConfigStore
es texto sin formato o Xml ("Formato de archivo=Xml")

El ejemplo muestra:

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Guardar"));
Configuración de salida de ConfigStore;
salidaConfig.ConfigureDefaults ();
salidaConfig.ConfigureAttributes ();

// Almacenar configuración de salida a formato txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Guardar"));
ConfigStore salidaConfig2;
salidaConfig2.ConfigureDefaults ();
salidaConfig2.ConfigureAttributes ();

Simulador::Ejecutar ();

Simulador::Destruir ();

Tenga en cuenta la colocación de estas declaraciones justo antes de la Simulador::Ejecutar () .
Esta salida registra todos los valores en su lugar justo antes de iniciar la simulación (es decir,.
después de que se haya realizado toda la configuración).

Después de ejecutar, puede abrir el salida-atributos.txt archivar y ver:

predeterminado ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
predeterminado ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
predeterminado ns3::PcapFileWrapper::CaptureSize "65535"
predeterminado ns3::PacketSocket::RcvBufSize "131072"
predeterminado ns3::ErrorModel::IsEnabled "verdadero"
predeterminado ns3::RateErrorModel::ErrorUnit "EU_BYTE"
predeterminado ns3::RateErrorModel::ErrorRate "0"
predeterminado ns3::RateErrorModel::RanVar "Uniforme:0:1"
predeterminado ns3::DropTailQueue::Modo "Paquetes"
predeterminado ns3::DropTailQueue::MaxPackets "100"
predeterminado ns3::DropTailQueue::MaxBytes "6553500"
predeterminado ns3::Aplicación::Hora de inicio "+0.0ns"
predeterminado ns3::Aplicación::StopTime "+0.0ns"
predeterminado ns3::ConfigStore::Modo "Guardar"
predeterminado ns3::ConfigStore::Nombre de archivo "atributos de salida.txt"
predeterminado ns3::ConfigStore::FileFormat "RawText"
predeterminado ns3::ConfigExample::TestInt16 "-5"
RngSeed global "1"
RngRun global "1"
Global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
SchedulerType global "ns3::MapScheduler"
Suma de comprobación global Enabled "falso"
valor /$ns3::ConfigExample/TestInt16 "-3"

En lo anterior, se muestran todos los valores predeterminados para los atributos del módulo principal.
Luego, todos los valores de la ns-3 se registran los valores globales. Finalmente, el valor de la
en vez de Ejemplo de configuración que estaba enraizado en el espacio de nombres de configuración. en un
real ns-3 programa, se mostrarían muchos más modelos, atributos y valores predeterminados.

También existe una versión XML en atributos de salida.xml:




























Este archivo se puede archivar con su script de simulación y datos de salida.

Reading
A continuación, discutimos la configuración de simulaciones vía un archivo de configuración de entrada almacenado. Hay
un par de diferencias clave en comparación con escribir la configuración de simulación final.
Primero, necesitamos colocar sentencias como estas al comienzo del programa, antes
se escriben declaraciones de configuración de simulación (por lo que los valores se registran antes de ser
utilizado en la construcción de objetos).

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

A continuación, tenga en cuenta que la carga de los datos de configuración de entrada se limita al atributo predeterminado (es decir,.
no instancia) valores y valores globales. Los valores de instancia de atributo no son compatibles
porque en esta etapa de la simulación, antes de que se construyan los objetos, no hay
tales instancias de objetos alrededor. (Tenga en cuenta que las futuras mejoras en el almacén de configuración pueden cambiar
este comportamiento).

En segundo lugar, mientras que la salida de Tienda de configuración state listará todo en la base de datos, el
el archivo de entrada solo necesita contener los valores específicos que se anularán. Entonces, una forma de usar
esta clase para la configuración del archivo de entrada es generar una configuración inicial usando el
producción ("Salvar") "Modo" descrito anteriormente, extraiga de ese archivo de configuración solo el
elementos que uno desea cambiar y mover estos elementos mínimos a un nuevo archivo de configuración
que luego se puede editar y cargar de forma segura en una ejecución de simulación posterior.

Cuando el Tienda de configuración se instancia el objeto, sus atributos "Nombre del archivo", "Modo"y
"Formato de archivo" debe establecerse, ya sea vía línea de comandos o vía declaraciones del programa.

Leyendo escribiendo Ejemplo
Como ejemplo más complicado, supongamos que queremos leer en una configuración de
por defecto de un archivo de entrada llamado entrada-predeterminados.xml, y escribe el resultado
atributos a un archivo separado llamado atributos de salida.xml.:

#incluye "ns3/config-store-module.h"
...
int principal (...)
{

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

//
// Permitir que el usuario anule cualquiera de los valores predeterminados y el Bind () anterior en
// tiempo de ejecución, a través de argumentos de línea de comandos
//
Línea de comando cmd;
cmd. Parse (argc, argv);

// configurar la topología
...

// Invocar justo antes de entrar en Simulator::Run()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Guardar"));
Configuración de salida de ConfigStore;
salidaConfig.ConfigureAttributes ();
Simulador::Ejecutar ();
}

Tienda de configuración GUI
Hay una interfaz basada en GTK para ConfigStore. Esto permite a los usuarios utilizar una GUI para
acceder y cambiar variables. Las capturas de pantalla de esta característica están disponibles en el |ns3|
Resumen presentación.

Para usar esta característica, uno debe instalar libgtk y libgtk-dev; un ejemplo Ubuntu
El comando de instalación es:

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

Para verificar si está configurado o no, verifique la salida del paso:

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

---- Resumen de las características opcionales del NS-3:
Enlaces de Python: habilitado
Compatibilidad con la exploración de la API de Python: habilitada
NS-3 Haga clic en Integración: habilitado
GtkConfigStore: no habilitado (biblioteca 'gtk+-2.0 >= 2.12' no encontrada)

En el ejemplo anterior, no estaba habilitado, por lo que no se puede usar hasta que se encuentre una versión adecuada.
instalado y:

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

se vuelve a ejecutar.

El uso es casi el mismo que el de la versión no basada en GTK, pero no hay Tienda de configuración
atributos involucrados:

// Invocar justo antes de entrar en Simulator::Run()
Configuración de GtkConfigStore;
config.ConfigureDefaults ();
config.ConfigureAttributes ();

Ahora, cuando ejecute el script, debería aparecer una GUI que le permita abrir menús de
atributos en diferentes nodos/objetos, y luego inicie la ejecución de la simulación cuando
están hechos.

Futuro Trabaja
Hay un par de posibles mejoras:

· Guarda un número de versión único con fecha y hora al inicio del archivo.

· Guarda la semilla inicial de rng en alguna parte.

· Haz que cada RandomVariable serialice su propia semilla inicial y vuelva a leerla más tarde.

Objeto nombres
marcador de posición capítulo

Inicio de sesión
El ns-3 la función de registro se puede utilizar para monitorear o depurar el progreso de la simulación
programas La salida de registro se puede habilitar mediante sentencias de programa en su principal() programa o
por el uso de la NS_LOG Variable ambiental.

Las declaraciones de registro no se compilan en compilaciones optimizadas de ns-3. Para utilizar el registro, uno
debe compilar la compilación de depuración (predeterminada) de ns-3.

El proyecto no garantiza si la salida de registro seguirá siendo la misma durante
hora. Se advierte a los usuarios que no construyan marcos de salida de simulación además del registro
código, ya que la salida y la forma en que se habilita la salida pueden cambiar con el tiempo.

Resumen
ns-3 Las declaraciones de registro se utilizan normalmente para registrar varios eventos de ejecución de programas, como
como la ocurrencia de eventos de simulación o el uso de una función particular.

Por ejemplo, este fragmento de código es de Ipv4L3Protocol::EsDirecciónDestino():

si (dirección == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("Para mí (dirección de transmisión de interfaz)");
return true;
}

Si se ha habilitado el registro para el Protocolo Ipv4L3 componente con una severidad de LÓGICA or
arriba (ver más abajo sobre la gravedad del registro), se imprimirá la declaración; de lo contrario
será suprimido.

Habilitación Salida
Hay dos formas en que los usuarios suelen controlar la salida del registro. La primera es estableciendo el
NS_LOG Variable ambiental; p.ej:

$ NS_LOG="*" ./waf --ejecutar primero

ejecutará el first programa tutorial con toda la salida de registro. (Los detalles de la NS_LOG
El formato se discutirá más adelante.)

Esto se puede hacer más granular seleccionando componentes individuales:

$ NS_LOG="Ipv4L3Protocol" ./waf --ejecutar primero

La salida se puede personalizar aún más con opciones de prefijo.

La segunda forma de habilitar el registro es usar declaraciones explícitas en su programa, como en
los first programa tutorial:

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

(El significado de LOG_LEVEL_INFO, y otros valores posibles, se discutirán a continuación.)

NS_LOG Sintaxis
El NS_LOG La variable de entorno contiene una lista de componentes y opciones de registro. Tronco
los componentes están separados por caracteres `:':

$NS_LOG=" : ..."

Las opciones para cada componente de registro se dan como banderas después de cada componente de registro:

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

Las opciones controlan la gravedad y el nivel de ese componente, y si son opcionales
se debe incluir información, como el tiempo de simulación, el nodo de simulación, la función
nombre, y la severidad simbólica.

Log Componentes de la bolsa de equipaje
Generalmente, un componente de registro se refiere a un solo código fuente . CC archivo, y abarca la
archivo completo.

Algunos ayudantes tienen métodos especiales para habilitar el registro de todos los componentes en un módulo,
abarcando diferentes unidades de compilación, pero lógicamente agrupadas, como el ns-3
codigo wifi:

WifiAyudante wifiAyudante;
wifiHelper.EnableLogComponents();

El NS_LOG log componente comodín `*' habilitará todos los componentes.

Para ver qué componentes de registro están definidos, cualquiera de estos funcionará:

$ NS_LOG="imprimir-lista" ./waf --ejecutar...

$ NS_LOG="foo" # un token que no coincide con ningún componente de registro

El primer formulario imprimirá el nombre y las banderas habilitadas para todos los componentes de registro que están
vinculado en; pruébalo con simulador de arañazos. El segundo formulario imprime todo el registro registrado
componentes, luego salga con un error.

Gravedad y Nivel De Seguros
Los mensajes individuales pertenecen a una sola "clase de gravedad", establecida por la macro que crea el
mensaje. En el ejemplo anterior, NS_LOG_LOGIC(..) crea el mensaje en el LOG_LOGIC
clase de severidad.

Las siguientes clases de gravedad se definen como enumerar constantes:

┌──────────────────────────────────────────────────────── ─┐
│Clase de gravedad │ Significado │
├──────────────────────────────────────────────────────── ─┤
LOG_NONE │ El valor predeterminado, sin registro │
├──────────────────────────────────────────────────────── ─┤
REGISTRO_ERROR │ Solo mensajes de error graves │
├──────────────────────────────────────────────────────── ─┤
REGISTRO_ADVERTENCIA │ Mensajes de advertencia │
├──────────────────────────────────────────────────────── ─┤
LOG_DEBUG │ Para usar en la depuración │
├──────────────────────────────────────────────────────── ─┤
LOG_INFO │ Informativo │
├──────────────────────────────────────────────────────── ─┤
LOG_FUNCIÓN │ Rastreo de funciones │
├──────────────────────────────────────────────────────── ─┤
LOG_LOGIC │ Seguimiento del flujo de control dentro de │
│ │ funciones │
└──────────────────────────────────────────────────────── ─┘

Por lo general, uno quiere ver mensajes en una clase de gravedad determinada y higher. Esto es hecho por
definiendo "niveles" de registro inclusivo:

┌──────────────────┬───────────────────────────── ─────┐
│Nivel │ Significado │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_ERROR │ Solo REGISTRO_ERROR clase de severidad │
│ │ mensajes. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_WARNREGISTRO_ADVERTENCIA y por encima. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_DEBUGLOG_DEBUG y por encima. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_INFOLOG_INFO y por encima. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_FUNCTIONLOG_FUNCIÓN y por encima. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_LOGICLOG_LOGIC y por encima. │
├───────────────────┼───────────────────────────── ─────┤
LOG_LEVEL_ALL │ Todas las clases de gravedad. │
├───────────────────┼───────────────────────────── ─────┤
LOG_TODO │ Sinónimo de LOG_LEVEL_ALL
└───────────────────┴───────────────────────────── ─────┘

Las opciones de clase y nivel de severidad se pueden dar en el NS_LOG variable de entorno por
estas fichas:

┌─────────┬────────────────┐
│Clase │ Nivel │
├─────────┼────────────────┤
errornivel_error
├─────────┼────────────────┤
advertiradvertencia_nivel
├─────────┼────────────────┤
depurarnivel_depuración
├─────────┼────────────────┤
infonivel_info
├─────────┼────────────────┤
funciónfunción_nivel
├─────────┼────────────────┤
lógicanivel_logico
├─────────┼────────────────┤
│ │ nivel_todos
│ │ all
│ │ *
└─────────┴────────────────┘

El uso de un token de clase de gravedad habilita los mensajes de registro solo en esa gravedad. Por ejemplo,
NS_LOG="*=advertir" no generará mensajes con severidad error. NS_LOG="*=nivel_depuración" will
mensajes de salida en niveles de gravedad depurar y por encima.

Las clases y niveles de gravedad se pueden combinar con `|' operador:
NS_LOG="*=nivel_aviso|lógica" generará mensajes en niveles de gravedad error, advertir y lógica.

El NS_LOG nivel de gravedad comodín '*' y all son sinónimos de nivel_todos.

Para los componentes de registro simplemente mencionados en NS_LOG

$NS_LOG=" :..."

la gravedad predeterminada es LOG_LEVEL_ALL.

Prefijo De Seguros
Varios prefijos pueden ayudar a identificar dónde y cuándo se originó un mensaje, y en qué
severidad

Las opciones de prefijo disponibles (como enumerar constantes) son

┌─────────────────┬─────────────────────────────── ───┐
│Prefijo Símbolo │ Significado │
├────────────────┼─────────────────────────────── ───┤
LOG_PREFIX_FUNC │ Prefijo el nombre del llamante │
│ │ función. │
├────────────────┼─────────────────────────────── ───┤
LOG_PREFIX_TIME │ Prefijar el tiempo de simulación. │
├────────────────┼─────────────────────────────── ───┤
LOG_PREFIX_NODE │ Prefije la identificación del nodo. │
├────────────────┼─────────────────────────────── ───┤
LOG_PREFIX_LEVEL │ Prefijo el nivel de gravedad. │
├────────────────┼─────────────────────────────── ───┤
LOG_PREFIX_TODO │ Habilitar todos los prefijos. │
└─────────────────┴─────────────────────────────── ───┘

Las opciones de prefijo se describen brevemente a continuación.

Las opciones se pueden dar en el NS_LOG variable de entorno por estos tokens:

┌─────────────┬───────────┐
│Token │ Alterno │
├─────────────┼───────────┤
función_prefijodivertida
├─────────────┼───────────┤
prefijo_tiempotime
└─────────────┴───────────┘

nodo_prefijonodo
├─────────────┼───────────┤
nivel_prefijonivel
├─────────────┼───────────┤
prefijo_todosall
│ │ *
└─────────────┴───────────┘

Para los componentes de registro simplemente mencionados en NS_LOG

$NS_LOG=" :..."

las opciones de prefijo por defecto son LOG_PREFIX_TODO.

Gravedad Prefijo
La clase de gravedad de un mensaje se puede incluir con las opciones nivel_prefijo or nivel.
Por ejemplo, este valor de NS_LOG habilita el registro para todos los componentes de registro (`*') y todos
clases de severidad (= todo) y prefija el mensaje con la clase de gravedad (|prefijo_nivel).

$ NS_LOG="*=todo|nivel_prefijo" ./waf --ejecutar scratch-simulator
Simulador de arañazos
[ERROR] mensaje de error
[ADVERTENCIA] mensaje de advertencia
[DEBUG] mensaje de depuración
[INFO] mensaje de información
Mensaje de función [FUNCT]
[LOGICA] mensaje lógico

Hora Prefijo
El tiempo de simulación se puede incluir con las opciones prefijo_tiempo or time. Esto imprime el
Tiempo de simulación en segundos.

Nodo Prefijo
La identificación del nodo de simulación se puede incluir con las opciones nodo_prefijo or nodo.

Función Prefijo
El nombre de la función de llamada se puede incluir con las opciones función_prefijo or divertida.

NS_LOG Comodines
El comodín del componente de registro `*' habilitará todos los componentes. Para habilitar todos los componentes en un
uso de nivel de gravedad específico *=.

El comodín de opción de nivel de gravedad `*' es un sinónimo de all. Esto debe ocurrir antes de cualquier
'|' Caracteres que separan las opciones. Para habilitar todas las clases de gravedad, utilice =*,
or =*|.

La opción comodín `*' o token all habilita todas las opciones de prefijo, pero debe ocurrir después de a
'|' personaje. Para habilitar una clase o nivel de gravedad específico y todos los prefijos, use
= |*.

El comodín de opción combinada ** habilita todas las severidades y todos los prefijos; por ejemplo,
=**.

El súper comodín *** habilita todas las gravedades y todos los prefijos para todos los componentes de registro.
Todos estos son equivalentes:

$ NS_LOG="***" ... $ NS_LOG="*=todos|*" ... $ NS_LOG="*=*|todos" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=nivel_todo|*" ... $ NS_LOG="*=*|prefijo_todo" ...
$ NS_LOG="*=*|*" ...

Ojo: hasta lo trivial simulador de arañazos produce más de 46 XNUMX líneas de salida con
NS_LOG="***"!

Cómo a add registro a a tu manera código
Agregar registro a su código es muy simple:

1. Invocar el NS_LOG_COMPONENT_DEFINE (...); macro dentro de espacio de nombres ns3.
Cree un identificador de cadena único (generalmente basado en el nombre del archivo y/o clase
definido dentro del archivo) y regístrelo con una llamada de macro como la siguiente:

espacio de nombres ns3 {

NS_LOG_COMPONENT_DEFINE ("Protocolo Ipv4L3");
...

Esto registra Protocolo Ipv4L3 como componente de registro.

(La macro se escribió cuidadosamente para permitir su inclusión dentro o fuera de
espacio de nombres ns3, y el uso variará según el código base, pero la intención original era
registra esto outside del espacio de nombres ns3 en el ámbito global del archivo).

2. Agregue instrucciones de registro (llamadas a macros) a sus funciones y cuerpos de funciones.

Inicio de sesión Macros
Las macros de registro y los niveles de gravedad asociados son

┌───────────────┬────────────────────────
│Clase de gravedad │Macro │
├───────────────┼────────────────── ─
LOG_NONE │ (no se necesita) │
├───────────────┼────────────────── ─
REGISTRO_ERRORNS_LOG_ERROR (...);
├───────────────┼────────────────── ─
REGISTRO_ADVERTENCIANS_LOG_WARN (...);
├───────────────┼────────────────── ─
LOG_DEBUGNS_LOG_DEBUG (...);
├───────────────┼────────────────── ─
LOG_INFONS_LOG_INFO (...);
├───────────────┼────────────────── ─
LOG_FUNCIÓNNS_LOG_FUNCTION (...);
├───────────────┼────────────────── ─
LOG_LOGICNS_LOG_LOGIC (...);
└───────────────┴──────────────────── ────

Las macros funcionan como transmisores de salida, por lo que cualquier cosa que pueda enviar a std :: cout, unido
by << operadores, se permite:

void MyClass::Check (valor int, char * elemento)
{
NS_LOG_FUNCTION (este << arg << elemento);
si (arg > 10)
{
NS_LOG_ERROR ("se encontró un valor incorrecto" << valor <
" mientras revisa " << nombre << "!");
}
...
}

Tenga en cuenta que NS_LOG_FUNCTION inserta automáticamente un `,' (coma-espacio) separador entre
cada uno de sus argumentos. Esto simplifica el registro de argumentos de función; simplemente concatenar
con << como en el ejemplo anterior.

Incondicional Inicio de sesión
Como conveniencia, el NS_LOG_UNCOND (...); macro siempre registrará sus argumentos, incluso si
el componente de registro asociado no está habilitado en ninguna gravedad. Esta macro no utiliza ningún
de las opciones de prefijo. Tenga en cuenta que el registro solo está habilitado en compilaciones de depuración; esta macro
no producirá resultados en compilaciones optimizadas.

Líneas directrices
· Comience cada método de clase con NS_LOG_FUNCTION (esta << argumentos...); Esto permite fácil
seguimiento de llamadas de función.

· Excepto: no registre operadores ni constructores de copias explícitas, ya que estos causarán
recursividad infinita y desbordamiento de pila.

· Para métodos sin argumentos usa la misma forma: NS_LOG_FUNCTION (este);

· Para funciones estáticas:

· Con uso de argumentos NS_LOG_FUNCTION (...); como normal.

· Uso sin argumentos NS_LOG_FUNCTION_NOARGS ();

· Usar NS_LOG_ERROR para condiciones de error grave que probablemente invaliden la simulación
ejecución.

· Usar NS_LOG_WARN para condiciones inusuales que pueden ser corregibles. Por favor, da algunos consejos.
en cuanto a la naturaleza del problema y cómo podría corregirse.

· NS_LOG_DEBUG generalmente se usa en un ad hoc manera de entender la ejecución de un modelo.

· Usar NS_LOG_INFO para obtener información adicional sobre la ejecución, como el tamaño de un
estructura de datos al agregar/eliminar de ella.

· Usar NS_LOG_LOGIC para rastrear ramas lógicas importantes dentro de una función.

· Prueba que tus cambios de registro no rompan el código. Ejecute algunos programas de ejemplo con
todos los componentes de registro encendidos (p. ej. NS_LOG="***").

Rastreo
El subsistema de rastreo es uno de los mecanismos más importantes para entender en ns-3. En
mayoria de los casos, ns-3 los usuarios tendrán una idea brillante para algunas redes nuevas y mejoradas
rasgo. Para verificar que esta idea funciona, el investigador hará cambios en un
sistema existente y luego ejecute experimentos para ver cómo se comporta la nueva característica reuniendo
estadísticas que capturan el comportamiento de la característica.

En otras palabras, el objetivo de ejecutar una simulación es generar resultados para futuras
estudiar. En ns-3, el subsistema que permite a un investigador hacer esto es el rastreo
subsistema.

Rastreo Necesidades
Hay muchas formas de obtener información de un programa. La forma más directa es
para imprimir directamente la información en la salida estándar, como en,

#incluir
...
int principal ()
{
...
std::cout << "El valor de x es " << x << std::endl;
...
}

Esto funciona en entornos pequeños, pero a medida que sus simulaciones se vuelven más y más
complicado, termina con más y más impresiones y la tarea de analizar y realizar
los cálculos en la salida comienzan a ser más y más difíciles.

Otra cosa a considerar es que cada vez que se necesita un nuevo dato, el núcleo del software
debe ser editado y otra impresión introducida. No existe una forma estandarizada de controlar todos
de esta producción, por lo que la cantidad de producción tiende a crecer sin límites. Eventualmente, el
el ancho de banda requerido para simplemente generar esta información comienza a limitar el tiempo de ejecución
de la simulación. Los archivos de salida crecen a tamaños enormes y analizarlos se convierte en un
problema.

ns-3 proporciona un mecanismo simple para iniciar sesión y proporcionar cierto control sobre la salida a través de
Log Componentes de la bolsa de equipaje, pero el nivel de control no es muy fino en absoluto. el registro
módulo es un instrumento relativamente contundente.

Es deseable tener una instalación que permita llegar al sistema central y solo
obtenga la información requerida sin tener que cambiar y volver a compilar el sistema central. Incluso
mejor sería un sistema que notificara al usuario cuando un elemento de interés cambió o un
sucedió un evento interesante.

El ns-3 El sistema de seguimiento está diseñado para funcionar en ese sentido y está bien integrado con
los subtemas Attribute y Config que permiten escenarios de uso relativamente simples.

Resumen
El subsistema de rastreo se basa en gran medida en el ns-3 Mecanismos de devolución de llamada y atributos. Tú
debe leer y comprender las secciones correspondientes del manual antes de intentar
entender el sistema de seguimiento.

El ns-3 El sistema de rastreo se basa en los conceptos de fuentes de rastreo independientes y
sumideros de rastreo; junto con un mecanismo uniforme para conectar fuentes a sumideros.

Las fuentes de rastreo son entidades que pueden señalar eventos que suceden en una simulación y proporcionar
acceso a datos subyacentes interesantes. Por ejemplo, una fuente de rastreo podría indicar cuándo un
paquete es recibido por un dispositivo de red y proporciona acceso al contenido del paquete para
sumideros de traza interesados. Una fuente de rastreo también podría indicar cuándo un estado interesante
el cambio ocurre en un modelo. Por ejemplo, la ventana de congestión de un modelo TCP es una primera
candidata a fuente de rastreo.

Las fuentes de rastreo no son útiles por sí mismas; deben estar conectados a otras piezas de código
que realmente hacen algo útil con la información proporcionada por la fuente. los
las entidades que consumen información de seguimiento se denominan sumideros de seguimiento. Las fuentes de rastreo son
los generadores de eventos y los sumideros de seguimiento son consumidores.

Esta división explícita permite que un gran número de fuentes de rastreo se dispersen alrededor
el sistema en lugares que los autores del modelo creen que podrían ser útiles. A menos que un usuario conecte un
rastrear el receptor a una de estas fuentes, no se genera nada. Esta disposición permite relativamente
usuarios no sofisticados para adjuntar nuevos tipos de sumideros a las fuentes de rastreo existentes, sin
que requieren editar y volver a compilar el núcleo o los modelos del simulador.

Puede haber cero o más consumidores de eventos de seguimiento generados por un origen de seguimiento. Uno puede
Piense en una fuente de seguimiento como una especie de enlace de información de punto a multipunto.

El "protocolo de transporte" para este enlace conceptual punto a multipunto es un ns-3 Devolución de llamada.

Recuerde de la Sección de devolución de llamada que la función de devolución de llamada es una forma de permitir que dos módulos
el sistema para comunicarse a través de llamadas de función y al mismo tiempo desacoplar la llamada
completamente la función de la clase llamada. Este es el mismo requisito que se describe anteriormente.
para el sistema de rastreo.

Básicamente, una fuente de rastreo is una devolución de llamada a la que se pueden registrar varias funciones.
Cuando un sumidero de rastreo expresa interés en recibir eventos de rastreo, agrega una devolución de llamada a un
lista de devoluciones de llamada retenidas por el origen de seguimiento. Cuando sucede un evento interesante, el rastro
fuente invoca su operador() proporcionando cero o más parámetros. Esto le dice a la fuente que
revise su lista de devoluciones de llamada invocando cada una por turno. De esta forma, los parámetros
se comunican a los sumideros de seguimiento, que son solo funciones.

El Más simple Ejemplo
Será útil dar un ejemplo rápido solo para reforzar lo que hemos dicho:

#include "ns3/objeto.h"
#incluir "ns3/uinteger.h"
#include "ns3/valor-trazado.h""
#include "ns3/trace-source-accessor.h"

#incluir

usando el espacio de nombres ns3;

Lo primero que debe hacer es incluir los archivos necesarios. Como se mencionó anteriormente, el sistema de seguimiento
hace un uso intensivo de los sistemas de objetos y atributos. Los primeros dos incluyen traer en el
declaraciones para esos sistemas. El archivo, valor rastreado.h trae lo requerido
declaraciones para rastrear datos que obedecen a la semántica de valores.

En general, la semántica de valores solo significa que puede pasar el objeto, no un
dirección. Para usar la semántica de valor, debe tener un objeto con un
constructor de copia asociado y operador de asignación disponibles. Ampliamos los requisitos
para hablar sobre el conjunto de operadores que están predefinidos para los tipos de datos simples antiguos (POD).
Operador=, operador++, operador--, operador+, operador==, etc.

Lo que todo esto significa es que podrá rastrear los cambios en un objeto realizado utilizando
esos operadores.:

clase MiObjeto: Objeto público
{
pública:
TypeId estático GetTypeId (vacío)
{
TypeId estático tid = TypeId ("MyObject")
.SetParent (Objeto::GetTypeId ())
.AddConstructor ()
.AddTraceSource ("MiEntero",
"Un valor entero para rastrear.",
MakeTraceSourceAccessor (&MiObjeto::m_miInt))
;
volver tid;
}

MiObjeto () {}
valor rastreado m_miInt;
};

Dado que el sistema de rastreo está integrado con Atributos, y los Atributos funcionan con Objetos,
debe haber un ns-3 Objeto para que viva la fuente traza. Las dos líneas importantes de
código son los .AddTraceSource y la valor rastreado declaración.

El .AddTraceSource proporciona los "ganchos" utilizados para conectar la fuente de rastreo al
mundo exterior. los valor rastreado declaración proporciona la infraestructura que sobrecarga el
operadores mencionados anteriormente e impulsa el proceso de devolución de llamada.:

vacío
IntTrace (Int valor antiguo, Int valor nuevo)
{
std::cout << "Rastreado" << oldValue << " to " << newValue << std::endl;
}

Esta es la definición del sumidero de seguimiento. Corresponde directamente a una función de devolución de llamada.
Esta función será llamada siempre que uno de los operadores del valor rastreado is
ejecutado.:

int
principal (int argc, char * argv [])
{
ptr miObjeto = CrearObjeto ();

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

miObjeto->m_miInt = 1234;
}

En este fragmento, lo primero que hay que hacer es crear el objeto en el que
la fuente de rastreo vive.

El siguiente paso, el TraceConnectSinContexto, forma la conexión entre la traza
origen y el sumidero de seguimiento. Observe la Hacer devolución de llamada función de plantilla. Recordar de la
Sección de devolución de llamada que crea el functor especializado responsable de proporcionar el
sobrecargado operador() utilizado para "disparar" la devolución de llamada. Los operadores sobrecargados (++, --, etc.)
usará esto operador() para invocar realmente la devolución de llamada. los TraceConnectSinContexto,
toma un parámetro de cadena que proporciona el nombre del atributo asignado a la traza
fuente. Ignoremos la parte sobre el contexto por ahora, ya que aún no es importante.

Finalmente, la línea,:

miObjeto->m_miInt = 1234;

debe interpretarse como una invocación de operador = en la variable miembro m_miInt con
el entero 1234 pasado como parámetro. Resulta que este operador está definido (por
valor rastreado) para ejecutar una devolución de llamada que devuelve void y toma dos valores enteros como
parámetros: un valor antiguo y un valor nuevo para el entero en cuestión. eso es exactamente
la firma de la función para la función de devolución de llamada que proporcionamos -- IntTrace.

Para resumir, un origen de seguimiento es, en esencia, una variable que contiene una lista de devoluciones de llamada. A
Trace Sink es una función que se utiliza como destino de una devolución de llamada. El atributo y el tipo de objeto
Los sistemas de información se utilizan para proporcionar una forma de conectar las fuentes de seguimiento con los sumideros de seguimiento. los
El acto de "golpear" una fuente de rastreo es ejecutar un operador en la fuente de rastreo que dispara
devoluciones de llamada Esto da como resultado que las devoluciones de llamada del receptor de rastreo registren interés en la fuente
siendo llamado con los parámetros proporcionados por la fuente.

Gracias a los Config Subsistema a Conecta a Trace Fuentes
El TraceConnectSinContexto llamada que se muestra arriba en el ejemplo simple es en realidad muy
rara vez se utiliza en el sistema. Más típicamente, el Config subsistema se utiliza para permitir seleccionar
una fuente de rastreo en el sistema usando lo que se llama un config camino.

Por ejemplo, uno podría encontrar algo parecido a lo siguiente en el sistema (tomado
obtenidos de ejemplos/tcp-gran-transferencia.cc):

void CwndTracer (uint32_t valor anterior, uint32_t valor nuevo) {}

...

Configuración::ConectarSinContexto (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
Hacer Devolución de Llamada (&CwndTracer));

Esto debería parecer muy familiar. Es lo mismo que el ejemplo anterior, excepto que
una función miembro estática de clase Config se está llamando en lugar de un método en Objeto;
y en lugar de un Atributo nombre, se proporciona una ruta.

Lo primero que debe hacer es leer el camino hacia atrás. El último segmento del camino debe ser
an Atributo de una Objeto. De hecho, si tuviera un puntero a la Objeto que tiene el
"Ventana de congestión" Atributo práctico (llámalo el objeto), podrías escribir esto como el
ejemplo anterior:

void CwndTracer (uint32_t valor anterior, uint32_t valor nuevo) {}

...

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

Resulta que el código para Configuración::ConectarSinContexto hace exactamente eso. Este
función toma un camino que representa una cadena de Objeto punteros y los sigue hasta que
llega al final de la ruta e interpreta el último segmento como un Atributo en la última
objeto. Repasemos lo que sucede.

El carácter "/" inicial en la ruta se refiere a un llamado espacio de nombres. Uno de los
espacios de nombres predefinidos en el sistema de configuración es "NodeList", que es una lista de todos los
nodos en la simulación. Los elementos de la lista se refieren mediante índices a la lista, por lo que
"/NodeList/0" se refiere al nodo cero en la lista de nodos creados por la simulación.
Este nodo es en realidad un ptr y también lo es una subclase de un ns3 :: Objeto.

Como se describe en la sección Modelo de objetos, ns-3 admite un modelo de agregación de objetos. Él
siguiente segmento de ruta comienza con el carácter "$" que indica un Getobjeto la llamada debería ser
hecho buscando el tipo que sigue. Cuando un nodo es inicializado por un
Ayudante de pila de Internet una serie de interfaces se agregan al nodo. Uno de estos es el
Protocolo TCP nivel cuatro. El tipo de tiempo de ejecución de este objeto de protocolo es ns3::Protocolo TcpL4''.
Cuándo los ``ObtenerObjeto se ejecuta, devuelve un puntero al objeto de este tipo.

El Protocolo TcpL4 clase define un atributo llamado "SocketList" que es una lista de
enchufes Cada enchufe es en realidad un ns3 :: Objeto con su propio Atributos. los artículos en
se hace referencia a la lista de sockets por índice al igual que en NodeList, por lo que "SocketList/0"
se refiere al socket cero en la lista de sockets en el nodo cero en NodeList --
el primer nodo construido en la simulación.

Este enchufe, cuyo tipo resulta ser un ns3::TcpSocketImpl define un atributo
llamada "CongestionWindow", que es una valor rastreado.
Configuración::ConectarSinContexto ahora hace un,:

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

usando el puntero de objeto de "SocketList/0" que hace la conexión entre la traza
fuente definida en el socket a la devolución de llamada -- CwndTracer.

Ahora, cada vez que se realiza un cambio en el valor rastreado representando la congestión
ventana en el socket TCP, la devolución de llamada registrada se ejecutará y la función
CwndTracer se llamará imprimiendo los valores antiguos y nuevos de la congestión TCP
ventana.

Gracias a los Rastreo API
Hay tres niveles de interacción con el sistema de rastreo:

· El usuario principiante puede controlar fácilmente qué objetos participan en el rastreo;

· Los usuarios intermedios pueden ampliar el sistema de seguimiento para modificar el formato de salida generado
o usar fuentes de rastreo existentes de diferentes maneras, sin modificar el núcleo de la
simulador;

· Los usuarios avanzados pueden modificar el núcleo del simulador para agregar nuevas fuentes y sumideros de seguimiento.

Gracias a Trace Ayudantes
El ns-3 Los ayudantes de seguimiento proporcionan un entorno rico para configurar y seleccionar diferentes
rastrear eventos y escribirlos en archivos. En las secciones anteriores, principalmente "Construcción
Topologías", hemos visto varias variedades de los métodos auxiliares de seguimiento diseñados para su uso
dentro de otros ayudantes (dispositivos).

Tal vez recuerde haber visto algunas de estas variaciones:

pointToPoint.EnablePcapAll ("segundo");
pointToPoint.EnablePcap ("segundo", p2pNodes.Get (0)->GetId(), 0);
csma.EnablePcap ("tercero", csmaDevices.Get (0), verdadero);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));

Sin embargo, lo que puede no ser obvio es que existe un modelo consistente para todos los
métodos relacionados con el rastreo encontrados en el sistema. Ahora nos tomaremos un poco de tiempo y echaremos un vistazo
en el "panorama general".

Actualmente hay dos casos de uso principales de los ayudantes de rastreo en ns-3: Ayudantes de dispositivos
y ayudantes de protocolo. Los ayudantes de dispositivos analizan el problema de especificar qué trazas deben
habilitarse a través de un nodo, par de dispositivos. Por ejemplo, es posible que desee especificar que pcap
el seguimiento debe estar habilitado en un dispositivo particular en un nodo específico. Esto se sigue de la
ns-3 modelo conceptual del dispositivo, y también los modelos conceptuales de los diversos dispositivos
ayudantes Como consecuencia natural de esto, los archivos creados siguen un
- - convenio de denominación.

Los ayudantes de protocolo analizan el problema de especificar qué seguimientos deben habilitarse a través de
un par de protocolo e interfaz. Esto se sigue de la ns-3 modelo conceptual de pila de protocolo,
y también los modelos conceptuales de los asistentes de pila de Internet. Naturalmente, los archivos de rastreo
debe seguir un - - convenio de denominación.

Por lo tanto, los asistentes de rastreo caen naturalmente en una taxonomía bidimensional. Hay
sutilezas que impiden que las cuatro clases se comporten de manera idéntica, pero nos esforzamos por
hacer que todos funcionen de la manera más similar posible; y siempre que sea posible hay análogos para
todos los métodos en todas las clases.

┌────────────────┬──────┬───────┐
│ │ pcap │ ascii │
├────────────────┼──────┼───────┤
│Ayudante del dispositivo │ │ │
├────────────────┼──────┼───────┤
│Ayudante de protocolo │ │ │
└────────────────┴──────┴───────┘

Usamos un enfoque llamado Mixin para agregar la funcionalidad de rastreo a nuestras clases auxiliares. A
Mixin es una clase que proporciona funcionalidad a la que es heredada por una subclase.
Heredar de un mixin no se considera una forma de especialización, pero en realidad es una forma de
recoger la funcionalidad.

Echemos un vistazo rápido a estos cuatro casos y sus respectivos mezclando.

cap Rastreo Device Ayudantes
El objetivo de estos ayudantes es facilitar la adición de una función de seguimiento de pcap coherente a un
ns-3 dispositivo. Queremos que todos los diversos tipos de seguimiento de pcap funcionen de la misma manera
todos los dispositivos, por lo que los métodos de estos ayudantes son heredados por los ayudantes de dispositivos. Echar un vistazo
at src/network/helper/trace-helper.h si desea seguir la discusión mientras mira
código real

La clase PcapHelperParaDispositivo es un Mixin proporciona la funcionalidad de alto nivel para usar
seguimiento de pcap en un ns-3 dispositivo. Cada dispositivo debe implementar un solo método virtual
heredado de esta clase.:

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

La firma de este método refleja la visión centrada en el dispositivo de la situación en este
nivel. Todos los métodos públicos heredados de la clase. PcapUserHelperParaDispositivo reducido a
llamando a este único método de implementación dependiente del dispositivo. Por ejemplo, el nivel más bajo
método pcap,:

void EnablePcap (std::string prefijo, Ptr nd, bool promiscuous = false, bool explicitFilename = false);

llamará al dispositivo implementación de HabilitarPcapInternal directamente. Todos los demás pcap públicos
Los métodos de seguimiento se basan en esta implementación para proporcionar información adicional a nivel de usuario.
funcionalidad. Lo que esto significa para el usuario es que todos los dispositivos auxiliares del sistema
tener todos los métodos de seguimiento de pcap disponibles; y estos métodos funcionarán todos en el mismo
a través de los dispositivos si el dispositivo implementa HabilitarPcapInternal correctamente.

cap Rastreo Device Ayudante Métodos
void EnablePcap (std::string prefijo, Ptr Dakota del Norte,
bool promiscuo = falso, bool nombre de archivo explícito = falso);
void EnablePcap (std::prefijo de cadena, std::string ndName,
bool promiscuo = falso, bool nombre de archivo explícito = falso);
void EnablePcap (prefijo std::string, NetDeviceContainer d,
bool promiscuo = falso);
void EnablePcap (std::prefijo de cadena, NodeContainer n,
bool promiscuo = falso);
void EnablePcap (std::prefijo de cadena, uint32_t nodeid, uint32_t deviceid,
bool promiscuo = falso);
void EnablePcapAll (std::prefijo de cadena, bool promiscuo = falso);

En cada uno de los métodos que se muestran arriba, hay un parámetro predeterminado llamado promiscuo que
por defecto es falso. Este parámetro indica que la traza no debe recopilarse en
modo promiscuo. Si desea que sus seguimientos incluyan todo el tráfico visto por el dispositivo
(y si el dispositivo admite un modo promiscuo) simplemente agregue un parámetro verdadero a cualquiera de los
llamadas arriba. Por ejemplo,:

ptr Dakota del Norte;
...
helper.EnablePcap ("prefijo", nd, verdadero);

habilitará capturas de modo promiscuo en el dispositivo de red especificado por nd.

Los dos primeros métodos también incluyen un parámetro predeterminado llamado nombre de archivo explícito con amados villancicos que
ser discutido a continuación.

Se le anima a leer detenidamente el Doxygen para la clase. PcapHelperParaDispositivo para encontrar los detalles
de estos métodos; pero para resumir...

Puede habilitar el seguimiento de pcap en un par de nodo/dispositivo de red en particular proporcionando un
ptr a una Habilitar Pcap método. los ptr está implícito ya que el dispositivo de red
debe pertenecer exactamente a uno Nodo. Por ejemplo,:

ptr Dakota del Norte;
...
helper.EnablePcap ("prefijo", nd);

Puede habilitar el seguimiento de pcap en un par de nodo/dispositivo de red en particular proporcionando un
std :: string que representa una cadena de servicio de nombres de objetos a un Habilitar Pcap método. los
ptr se busca desde la cadena de nombre. Nuevamente, el está implícito ya que el
el dispositivo de red nombrado debe pertenecer exactamente a uno Nodo. Por ejemplo,:

Nombres::Agregar ("servidor" ...);
Nombres::Agregar ("servidor/eth0" ...);
...
helper.EnablePcap ("prefijo", "servidor/ath0");

Puede habilitar el seguimiento de pcap en una colección de pares de nodo/red-dispositivo proporcionando un
NetDeviceContainerNetDeviceContainer. Para cada dispositivo de red en el contenedor se comprueba el tipo. Para cada
dispositivo del tipo apropiado (el mismo tipo que es administrado por el ayudante del dispositivo), el rastreo es
activado. Nuevamente, el está implícito ya que el dispositivo de red encontrado debe pertenecer exactamente a
one Nodo. Por ejemplo,:

Contenedor de dispositivo de red d = ...;
...
helper.EnablePcap ("prefijo", d);

Puede habilitar el seguimiento de pcap en una colección de pares de nodo/red-dispositivo proporcionando un
Contenedor de nodo. Para cada Nodo en la categoría Industrial. Contenedor de nodo esta adjunto Dispositivos de red son iterados.
Para cada uno dispositivo de red adjunto a cada nodo en el contenedor, el tipo de ese dispositivo es
comprobado. Para cada dispositivo del tipo apropiado (el mismo tipo que es administrado por el dispositivo
ayudante), el rastreo está habilitado.:

Contenedor de nodo n;
...
helper.EnablePcap ("prefijo", n);

Puede habilitar el seguimiento de pcap sobre la base de la ID del nodo y la ID del dispositivo, así como con explícito
ptr. Cada Nodo en el sistema tiene un ID de nodo entero y cada dispositivo conectado a un nodo
tiene un ID de dispositivo entero:

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

Finalmente, puede habilitar el seguimiento de pcap para todos los dispositivos en el sistema, con el mismo tipo que
que gestiona el ayudante del dispositivo.:

helper.EnablePcapAll ("prefijo");

cap Rastreo Device Ayudante Nombre del archivo Selección
Implícito en las descripciones de métodos anteriores está la construcción de un nombre de archivo completo por
el método de implementación. Por convención, las trazas de pcap en el ns-3 sistema son de la forma
- identificación>- id>.pcap

Como se mencionó anteriormente, cada nodo en el sistema tendrá una identificación de nodo asignada por el sistema; y
cada dispositivo tendrá un índice de interfaz (también llamado ID de dispositivo) relativo a su nodo.
Por defecto, entonces, un archivo de rastreo pcap creado como resultado de habilitar el rastreo en el primer
dispositivo del nodo 21 usando el prefijo "prefijo" sería prefijo-21-1.pcap.

Siempre puedes usar el ns-3 servicio de nombres de objetos para que esto quede más claro. Por ejemplo, si
utiliza el servicio de nombres de objetos para asignar el nombre "servidor" al nodo 21, el pcap resultante
el nombre del archivo de rastreo se convertirá automáticamente, prefijo-servidor-1.pcap y si también asignas el
nombre "eth0" al dispositivo, su nombre de archivo pcap lo recogerá automáticamente y será
, que son prefijo-servidor-eth0.pcap.

Finalmente, dos de los métodos mostrados arriba:

void EnablePcap (std::string prefijo, Ptr nd, bool promiscuous = false, bool explicitFilename = false);
void EnablePcap (std::prefijo de cadena, std::string ndName, bool promiscuous = false, bool explicitFilename = false);

tener un parámetro por defecto llamado nombre de archivo explícito. Cuando se establece en verdadero, este parámetro
desactiva el mecanismo de finalización automática de nombre de archivo y le permite crear un archivo explícito
Nombre del archivo. Esta opción solo está disponible en los métodos que habilitan el seguimiento de pcap en un
dispositivo único.

Por ejemplo, para hacer arreglos para que un ayudante de dispositivo cree un solo pcap promiscuo
archivo de captura de un nombre específico (mi-pcap-archivo.pcap) en un dispositivo dado, uno podría:

ptr Dakota del Norte;
...
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);

El Primer su verdadero El parámetro habilita las trazas de modo promiscuo y el segundo le dice al ayudante
para interpretar el prefijo parámetro como un nombre de archivo completo.

ASCII Rastreo Device Ayudantes
El comportamiento del ayudante de rastreo ascii Mixin es sustancialmente similar a la versión pcap.
Eche un vistazo a src/network/helper/trace-helper.h si quieres seguir la discusión
mientras mira el código real.

La clase AsciiTraceHelperForDevice agrega la funcionalidad de alto nivel para usar ascii
seguimiento a una clase auxiliar de dispositivo. Como en el caso de pcap, cada dispositivo debe implementar un
único método virtual heredado de la traza ascii Mixin.:

vacío virtual EnableAsciiInternal (Ptr stream, std::string prefijo, Ptr sf) = 0;

La firma de este método refleja la visión centrada en el dispositivo de la situación en este
nivel; y también el hecho de que el ayudante puede estar escribiendo en un flujo de salida compartido. Todo
los métodos públicos relacionados con el seguimiento de ascii heredados de la clase AsciiTraceHelperForDevice
reducir a llamar a este único método de implementación dependiente del dispositivo. por ejemplo, el
métodos de rastreo ascii de nivel más bajo,:

void EnableAscii (std::string prefijo, Ptr Dakota del Norte);
void EnableAscii (Ptr corriente, punto Dakota del Norte);

llamará al dispositivo implementación de HabilitarAsciiInterno directamente, proporcionando una
prefijo o flujo válido. Todos los demás métodos públicos de rastreo ascii se basarán en estos
funciones de bajo nivel para proporcionar funcionalidad adicional a nivel de usuario. Lo que esto significa para el
usuario es que todos los asistentes de dispositivos en el sistema tendrán todos los métodos de rastreo ascii
disponible; y todos estos métodos funcionarán de la misma manera en todos los dispositivos si los dispositivos
implementar HabilitarAsciiInterno correctamente.

ASCII Rastreo Device Ayudante Métodos
void EnableAscii (std::string prefijo, Ptr Dakota del Norte);
void EnableAscii (Ptr corriente, punto Dakota del Norte);

void EnableAscii (std::string prefijo, std::string ndName);
void EnableAscii (Ptr corriente, std::string ndName);

void EnableAscii (std::string prefijo, NetDeviceContainer d);
void EnableAscii (Ptr corriente, NetDeviceContainer d);

void EnableAscii (std::prefijo de cadena, NodeContainer n);
void EnableAscii (Ptr corriente, NodeContainer n);

void EnableAscii (std::prefijo de cadena, uint32_t nodeid, uint32_t deviceid);
void EnableAscii (Ptr stream, uint32_t id de nodo, uint32_t id de dispositivo);

void EnableAsciiAll (prefijo std::string);
void EnableAsciiAll (Ptr corriente);

Se le anima a leer detenidamente el Doxygen para la clase. TraceHelperParaDispositivo para encontrar el
detalles de estos métodos; pero para resumir...

Hay el doble de métodos disponibles para el rastreo ascii que para pcap
rastreo. Esto se debe a que, además del modelo de estilo pcap donde los rastros de cada
un par único de nodo/dispositivo se escriben en un archivo único, admitimos un modelo en el que rastrear
la información de muchos pares de nodos/dispositivos se escribe en un archivo común. Esto significa que el
- - El mecanismo de generación de nombres de archivos se reemplaza por un mecanismo para
referirse a un archivo común; y el número de métodos API se duplica para permitir que todos
combinaciones.

Al igual que en el seguimiento de pcap, puede habilitar el seguimiento de ascii en un par de nodo/dispositivo de red en particular
al proporcionar un ptr a una HabilitarAscii método. los ptr está implícito ya que
el dispositivo de red debe pertenecer exactamente a uno Nodo. Por ejemplo,:

ptr Dakota del Norte;
...
ayudante.EnableAscii ("prefijo", nd);

En este caso, no se escriben contextos de seguimiento en el archivo de seguimiento ASCII, ya que serían
redundante. El sistema elegirá el nombre del archivo que se creará usando las mismas reglas que
descrito en la sección pcap, excepto que el archivo tendrá el sufijo ".tr" en lugar de
".pcap".

Si desea habilitar el seguimiento ascii en más de un dispositivo de red y enviar todos los seguimientos
a un solo archivo, también puede hacerlo usando un objeto para referirse a un solo archivo:

ptr nd1;
ptr nd2;
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
ayudante.EnableAscii (flujo, nd1);
ayudante.EnableAscii (flujo, nd2);

En este caso, los contextos de seguimiento se escriben en el archivo de seguimiento ASCII, ya que son necesarios.
para eliminar la ambigüedad de los rastros de los dos dispositivos. Tenga en cuenta que dado que el usuario está completamente
especificando el nombre del archivo, la cadena debe incluir ".tr" para mantener la coherencia.

Puede habilitar el rastreo ascii en un par particular de nodo/dispositivo de red proporcionando un
std :: string que representa una cadena de servicio de nombres de objetos a un Habilitar Pcap método. los
ptr se busca desde la cadena de nombre. Nuevamente, el está implícito ya que el
el dispositivo de red nombrado debe pertenecer exactamente a uno Nodo. Por ejemplo,:

Nombres::Add ("cliente" ...);
Nombres::Add ("cliente/eth0" ...);
Nombres::Agregar ("servidor" ...);
Nombres::Agregar ("servidor/eth0" ...);
...
helper.EnableAscii ("prefijo", "cliente/eth0");
helper.EnableAscii ("prefijo", "servidor/eth0");

Esto daría como resultado dos archivos llamados prefijo-cliente-eth0.tr y prefijo-servidor-eth0.tr con
seguimientos para cada dispositivo en el archivo de seguimiento respectivo. Dado que todos los EnableAscii
las funciones están sobrecargadas para tomar un contenedor de flujo, también puede usar ese formulario:

Nombres::Add ("cliente" ...);
Nombres::Add ("cliente/eth0" ...);
Nombres::Agregar ("servidor" ...);
Nombres::Agregar ("servidor/eth0" ...);
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
ayudante.EnableAscii (flujo, "cliente/eth0");
ayudante.EnableAscii (flujo, "servidor/eth0");

Esto daría como resultado un único archivo de rastreo llamado nombre-de-archivo-de-rastreo.tr que contiene todo
los eventos de rastreo para ambos dispositivos. Los eventos serían desambiguados por contexto de seguimiento
instrumentos de cuerda.

Puede habilitar el rastreo ascii en una colección de pares de nodo/red-dispositivo proporcionando un
NetDeviceContainerNetDeviceContainer. Para cada dispositivo de red en el contenedor se comprueba el tipo. Para cada
dispositivo del tipo apropiado (el mismo tipo que es administrado por el ayudante del dispositivo), el rastreo es
activado. Nuevamente, el está implícito ya que el dispositivo de red encontrado debe pertenecer exactamente a
one Nodo. Por ejemplo,:

Contenedor de dispositivo de red d = ...;
...
ayudante.EnableAscii ("prefijo", d);

Esto daría como resultado la creación de una serie de archivos de seguimiento ASCII, cada uno de los cuales sigue
la - - convención .tr. Combinando todos los rastros en un
el archivo único se logra de manera similar a los ejemplos anteriores:

Contenedor de dispositivo de red d = ...;
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
ayudante.EnableAscii (flujo, d);

Puede habilitar el rastreo ascii en una colección de pares de nodo/red-dispositivo proporcionando un
Contenedor de nodo. Para cada Nodo en la categoría Industrial. Contenedor de nodo esta adjunto Dispositivos de red son iterados.
Para cada uno dispositivo de red adjunto a cada nodo en el contenedor, el tipo de ese dispositivo es
comprobado. Para cada dispositivo del tipo apropiado (el mismo tipo que es administrado por el dispositivo
ayudante), el rastreo está habilitado.:

Contenedor de nodo n;
...
ayudante.EnableAscii ("prefijo", n);

Esto daría como resultado la creación de una serie de archivos de seguimiento ASCII, cada uno de los cuales sigue
la - - convención .tr. Combinando todos los rastros en un
el archivo único se logra de manera similar a los ejemplos anteriores:

Puede habilitar el seguimiento de pcap sobre la base de la ID del nodo y la ID del dispositivo, así como con explícito
ptr. Cada Nodo en el sistema tiene un ID de nodo entero y cada dispositivo conectado a un nodo
tiene un ID de dispositivo entero:

ayudante.EnableAscii ("prefijo", 21, 1);

Por supuesto, las trazas se pueden combinar en un solo archivo como se muestra arriba.

Finalmente, puede habilitar el seguimiento de pcap para todos los dispositivos en el sistema, con el mismo tipo que
que gestiona el ayudante del dispositivo.:

helper.EnableAsciiAll ("prefijo");

Esto daría como resultado la creación de una serie de archivos de rastreo ascii, uno para cada dispositivo en
el sistema del tipo manejado por el ayudante. Todos estos archivos seguirán el
- - convención .tr. Combinando todas las trazas en una sola
El archivo se logra de manera similar a los ejemplos anteriores.

ASCII Rastreo Device Ayudante Nombre del archivo Selección
Implícito en las descripciones anteriores del método estilo prefijo está la construcción del
nombres de archivo por el método de implementación. Por convención, las huellas ascii en el ns-3 sistema son
de la forma - identificación>- id>.tr.

Como se mencionó anteriormente, cada nodo en el sistema tendrá una identificación de nodo asignada por el sistema; y
cada dispositivo tendrá un índice de interfaz (también llamado ID de dispositivo) relativo a su nodo.
Entonces, por defecto, se crea un archivo de rastreo ascii como resultado de habilitar el rastreo en el primer
dispositivo del nodo 21, utilizando el prefijo "prefijo", sería prefijo-21-1.tr.

Siempre puedes usar el ns-3 servicio de nombres de objetos para que esto quede más claro. Por ejemplo, si
utiliza el servicio de nombres de objetos para asignar el nombre "servidor" al nodo 21, el resultado
el nombre del archivo de seguimiento ascii se convertirá automáticamente en, prefijo-servidor-1.tr y si también asignas
el nombre "eth0" al dispositivo, su nombre de archivo de seguimiento ascii lo recogerá automáticamente
y ser llamado prefijo-servidor-eth0.tr.

cap Rastreo Protocolo Ayudantes
El objetivo de estos mezclando es facilitar la adición de una función de seguimiento de pcap coherente a
protocolos Queremos que todos los diversos tipos de seguimiento de pcap funcionen de la misma manera en todos
protocolos, por lo que los métodos de estos ayudantes son heredados por los ayudantes de pila. Echa un vistazo a
src/network/helper/trace-helper.h si desea seguir la discusión mientras mira
código real

En esta sección, ilustraremos los métodos aplicados al protocolo. Ipv4. Para
especifique trazas en protocolos similares, simplemente sustituya el tipo apropiado. Por ejemplo,
Usar una ptr en lugar de un ptr y llama HabilitarPcapIpv6 en lugar de HabilitarPcapIpv4.

La clase PcapHelperParaIpv4 proporciona la funcionalidad de alto nivel para usar el seguimiento de pcap
en la categoría Industrial. Ipv4 protocolo. Cada ayudante de protocolo que habilite estos métodos debe implementar un solo
método virtual heredado de esta clase. Habrá una implementación separada para
Ipv6, por ejemplo, pero la única diferencia estará en los nombres y las firmas de los métodos.
Se requieren diferentes nombres de métodos para desambiguar la clase Ipv4 obtenidos de Ipv6 que son ambos
derivado de la clase Objetoy métodos que comparten la misma firma.:

virtual void EnablePcapIpv4Internal (std::string prefijo, Ptr ipv4, interfaz uint4_t) = 32;

La firma de este método refleja la vista centrada en el protocolo y la interfaz del
situación a este nivel. Todos los métodos públicos heredados de la clase. PcapHelperParaIpv4
reducir a llamar a este único método de implementación dependiente del dispositivo. por ejemplo, el
método pcap de nivel más bajo,:

void EnablePcapIpv4 (prefijo std::string, Ptr ipv4, interfaz uint4_t);

llamará al dispositivo implementación de HabilitarPcapIpv4Interno directamente. Todos los demás públicos
Los métodos de seguimiento de pcap se basan en esta implementación para proporcionar información adicional a nivel de usuario.
funcionalidad. Lo que esto significa para el usuario es que todos los asistentes de protocolo del sistema
tener todos los métodos de seguimiento de pcap disponibles; y estos métodos funcionarán todos en el mismo
camino a través de los protocolos si el ayudante implementa HabilitarPcapIpv4Interno correctamente.

cap Rastreo Protocolo Ayudante Métodos
Estos métodos están diseñados para estar en correspondencia uno a uno con el Nodo- y
dispositivo de red- versiones centradas de las versiones del dispositivo. En vez de Nodo y dispositivo de red par
restricciones, usamos restricciones de protocolo e interfaz.

Tenga en cuenta que, al igual que en la versión del dispositivo, hay seis métodos:

void EnablePcapIpv4 (prefijo std::string, Ptr ipv4, interfaz uint4_t);
void EnablePcapIpv4 (prefijo std::string, std::string ipv4Name, interfaz uint32_t);
void EnablePcapIpv4 (prefijo std::string, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std::prefijo de cadena, NodeContainer n);
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, interfaz uint32_t);
void EnablePcapIpv4All (prefijo std::string);

Se le anima a leer detenidamente el Doxygen para la clase. PcapHelperParaIpv4 para encontrar los detalles
de estos métodos; pero para resumir...

Puede habilitar el seguimiento de pcap en un par de protocolo/interfaz en particular proporcionando un
ptr y interfaz. a una Habilitar Pcap método. Por ejemplo,:

ptr ipv4 = nodo->ObtenerObjeto ();
...
helper.EnablePcapIpv4 ("prefijo", ipv4, 0);

Puede habilitar el seguimiento de pcap en un par de nodo/dispositivo de red en particular proporcionando un
std :: string que representa una cadena de servicio de nombres de objetos a un Habilitar Pcap método. los
ptr se busca desde la cadena de nombre. Por ejemplo,:

Nombres::Agregar ("servidorIPv4" ...);
...
helper.EnablePcapIpv4 ("prefijo", "serverIpv4", 1);

Puede habilitar el seguimiento de pcap en una colección de pares de protocolo/interfaz proporcionando un
Ipv4InterfazContenedor. Para cada Ipv4 / par de interfaz en el contenedor el tipo de protocolo
está chequeado. Para cada protocolo del tipo adecuado (el mismo tipo que gestiona el
ayudante del dispositivo), el rastreo está habilitado para la interfaz correspondiente. Por ejemplo,:

nodos NodeContainer;
...
Dispositivos NetDeviceContainer = deviceHelper.Install (nodos);
...
Ayudante de dirección ipv4 ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = ipv4.Assign (dispositivos);
...
helper.EnablePcapIpv4 ("prefijo", interfaces);

Puede habilitar el seguimiento de pcap en una colección de pares de protocolo/interfaz proporcionando un
Contenedor de nodo. Para cada Nodo en la categoría Industrial. Contenedor de nodo se encuentra el protocolo apropiado. Para
cada protocolo, se enumeran sus interfaces y se habilita el rastreo en el resultado
pares Por ejemplo,:

Contenedor de nodo n;
...
ayudante.EnablePcapIpv4 ("prefijo", n);

También puede habilitar el seguimiento de pcap en función de la ID del nodo y la interfaz. En este caso,
el ID de nodo se traduce a un ptr y se busca el protocolo adecuado en el
nodo. El protocolo y la interfaz resultantes se utilizan para especificar la traza resultante.
fuente.:

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

Finalmente, puede habilitar el seguimiento de pcap para todas las interfaces del sistema, con
siendo el protocolo del mismo tipo que el administrado por el ayudante del dispositivo.:

helper.EnablePcapIpv4All ("prefijo");

cap Rastreo Protocolo Ayudante Nombre del archivo Selección
Implícita en todas las descripciones de métodos anteriores está la construcción del
nombres de archivo por el método de implementación. Por convención, los seguimientos de pcap tomados para dispositivos en
los ns-3 sistema son de la forma - identificación>- id>.pcap. En el caso de
rastros de protocolo, hay una correspondencia uno a uno entre protocolos y Nodes. Es
porque el protocolo Objetos se agregan a Nodo Objetos. Como no existe un protocolo global
id en el sistema, usamos el id de nodo correspondiente en el nombre del archivo. Por lo tanto hay un
posibilidad de colisiones de nombres de archivos en nombres de archivos de rastreo elegidos automáticamente. Para esto
razón, la convención de nombre de archivo se cambia para las trazas de protocolo.

Como se mencionó anteriormente, cada nodo en el sistema tendrá una identificación de nodo asignada por el sistema.
Dado que existe una correspondencia uno a uno entre las instancias de protocolo y las instancias de nodo
usamos la identificación del nodo. Cada interfaz tiene una identificación de interfaz relativa a su protocolo. Usamos
la Convención " -norte -i .pcap" para la denominación de archivos de seguimiento en
ayudantes de protocolo

Por lo tanto, de forma predeterminada, un archivo de rastreo pcap creado como resultado de habilitar el rastreo en
interfaz 1 del protocolo Ipv4 del nodo 21 utilizando el prefijo "prefijo" sería
"prefijo-n21-i1.pcap".

Siempre puedes usar el ns-3 servicio de nombres de objetos para que esto quede más claro. Por ejemplo, si
utiliza el servicio de nombres de objetos para asignar el nombre "serverIpv4" al Ptr en el nodo
21, el nombre del archivo de seguimiento de pcap resultante se convertirá automáticamente en,
"prefijo-nserverIpv4-i1.pcap".

ASCII Rastreo Protocolo Ayudantes
El comportamiento de los asistentes de seguimiento de ascii es sustancialmente similar al caso de pcap. Tomar un
mirar src/network/helper/trace-helper.h si quieres seguir la discusión mientras
mirando código real.

En esta sección, ilustraremos los métodos aplicados al protocolo. Ipv4. Para
especifique trazas en protocolos similares, simplemente sustituya el tipo apropiado. Por ejemplo,
Usar una ptr en lugar de un ptr y llama HabilitarAsciiIpv6 en lugar de
HabilitarAsciiIpv4.

La clase AsciiTraceHelperParaIpv4 agrega la funcionalidad de alto nivel para usar ascii
seguimiento a un ayudante de protocolo. Cada protocolo que habilite estos métodos debe implementar un
único método virtual heredado de esta clase.:

vacío virtual EnableAsciiIpv4Internal (Ptr flujo, std::prefijo de cadena,
ptr ipv4, interfaz uint4_t) = 32;

La firma de este método refleja la visión centrada en el protocolo y la interfaz del
situación a este nivel; y también el hecho de que el ayudante puede estar escribiendo a un
salida de corriente. Todos los métodos públicos heredados de la clase.
PcapAndAsciiTraceHelperParaIpv4 reducir a llamar a este único dispositivo dependiente
método de implementación. Por ejemplo, los métodos de rastreo ascii de nivel más bajo:

void EnableAsciiIpv4 (prefijo std::string, Ptr ipv4, interfaz uint4_t);
anular EnableAsciiIpv4 (Ptr corriente, punto ipv4, interfaz uint4_t);

llamará al dispositivo implementación de HabilitarAsciiIpv4Interno directamente, proporcionando
el prefijo o la secuencia. Todos los demás métodos públicos de rastreo ascii se basarán en estos
funciones de bajo nivel para proporcionar funcionalidad adicional a nivel de usuario. Lo que esto significa para el
usuario es que todos los asistentes de dispositivos en el sistema tendrán todos los métodos de rastreo ascii
disponible; y todos estos métodos funcionarán de la misma manera en todos los protocolos si el
implementar protocolos HabilitarAsciiIpv4Interno correctamente.

ASCII Rastreo Device Ayudante Métodos
void EnableAsciiIpv4 (prefijo std::string, Ptr ipv4, interfaz uint4_t);
anular EnableAsciiIpv4 (Ptr corriente, punto ipv4, interfaz uint4_t);

void EnableAsciiIpv4 (prefijo std::string, std::string ipv4Name, interfaz uint32_t);
anular EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, interfaz uint32_t);

void EnableAsciiIpv4 (std::prefijo de cadena, Ipv4InterfaceContainer c);
anular EnableAsciiIpv4 (Ptr flujo, Ipv4InterfaceContainer c);

void EnableAsciiIpv4 (std::prefijo de cadena, NodeContainer n);
anular EnableAsciiIpv4 (Ptr corriente, NodeContainer n);

void EnableAsciiIpv4 (std::prefijo de cadena, uint32_t nodeid, uint32_t deviceid);
anular EnableAsciiIpv4 (Ptr stream, uint32_t nodeid, interfaz uint32_t);

void EnableAsciiIpv4All (prefijo std::string);
void EnableAsciiIpv4All (Ptr arroyo);

Se le anima a leer detenidamente el Doxygen para la clase. PcapAndAsciiHelperParaIpv4 para encontrar el
detalles de estos métodos; pero para resumir...

Hay el doble de métodos disponibles para el rastreo ascii que para pcap
rastreo. Esto se debe a que, además del modelo de estilo pcap donde los rastros de cada
un par protocolo/interfaz único se escriben en un archivo único, admitimos un modelo en el que
la información de rastreo para muchos pares de protocolo/interfaz se escribe en un archivo común. Este
significa que el -norte - Se reemplaza el mecanismo de generación de nombres de archivo.
por un mecanismo para hacer referencia a un archivo común; y el número de métodos API se duplica a
permitir todas las combinaciones.

Al igual que en el seguimiento de pcap, puede habilitar el seguimiento de ascii en un protocolo/interfaz en particular
par proporcionando un ptr y una interfaz. a una HabilitarAscii método. Por ejemplo,:

ptr ipv4;
...
ayudante.EnableAsciiIpv4 ("prefijo", ipv4, 1);

En este caso, no se escriben contextos de seguimiento en el archivo de seguimiento ASCII, ya que serían
redundante. El sistema elegirá el nombre del archivo que se creará usando las mismas reglas que
descrito en la sección pcap, excepto que el archivo tendrá el sufijo ".tr" en lugar de
".pcap".

Si desea habilitar el rastreo ascii en más de una interfaz y enviar todos los rastreos a
un solo archivo, también puede hacerlo usando un objeto para referirse a un solo archivo. Nosotros
ya tiene algo similar a esto en el ejemplo "cwnd" anterior:

ptr protocolo4 = nodo1->GetObject ();
ptr protocolo4 = nodo2->GetObject ();
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
ayudante.EnableAsciiIpv4 (flujo, protocolo1, 1);
ayudante.EnableAsciiIpv4 (flujo, protocolo2, 1);

En este caso, los contextos de seguimiento se escriben en el archivo de seguimiento ASCII, ya que son necesarios.
para eliminar la ambigüedad de los rastros de las dos interfaces. Tenga en cuenta que dado que el usuario está completamente
especificando el nombre del archivo, la cadena debe incluir ".tr" para mantener la coherencia.

Puede habilitar el rastreo ascii en un protocolo particular al proporcionar un std :: string
que representa una cadena de servicio de nombres de objetos a un Habilitar Pcap método. los ptr is
levantó la vista de la cadena de nombres. los en los nombres de archivo resultantes está implícito ya que
existe una correspondencia uno a uno entre instancias de protocolo y nodos, por ejemplo:

Nombres::Agregar ("nodo1Ipv4" ...);
Nombres::Agregar ("nodo2Ipv4" ...);
...
helper.EnableAsciiIpv4 ("prefijo", "nodo1Ipv4", 1);
helper.EnableAsciiIpv4 ("prefijo", "nodo2Ipv4", 1);

Esto daría como resultado dos archivos llamados "prefix-nnode1Ipv4-i1.tr" y
"prefix-nnode2Ipv4-i1.tr" con rastros para cada interfaz en el archivo de rastro respectivo.
Dado que todas las funciones EnableAscii están sobrecargadas para tomar un contenedor de flujo, puede
Usa esa forma también:

Nombres::Agregar ("nodo1Ipv4" ...);
Nombres::Agregar ("nodo2Ipv4" ...);
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
ayudante.EnableAsciiIpv4 (flujo, "node1Ipv4", 1);
ayudante.EnableAsciiIpv4 (flujo, "node2Ipv4", 1);

Esto daría como resultado un único archivo de rastreo llamado "trace-file-name.tr" que contiene todos los
los eventos de rastreo para ambas interfaces. Los eventos serían desambiguados por contexto de seguimiento
instrumentos de cuerda.

Puede habilitar el rastreo ascii en una colección de pares de protocolo/interfaz proporcionando un
Ipv4InterfazContenedor. Para cada protocolo del tipo adecuado (el mismo tipo que se gestiona
por el ayudante del dispositivo), el rastreo está habilitado para la interfaz correspondiente. Nuevamente, el
está implícito ya que hay una correspondencia uno a uno entre cada protocolo y
su nodo. Por ejemplo,:

nodos NodeContainer;
...
Dispositivos NetDeviceContainer = deviceHelper.Install (nodos);
...
Ayudante de dirección ipv4 ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = ipv4.Assign (dispositivos);
...
...
helper.EnableAsciiIpv4 ("prefijo", interfaces);

Esto daría como resultado la creación de una serie de archivos de seguimiento ASCII, cada uno de los cuales sigue
la -norte -i convención .tr. Combinando todos los rastros en un
el archivo único se logra de manera similar a los ejemplos anteriores:

nodos NodeContainer;
...
Dispositivos NetDeviceContainer = deviceHelper.Install (nodos);
...
Ayudante de dirección ipv4 ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer interfaces = ipv4.Assign (dispositivos);
...
ptr stream = asciiTraceHelper.CreateFileStream ("trace-file-name.tr");
...
helper.EnableAsciiIpv4 (flujo, interfaces);

Puede habilitar el rastreo ascii en una colección de pares de protocolo/interfaz proporcionando un
Contenedor de nodo. Para cada Nodo en la categoría Industrial. Contenedor de nodo se encuentra el protocolo apropiado. Para
cada protocolo, se enumeran sus interfaces y se habilita el rastreo en el resultado
pares Por ejemplo,:

Contenedor de nodo n;
...
ayudante.EnableAsciiIpv4 ("prefijo", n);

Esto daría como resultado la creación de una serie de archivos de seguimiento ASCII, cada uno de los cuales sigue
la - - convención .tr. Combinando todos los rastros en un
el archivo único se logra de manera similar a los ejemplos anteriores:

También puede habilitar el seguimiento de pcap en función de la ID del nodo y la ID del dispositivo. En este caso,
el ID de nodo se traduce a un ptr y se busca el protocolo adecuado en el
nodo. El protocolo y la interfaz resultantes se utilizan para especificar la traza resultante.
fuente.:

ayudante.EnableAsciiIpv4 ("prefijo", 21, 1);

Por supuesto, las trazas se pueden combinar en un solo archivo como se muestra arriba.

Finalmente, puede habilitar el rastreo ascii para todas las interfaces del sistema, con
siendo el protocolo del mismo tipo que el administrado por el ayudante del dispositivo.:

helper.EnableAsciiIpv4All ("prefijo");

Esto daría como resultado la creación de una serie de archivos de seguimiento ASCII, uno para cada interfaz.
en el sistema relacionado con un protocolo del tipo gestionado por el ayudante. Todos estos archivos
seguirá el -norte -i
en un solo archivo se logra de manera similar a los ejemplos anteriores.

ASCII Rastreo Device Ayudante Nombre del archivo Selección
Implícito en las descripciones anteriores del método estilo prefijo está la construcción del
nombres de archivo por el método de implementación. Por convención, las huellas ascii en el ns-3 sistema son
de la forma " - - .tr."

Como se mencionó anteriormente, cada nodo en el sistema tendrá una identificación de nodo asignada por el sistema.
Dado que existe una correspondencia uno a uno entre los protocolos y los nodos, usamos node-id
para identificar la identidad del protocolo. Cada interfaz en un protocolo dado tendrá un
índice de interfaz (también llamado simplemente interfaz) relativo a su protocolo. Por defecto,
luego, un archivo de rastreo ascii creado como resultado de habilitar el rastreo en el primer dispositivo de
el nodo 21, usando el prefijo "prefijo", sería "prefijo-n21-i1.tr". Usa el prefijo para
eliminar la ambigüedad de múltiples protocolos por nodo.

Siempre puedes usar el ns-3 servicio de nombres de objetos para que esto quede más claro. Por ejemplo, si
utiliza el servicio de nombres de objetos para asignar el nombre "serverIpv4" al protocolo en el nodo
21, y también especifique la interfaz uno, el nombre del archivo de seguimiento ascii resultante se
convertirse en "prefijo-nserverIpv4-1.tr".

Rastreo implementación detalles
Data Colecciones
Este capítulo describe el marco de recopilación de datos ns-3 (DCF), que proporciona
capacidades para obtener datos generados por modelos en el simulador, para realizar en línea
reducción y procesamiento de datos, y para ordenar datos sin procesar o transformados en varios resultados
formatos.

El marco actualmente admite ejecuciones independientes de ns-3 que no dependen de ningún
control de ejecución del programa. Los objetos proporcionados por el DCF pueden engancharse a ns-3 rastrear
fuentes para permitir el procesamiento de datos.

El código fuente de las clases vive en el directorio. src / stats.

Este capítulo está organizado de la siguiente forma. Primero, se ofrece una descripción general de la arquitectura.
presentado. A continuación, se presentan los ayudantes para estas clases; este tratamiento inicial
debería permitir el uso básico del marco de recopilación de datos para muchos casos de uso. Usuarios que
desean producir resultados fuera del alcance de los ayudantes actuales, o que desean crear
sus propios objetos de recopilación de datos, debe leer el resto del capítulo, que va
en detalle sobre todos los tipos de objetos DCF básicos y proporciona codificación de bajo nivel
ejemplos.

Design
El DCF consta de tres clases básicas:

· Muestra es un mecanismo para instrumentar y controlar la salida de datos de simulación que se
utilizado para monitorear eventos interesantes. Produce resultados en forma de uno o más ns-3
rastrear fuentes. Los objetos de la sonda están conectados a una o más trazas sumideros (Llamado
Colectores), que procesan muestras en línea y las preparan para su salida.

· Coleccionista consume los datos generados por uno o más objetos Probe. Realiza
transformaciones en los datos, como la normalización, la reducción y el cálculo de
estadísticas básicas. Los objetos recopiladores no producen datos directamente
ejecución ns-3; en su lugar, generan datos en sentido descendente a otro tipo de objeto, llamado
Aggregator, que realiza esa función. Normalmente, los recopiladores generan sus datos en
también en forma de fuentes de trazas, lo que permite encadenar a los recolectores en serie.

· Aggregator es el punto final de los datos recopilados por una red de sondas y recopiladores.
La principal responsabilidad del Agregador es reunir los datos y sus correspondientes
metadatos, en diferentes formatos de salida, como archivos de texto sin formato, archivos de hoja de cálculo o
bases de datos.

Las tres clases proporcionan la capacidad de activarse o desactivarse dinámicamente.
a lo largo de una simulación.

Cualquiera independiente ns-3 La ejecución de simulación que utiliza el DCF normalmente creará al menos una
instancia de cada una de las tres clases anteriores.
[imagen] Descripción general del marco de recopilación de datos.

El flujo general del procesamiento de datos se describe en Data Colecciones Marco conceptual visión de conjunto.
En el lado izquierdo, una carrera ns-3 Se representa la simulación. En el curso de la ejecución del
simulación, los datos se ponen a disposición mediante modelos a través de fuentes de seguimiento o por otros medios.
El diagrama muestra que las sondas se pueden conectar a estas fuentes de rastreo para recibir datos
asincrónicamente, o las sondas pueden sondear datos. Luego, los datos se pasan a un objeto recopilador
que transforma los datos. Finalmente, se puede conectar un agregador a las salidas del
colector, para generar parcelas, archivos o bases de datos.
[imagen] Agregación del marco de recopilación de datos.UNINDENT

Se proporciona una variación de la figura anterior en Data Colecciones Marco conceptual agregación.
Esta segunda figura ilustra que los objetos DCF se pueden encadenar juntos de una manera
que los objetos aguas abajo toman entradas de múltiples objetos aguas arriba. La figura
muestra conceptualmente que múltiples sondas pueden generar una salida que se alimenta a un solo
coleccionista; como ejemplo, un colector que genera una relación de dos contadores
normalmente adquieren los datos de cada contador de sondas independientes. Múltiples recolectores también pueden
alimentar a un solo agregador, que (como su nombre lo indica) puede recopilar una serie de datos
corrientes para su inclusión en una única parcela, archivo o base de datos.

Data Colecciones Ayudantes
La plena flexibilidad del marco de recopilación de datos es proporcionada por la interconexión
de sondas, colectores y agregadores. Realizar todas estas interconexiones conduce a
muchas declaraciones de configuración en programas de usuario. Para facilitar su uso, algunos de los más comunes
las operaciones se pueden combinar y encapsular en funciones auxiliares. Además, algunos
declaraciones que involucran ns-3 las fuentes de seguimiento no tienen enlaces de Python, debido a limitaciones en
las fijaciones.

Data Colecciones Ayudantes Resumen
En esta sección, proporcionamos una descripción general de algunas clases auxiliares que se han creado para
Facilitar la configuración del marco de recopilación de datos para algunos casos de uso comunes. El
Los ayudantes permiten a los usuarios formar operaciones comunes con solo unas pocas declaraciones en su C ++ o
Programas de Python. Pero esta facilidad de uso tiene el costo de una cantidad significativamente menor
flexibilidad que la configuración de bajo nivel puede proporcionar, y la necesidad de codificar explícitamente
compatibilidad con nuevos tipos de sonda en los ayudantes (para solucionar un problema que se describe a continuación).

El énfasis en los ayudantes actuales es reunir datos de ns-3 rastrear fuentes en
gráficos de gnuplot o archivos de texto, sin un alto grado de personalización de salida o estadísticos
procesamiento (inicialmente). Además, el uso está restringido a los tipos de sonda disponibles en
ns-3. Las secciones posteriores de esta documentación entrarán en más detalles sobre la creación de nuevos
Tipos de sondas, así como detalles sobre cómo conectar sondas, recopiladores y agregadores
en arreglos personalizados.

Hasta la fecha, se han implementado dos ayudantes de recopilación de datos:

· Ayudante de Gnuplot

· Ayudante de archivos

Ayudante de Gnuplot
GnuplotHelper es una clase auxiliar para producir archivos de salida que se utilizan para hacer gnuplots. El
El objetivo general es proporcionar a los usuarios la capacidad de realizar rápidamente gráficos a partir de los datos exportados.
in ns-3 rastrear fuentes. De forma predeterminada, se realiza una cantidad mínima de transformación de datos;
El objetivo es generar gráficos con tan pocas declaraciones de configuración (predeterminadas) como
posible.

Ayudante de Gnuplot Resumen
GnuplotHelper creará 3 archivos diferentes al final de la simulación:

· Un archivo de datos gnuplot separado por espacios

· Un archivo de control de gnuplot

· Un script de shell para generar el gnuplot

Hay dos declaraciones de configuración que se necesitan para producir gráficos. La primera
declaración configura la trama (nombre de archivo, título, leyendas y tipo de salida, donde la salida
el tipo predeterminado es PNG si no se especifica):

ConfigurePlot vacío (const std :: string & outputFileNameWithoutExtension,
const std :: cadena y título,
const std :: string & xLegend,
const std :: string & yLegend,
const std :: string & terminalType = ".png");

La segunda declaración engancha la fuente de seguimiento de interés:

vacío PlotProbe (const std :: string & typeId,
const std :: cadena y ruta,
const std :: string & probeTraceSource,
const std :: string & title);

Los argumentos son los siguientes:

· TypeId: El ns-3 TypeId de la sonda

· Camino: El camino en el ns-3 espacio de nombres de configuración a una o más fuentes de seguimiento

· ProbeTraceSource: qué salida de la sonda (en sí misma una fuente de rastreo) debe trazarse

· Título: el título que se asociará con el (los) conjunto (s) de datos (en la leyenda de gnuplot)

Una variante del PlotProbe anterior es especificar un quinto argumento opcional que controla
donde en la trama se coloca la clave (leyenda).

Un ejemplo completamente elaborado (de séptimo.cc) se muestra a continuación:

// Crea el asistente de gnuplot.
GnuplotHelper plotHelper;

// Configura la trama.
// Configura la trama. El primer argumento es el prefijo del nombre del archivo.
// para los archivos de salida generados. El segundo, tercero y cuarto
// los argumentos son, respectivamente, el título de la trama, el eje xy las etiquetas del eje y
plotHelper.ConfigurePlot ("recuento de bytes del séptimo paquete",
"Recuento de bytes de paquetes frente a tiempo",
"Tiempo (segundos)",
"Recuento de bytes de paquetes",
"png");

// Especifique el tipo de sonda, la ruta de origen del rastreo (en el espacio de nombres de configuración) y
// sondear la fuente de rastreo de salida ("OutputBytes") para trazar. El cuarto argumento
// especifica el nombre de la etiqueta de la serie de datos en el gráfico. El último
// El argumento formatea el gráfico especificando dónde debe colocarse la clave.
plotHelper.PlotProbe (tipo de sonda,
ruta de seguimiento,
"OutputBytes",
"Recuento de bytes de paquetes",
GnuplotAggregator :: KEY_BELOW);

En este ejemplo, el tipo de sonda y ruta de seguimiento son los siguientes (para IPv4):

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

ProbeType es un parámetro clave para que este ayudante funcione. Este TypeId debe estar registrado
en el sistema, y ​​la firma en el receptor de seguimiento de la sonda debe coincidir con la del seguimiento
fuente a la que se está conectando. Los tipos de sonda están predefinidos para varios tipos de datos
correspondiente a ns-3 valores de seguimiento, y para algunas otras firmas de origen de seguimiento, como
la fuente de rastreo 'Tx' de ns3 :: Ipv4L3Protocol clase.

Tenga en cuenta que la ruta de origen de seguimiento especificada puede contener comodines. En este caso, varios
los conjuntos de datos se grafican en una parcela; uno para cada ruta coincidente.

La salida principal producida serán tres archivos:

séptimo-paquete-byte-count.dat
séptimo-paquete-byte-count.plt
séptimo-paquete-byte-count.sh

En este punto, los usuarios pueden editar manualmente el archivo .plt para realizar más personalizaciones, o
simplemente ejecútelo a través de gnuplot. Corriendo sh séptimo-paquete-byte-count.sh simplemente corre la trama
a través de gnuplot, como se muestra a continuación.
[imagen] Gnuplot 2-D Creado por séptimo.cc Ejemplo..UNINDENT

Se puede ver que los elementos clave (leyenda, título, ubicación de la leyenda, xlabel, ylabel,
y la ruta de los datos) se colocan en la trama. Dado que hubo dos partidos para el
ruta de configuración proporcionada, se muestran las dos series de datos:

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

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

Ayudante de Gnuplot ConfigurarPlot
El GnuplotHelper's ConfigurePlot () La función se puede utilizar para configurar gráficos.

Tiene el siguiente prototipo:

ConfigurePlot vacío (const std :: string & outputFileNameWithoutExtension,
const std :: cadena y título,
const std :: string & xLegend,
const std :: string & yLegend,
const std :: string & terminalType = ".png");

Tiene los siguientes argumentos:

┌───────────────────────────────┬───────────────── ─────────────────┐
│Argumento │ Descripción │
├───────────────────────────────┼───────────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nombre de los archivos relacionados con gnuplot a │
│ │ escribir sin extensión. │
├───────────────────────────────┼───────────────── ─────────────────┤
│title │ Trace la cadena de título que se utilizará para │
│ │ esta trama. │
├───────────────────────────────┼───────────────── ─────────────────┤
│xLegend │ La leyenda de la x horizontal │
Eje │ │. │
├───────────────────────────────┼───────────────── ─────────────────┤
│yLegend │ La leyenda de la vertical y │
Eje │ │. │
└──────────────────────────────┴───────────────── ─────────────────┘

│terminalType │ Cadena de configuración de tipo de terminal para │
│ │ salida. El terminal predeterminado │
│ │ tipo es "png". │
└──────────────────────────────┴───────────────── ─────────────────┘

El GnuplotHelper's ConfigurePlot () La función configura los parámetros relacionados con la gráfica para este
ayudante de gnuplot para que cree un archivo de datos gnuplot separado por espacios llamado
outputFileNameWithoutExtension + ".dat", un archivo de control gnuplot llamado
outputFileNameWithoutExtension + ".plt", y un script de shell para generar el gnuplot llamado
outputFileNameWithoutExtension + ".sh".

Un ejemplo de cómo utilizar esta función se puede ver en la séptimo.cc código descrito arriba
donde se usó de la siguiente manera:

plotHelper.ConfigurePlot ("recuento de bytes del séptimo paquete",
"Recuento de bytes de paquetes frente a tiempo",
"Tiempo (segundos)",
"Recuento de bytes de paquetes",
"png");

Ayudante de Gnuplot Trazar sonda
El GnuplotHelper's PlotProbe () La función se puede utilizar para trazar los valores generados por las sondas.

Tiene el siguiente prototipo:

vacío PlotProbe (const std :: string & typeId,
const std :: cadena y ruta,
const std :: string & probeTraceSource,
const std :: cadena y título,
enumeración GnuplotAggregator :: KeyLocation keyLocation = GnuplotAggregator :: KEY_INSIDE);

Tiene los siguientes argumentos:

┌─────────────────┬─────────────────────────────── ───┐
│Argumento │ Descripción │
├────────────────┼─────────────────────────────── ───┤
│typeId │ El ID de tipo de la sonda │
│ │ creado por este ayudante. │
├────────────────┼─────────────────────────────── ───┤
│ruta │ Ruta de configuración para acceder a la traza │
│ │ fuente. │
├────────────────┼─────────────────────────────── ───┤
│probeTraceSource │ La fuente de rastreo de la sonda a │
│ │ acceso. │
├────────────────┼─────────────────────────────── ───┤
│title │ El título que se asociará a │
│ │ este conjunto de datos │
├────────────────┼─────────────────────────────── ───┤
│keyLocation │ La ubicación de la clave en el │
│ │ trama. La ubicación predeterminada es │
│ │ adentro. │
└─────────────────┴─────────────────────────────── ───┘

El GnuplotHelper's PlotProbe () La función traza un conjunto de datos generado al conectar el ns-3
rastrear la fuente con una sonda creada por el ayudante, y luego trazar los valores de la
probeTraceSource. El conjunto de datos tendrá el título proporcionado y constará de la
'newValue' en cada marca de tiempo.

Si la ruta de configuración tiene más de una coincidencia en el sistema porque hay un comodín, entonces
se trazará un conjunto de datos para cada coincidencia. Los títulos del conjunto de datos tendrán el sufijo
caracteres coincidentes para cada uno de los comodines en la ruta de configuración, separados por espacios. Para
ejemplo, si el título del conjunto de datos propuesto es la cadena "bytes" y hay dos comodines
en la ruta, los títulos de conjuntos de datos como "bytes-0 0" o "bytes-12 9" serán posibles como
etiquetas para los conjuntos de datos que se trazan.

Un ejemplo de cómo utilizar esta función se puede ver en la séptimo.cc código descrito arriba
donde se usó (con sustitución de variable) de la siguiente manera:

plotHelper.PlotProbe ("ns3 :: Ipv4PacketProbe",
"/ NodeList / * / $ ns3 :: Ipv4L3Protocol / Tx",
"OutputBytes",
"Recuento de bytes de paquetes",
GnuplotAggregator :: KEY_BELOW);

Otro Ejemplos
parcela Ayudante Ejemplo
Un ejemplo un poco más simple que el séptimo.cc se puede encontrar un ejemplo en
src / stats / examples / gnuplot-helper-example.cc. El siguiente gnuplot 2-D se creó utilizando
el ejemplo.
[imagen] Gnuplot 2-D Creado por gnuplot-helper-example.cc Ejemplo..UNINDENT

En este ejemplo, hay un objeto Emisor que incrementa su contador de acuerdo con un
Poisson procesa y luego emite el valor del contador como fuente de rastreo.

Ptr emisor = CreateObject ();
Nombres :: Agregar ("/ Nombres / Emisor", emisor);

Tenga en cuenta que debido a que no hay comodines en la ruta utilizada a continuación, solo se
dibujado en la trama. Este único flujo de datos en el gráfico simplemente se etiqueta "Contador de emisores",
sin sufijos adicionales como uno vería si hubiera comodines en la ruta.

// Crea el asistente de gnuplot.
GnuplotHelper plotHelper;

// Configura la trama.
plotHelper.ConfigurePlot ("gnuplot-helper-example",
"Recuentos de emisores frente a tiempo",
"Tiempo (segundos)",
"Contador de emisores",
"png");

// Trace los valores generados por la sonda. El camino que proporcionamos
// ayuda a eliminar la ambigüedad de la fuente del rastro.
plotHelper.PlotProbe ("ns3 :: Uinteger32Probe",
"/ Nombres / Emisor / Contador",
"Producción",
"Contador de emisores",
GnuplotAggregator :: KEY_INSIDE);

Ayudante de archivo
FileHelper es una clase auxiliar que se utiliza para colocar valores de datos en un archivo. El objetivo general es
para proporcionar a los usuarios la capacidad de crear rápidamente archivos de texto formateados a partir de datos exportados
in ns-3 rastrear fuentes. De forma predeterminada, se realiza una cantidad mínima de transformación de datos;
el objetivo es generar archivos con tan pocas declaraciones de configuración (predeterminadas) como
posible.

Ayudante de archivo Resumen
FileHelper creará 1 o más archivos de texto al final de la simulación.

FileHelper puede crear 4 tipos diferentes de archivos de texto:

· Formateado

· Separado por espacios (predeterminado)

· Separado por comas

· Separado por tabuladores

Los archivos formateados utilizan cadenas de formato de estilo C y la función sprintf () para imprimir su
valores en el archivo que se está escribiendo.

El siguiente archivo de texto con 2 columnas de valores formateados llamados
séptimo-paquete-byte-count-0.txt fue creado usando más código nuevo que se agregó al
mas originales ns-3 Código del ejemplo del tutorial. Solo se muestran las primeras 10 líneas de este archivo
aquí por brevedad.

Tiempo (segundos) = 1.000e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.004e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.004e + 00 Recuento de bytes del paquete = 576
Tiempo (segundos) = 1.009e + 00 Recuento de bytes del paquete = 576
Tiempo (segundos) = 1.009e + 00 Recuento de bytes del paquete = 576
Tiempo (segundos) = 1.015e + 00 Recuento de bytes del paquete = 512
Tiempo (segundos) = 1.017e + 00 Recuento de bytes del paquete = 576
Tiempo (segundos) = 1.017e + 00 Recuento de bytes del paquete = 544
Tiempo (segundos) = 1.025e + 00 Recuento de bytes del paquete = 576
Tiempo (segundos) = 1.025e + 00 Recuento de bytes del paquete = 544

...

El siguiente archivo de texto diferente con 2 columnas de valores formateados llamados
séptimo-paquete-byte-count-1.txt también se creó utilizando el mismo código nuevo que se agregó a
el original ns-3 Código del ejemplo del tutorial. Solo se muestran las primeras 10 líneas de este archivo
aquí por brevedad.

Tiempo (segundos) = 1.002e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.007e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.013e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.020e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.028e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.036e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.045e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.053e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.061e + 00 Recuento de bytes del paquete = 40
Tiempo (segundos) = 1.069e + 00 Recuento de bytes del paquete = 40

...

El nuevo código que se agregó para producir los dos archivos de texto se encuentra a continuación. Más detalles sobre
esta API se tratará en una sección posterior.

Tenga en cuenta que debido a que hubo 2 coincidencias para el comodín en la ruta, 2 archivos de texto separados
fueron creados. El primer archivo de texto, que se llama "séptimo-paquete-byte-recuento-0.txt",
corresponde a la coincidencia de comodines con el "*" reemplazado por "0". El segundo archivo de texto,
que se llama "séptimo-paquete-byte-count-1.txt", corresponde a la coincidencia del comodín con
el "*" reemplazado por "1". Además, tenga en cuenta que la función llamada a WriteProbe () dará un
mensaje de error si no hay coincidencias para una ruta que contiene comodines.

// Crea el asistente de archivos.
Ayudante de archivos Ayudante de archivos;

// Configure el archivo a escribir.
fileHelper.ConfigureFile ("séptimo-paquete-byte-recuento",
FileAggregator :: FORMATTED);

// Establezca las etiquetas para este archivo de salida formateado.
fileHelper.Set2dFormat ("Tiempo (segundos) =% .3e \ tRecuento de bytes del paquete =% .0f");

// Escribe los valores generados por la sonda.
fileHelper.WriteProbe ("ns3 :: Ipv4PacketProbe",
"/ NodeList / * / $ ns3 :: Ipv4L3Protocol / Tx",
"OutputBytes");

Ayudante de archivo Configurar archivo
El FileHelper ConfigureFile () La función se puede utilizar para configurar archivos de texto.

Tiene el siguiente prototipo:

Void ConfigureFile (const std :: string & outputFileNameWithoutExtension,
enumeración FileAggregator :: FileType fileType = FileAggregator :: SPACE_SEPARATED);

Tiene los siguientes argumentos:

┌───────────────────────────────┬───────────────── ─────────────────┐
│Argumento │ Descripción │
├───────────────────────────────┼───────────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nombre del archivo de salida para escribir │
│ │ sin extensión. │
├───────────────────────────────┼───────────────── ─────────────────┤
│fileType │ Tipo de archivo a escribir. El │
│ │ el tipo de archivo predeterminado es el espacio │
│ │ separados. │
└──────────────────────────────┴───────────────── ─────────────────┘

El FileHelper ConfigureFile () La función configura los parámetros relacionados con el archivo de texto para el
ayudante de archivos para que cree un archivo llamado outputFileNameWithoutExtension plus
posible información adicional de coincidencias de comodines más ".txt" con valores impresos como
especificado por fileType. El tipo de archivo predeterminado está separado por espacios.

Un ejemplo de cómo utilizar esta función se puede ver en la séptimo.cc código descrito arriba
donde se usó de la siguiente manera:

fileHelper.ConfigureFile ("séptimo-paquete-byte-recuento",
FileAggregator :: FORMATTED);

Ayudante de archivo Sonda de escritura
El FileHelper WriteProbe () La función se puede utilizar para escribir valores generados por sondas para
archivos de texto.

Tiene el siguiente prototipo:

vacío WriteProbe (const std :: string & typeId,
const std :: cadena y ruta,
const std :: string & probeTraceSource);

Tiene los siguientes argumentos:

┌─────────────────┬─────────────────────────────── ───┐
│Argumento │ Descripción │
├────────────────┼─────────────────────────────── ───┤
│typeId │ El ID de tipo de la sonda │
│ │ creado. │
├────────────────┼─────────────────────────────── ───┤
│ruta │ Ruta de configuración para acceder a la traza │
│ │ fuente. │
├────────────────┼─────────────────────────────── ───┤
│probeTraceSource │ La fuente de rastreo de la sonda a │
│ │ acceso. │
└─────────────────┴─────────────────────────────── ───┘

El FileHelper WriteProbe () La función crea archivos de texto de salida generados al conectar el
fuente de rastreo ns-3 con una sonda creada por el ayudante, y luego escribiendo los valores del
probeTraceSource. Los nombres de los archivos de salida tendrán el texto almacenado en la variable miembro
m_outputFileNameWithoutExtension más ".txt", y consistirá en el 'newValue' en cada
marca de tiempo.

Si la ruta de configuración tiene más de una coincidencia en el sistema porque hay un comodín, entonces
Se creará un archivo de salida para cada partido. Los nombres de los archivos de salida contendrán el
texto en m_outputFileNameWithoutExtension más los caracteres coincidentes para cada uno de los
comodines en la ruta de configuración, separados por guiones, más ".txt". Por ejemplo, si el valor
en m_outputFileNameWithoutExtension es la cadena "packet-byte-count", y hay dos
comodines en la ruta, luego nombres de archivos de salida como "packet-byte-count-0-0.txt" o
"packet-byte-count-12-9.txt" será posible como nombres para los archivos que se crearán.

Un ejemplo de cómo utilizar esta función se puede ver en la séptimo.cc código descrito arriba
donde se usó de la siguiente manera:

fileHelper.WriteProbe ("ns3 :: Ipv4PacketProbe",
"/ NodeList / * / $ ns3 :: Ipv4L3Protocol / Tx",
"OutputBytes");

Otro Ejemplos
Archive Ayudante Ejemplo
Un ejemplo un poco más simple que el séptimo.cc se puede encontrar un ejemplo en
src / stats / examples / file-helper-example.cc. Este ejemplo solo usa FileHelper.

El siguiente archivo de texto con 2 columnas de valores formateados llamados archivo-ayudante-ejemplo.txt
fue creado usando el ejemplo. Aquí solo se muestran las primeras 10 líneas de este archivo para
brevedad.

Tiempo (segundos) = 0.203 Cuenta = 1
Tiempo (segundos) = 0.702 Cuenta = 2
Tiempo (segundos) = 1.404 Cuenta = 3
Tiempo (segundos) = 2.368 Cuenta = 4
Tiempo (segundos) = 3.364 Cuenta = 5
Tiempo (segundos) = 3.579 Cuenta = 6
Tiempo (segundos) = 5.873 Cuenta = 7
Tiempo (segundos) = 6.410 Cuenta = 8
Tiempo (segundos) = 6.472 Cuenta = 9
...

En este ejemplo, hay un objeto Emisor que incrementa su contador de acuerdo con un
Poisson procesa y luego emite el valor del contador como fuente de rastreo.

Ptr emisor = CreateObject ();
Nombres :: Agregar ("/ Nombres / Emisor", emisor);

Tenga en cuenta que debido a que no hay comodines en la ruta utilizada a continuación, solo se
creado. Este único archivo de texto se llama simplemente "file-helper-example.txt", sin extra
sufijos como vería si hubiera comodines en la ruta.

// Crea el asistente de archivos.
Ayudante de archivos Ayudante de archivos;

// Configure el archivo a escribir.
fileHelper.ConfigureFile ("file-helper-example",
FileAggregator :: FORMATTED);

// Establezca las etiquetas para este archivo de salida formateado.
fileHelper.Set2dFormat ("Tiempo (segundos) =% .3e \ tCount =% .0f");

// Escribe los valores generados por la sonda. El camino que nosotros
// proporciona ayuda para eliminar la ambigüedad de la fuente del rastro.
fileHelper.WriteProbe ("ns3 :: Uinteger32Probe",
"/ Nombres / Emisor / Contador",
"Producción");

<b></b><b></b> y Limitaciones
Actualmente, solo estas sondas se han implementado y conectado a GnuplotHelper y
al FileHelper:

· Sonda booleana

· Doble sonda

· Uinteger8Probe

· Uinteger16Probe

· Uinteger32Probe

· Sonda de tiempo

· Sonda de paquetes

· Sonda de paquete de aplicación

· Sonda de paquetes Ipv4

Estas sondas, por lo tanto, son los únicos TypeIds disponibles para usarse en PlotProbe () y
WriteProbe ().

En las siguientes secciones, cubrimos cada uno de los tipos de objetos fundamentales (sonda, colector,
y agregador) con más detalle, y mostrar cómo se pueden conectar entre sí utilizando
API de nivel inferior.

Sondas
Esta sección detalla las funcionalidades proporcionadas por la clase Probe a un ns-3
simulación, y da ejemplos sobre cómo codificarlos en un programa. Esta sección está destinada a
usuarios interesados ​​en desarrollar simulaciones con el ns-3 herramientas y uso de los datos
Collection Framework, del cual la clase Probe es parte, para generar salida de datos con
los resultados de su simulación.

Muestra Resumen
Se supone que un objeto Probe está conectado a una variable de la simulación cuyos valores
a lo largo del experimento son relevantes para el usuario. La sonda registrará lo que fueron
valores asumidos por la variable a lo largo de la simulación y pasar dichos datos a otra
miembro del marco de recopilación de datos. Si bien está fuera del alcance de esta sección
discutir lo que sucede después de que la sonda produce su salida, es suficiente decir que, por
Al final de la simulación, el usuario tendrá información detallada sobre qué valores fueron
almacenados dentro de la variable que se está probando durante la simulación.

Normalmente, una sonda se conecta a un ns-3 fuente de rastreo. De esta manera, siempre que el
la fuente de rastreo exporta un nuevo valor, la sonda consume el valor (y lo exporta en sentido descendente
a otro objeto a través de su propia fuente de seguimiento).

La sonda se puede considerar como una especie de filtro en las fuentes de rastreo. Las principales razones de
posiblemente engancharse a una sonda en lugar de directamente a una fuente de rastreo son los siguientes:

· Las sondas se pueden encender y apagar dinámicamente durante la simulación con llamadas a Permitir()
y Desactivar(). Por ejemplo, la salida de datos puede desactivarse durante la
fase de calentamiento de simulación.

· Las sondas pueden realizar operaciones en los datos para extraer valores de más complicados
estructuras; por ejemplo, generar el valor del tamaño del paquete desde un ns3 :: Packet recibido.

· Las sondas registran un nombre en el espacio de nombres ns3 :: Config (usando Nombres :: Agregar ()) para que otro
los objetos pueden referirse a ellos.

· Las sondas proporcionan un método estático que permite manipular una sonda por su nombre, como
lo que se hace en ns2measure [Cic06]

Stat :: put ("my_metric", ID, muestra);

El equivalente ns-3 del código de medida ns2 anterior es, por ejemplo

DoubleProbe :: SetValueByPath ("/ ruta / a / sonda", muestra);

contenido SEO
Tenga en cuenta que no se puede crear un objeto de clase base Probe porque es una base abstracta
class, es decir, tiene funciones virtuales puras que no han sido implementadas. Un objeto de
tipo DoubleProbe, que es una subclase de la clase Probe, se creará aquí para mostrar
lo que hay que hacer.

Uno declara un DoubleProbe en la memoria dinámica utilizando la clase de puntero inteligente (Ptr ). A
crear un DoubleProbe en memoria dinámica con punteros inteligentes, solo es necesario llamar al
ns-3 Método Crear objeto():

Ptr myprobe = CreateObject ();

La declaración anterior crea DoubleProbes utilizando los valores predeterminados para sus atributos.
Hay cuatro atributos en la clase DoubleProbe; dos en el objeto de clase base
DataCollectionObject y dos en la clase base Probe:

· "Nombre" (DataCollectionObject), un StringValue

· "Habilitado" (DataCollectionObject), un valor booleano

· "Inicio" (sonda), un valor de tiempo

· "Detener" (sonda), un valor de tiempo

Se pueden establecer dichos atributos en la creación del objeto utilizando el siguiente método:

Ptr myprobe = CreateObjectWithAttributes (
"Nombre", StringValue ("myprobe"),
"Habilitado", BooleanValue (falso),
"Inicio", TimeValue (Segundos (100.0)),
"Detener", TimeValue (Segundos (1000.0)));

Start y Stop son variables de tiempo que determinan el intervalo de acción de la sonda. El
La sonda solo generará datos si el tiempo actual de la simulación está dentro de ese
intervalo. El valor de tiempo especial de 0 segundos para la parada deshabilitará este atributo (es decir,
mantenga la sonda encendida durante toda la simulación). Habilitado es una bandera que enciende o enciende la sonda.
off, y debe establecerse en true para que Probe exporte datos. El nombre es el nombre del objeto.
en el marco DCF.

Importador y exportar datos
ns-3 Las fuentes de rastreo están fuertemente tipadas, por lo que los mecanismos para conectar Probes a un rastreo
fuente y para exportar datos pertenecen a sus subclases. Por ejemplo, el valor predeterminado
distribución de ns-3 proporciona una clase DoubleProbe que está diseñada para conectarse a una traza
fuente exportando un valor doble. A continuación, detallaremos el funcionamiento de DoubleProbe, y
luego discuta cómo el usuario puede definir otras clases de Probe.

Sonda doble Resumen
El DoubleProbe se conecta a un doble valor ns-3 fuente de rastreo, y exporta un
diferente de doble valor ns-3 fuente de rastreo.

El siguiente código, extraído de src / stats / examples / double-probe-example.cc, muestra el básico
operaciones de conectar el DoubleProbe a una simulación, donde está probando un contador
exportado por un objeto emisor (clase Emitter).

Ptr emisor = CreateObject ();
Nombres :: Agregar ("/ Nombres / Emisor", emisor);
...

Ptr probe1 = CreateObject ();

// Conecta la sonda al Contador del emisor
bool conectado = sonda1-> ConnectByObject ("Contador", emisor);

El siguiente código está probando el mismo contador exportado por el mismo objeto emisor. Esta
DoubleProbe, sin embargo, está utilizando una ruta en el espacio de nombres de configuración para hacer
conexión. Tenga en cuenta que el emisor se registró en el espacio de nombres de configuración después
fue creado; de lo contrario, ConnectByPath no funcionaría.

Ptr probe2 = CreateObject ();

// Tenga en cuenta que aquí no se comprueba ningún valor de retorno
probe2-> ConnectByPath ("/ Nombres / Emisor / Contador");

El siguiente DoubleProbe que se muestra a continuación tendrá su valor establecido usando su ruta en
el espacio de nombres de la configuración. Tenga en cuenta que esta vez DoubleProbe se registró en el
espacio de nombres de configuración después de su creación.

Ptr probe3 = CreateObject ();
probe3-> SetName ("StaticallyAccessedProbe");

// Debemos agregarlo a la base de datos de configuración
Nombres :: Agregar ("/ Nombres / Sondas", probe3-> GetName (), probe3);

La función Count () del emisor ahora puede establecer el valor de este DoubleProbe como
manera:

vacío
Emisor :: Recuento (vacío)
{
...
m_contador + = 1.0;
DoubleProbe :: SetValueByPath ("/ Nombres / StaticallyAccessedProbe", m_counter);
...
}

El ejemplo anterior muestra cómo el código que llama a Probe no tiene que tener un código explícito.
referencia a la sonda, pero puede dirigir la configuración del valor a través del espacio de nombres Config.
Esto es similar en funcionalidad a la Estadísticas :: Poner método introducido por ns2measure paper
[Cic06], y permite a los usuarios insertar temporalmente declaraciones Probe como Printf declaraciones
dentro de lo existente ns-3 modelos. Tenga en cuenta que para poder utilizar DoubleProbe en este
ejemplo como este, eran necesarias 2 cosas:

1. el archivo de encabezado del módulo de estadísticas se incluyó en el archivo .cc de ejemplo

2. el ejemplo se hizo dependiente del módulo de estadísticas en su archivo wscript.

Es necesario hacer cosas análogas para agregar otras sondas en otros lugares del ns-3
base de código.

Los valores para DoubleProbe también se pueden configurar usando la función DoubleProbe :: SetValue (),
mientras que los valores para DoubleProbe se pueden obtener usando la función
DoubleProbe :: GetValue ().

DoubleProbe exporta valores dobles en su origen de seguimiento "Salida"; un objeto corriente abajo
puede conectar un receptor de seguimiento (NotifyViaProbe) a esto de la siguiente manera:

conectado = sonda1-> TraceConnect ("Salida", sonda1-> GetName (), MakeCallback (& ​​NotifyViaProbe));

Otro sondas
Además de DoubleProbe, también están disponibles las siguientes sondas:

· Uinteger8Probe se conecta a un ns-3 fuente de seguimiento exportando un archivo uint8_t.

· Uinteger16Probe se conecta a un ns-3 fuente de seguimiento exportando un archivo uint16_t.

· Uinteger32Probe se conecta a un ns-3 fuente de seguimiento exportando un archivo uint32_t.

· PacketProbe se conecta a un ns-3 fuente de rastreo exportando un paquete.

· ApplicationPacketProbe se conecta a un ns-3 rastrear la fuente exportando un paquete y un socket
dirección.

· Ipv4PacketProbe se conecta a un ns-3 fuente de seguimiento exportando un paquete, un objeto IPv4 y
una interfaz.

Creamos new Muestra tipos
Para crear un nuevo tipo de sonda, debe realizar los siguientes pasos:

· Asegúrese de que su nueva clase Probe se derive de la clase base Probe.

· Asegúrese de que las funciones virtuales puras que su nueva clase Probe hereda de la
Se implementan las clases base de la sonda.

· Busque una clase Probe existente que utilice una fuente de seguimiento que sea más parecida en tipo a la
tipo de fuente de rastreo que utilizará su sonda.

· Copie el archivo de encabezado (.h) y el archivo de implementación (.cc) de la clase Probe existente en dos
nuevos archivos con nombres que coincidan con su nueva sonda.

· Reemplazar los tipos, argumentos y variables en los archivos copiados con el apropiado
escriba para su sonda.

· Realice las modificaciones necesarias para que el código se compile y se comporte como lo haría
gusta.

Ejemplos
Aquí se comentarán en detalle dos ejemplos:

· Ejemplo de sonda doble

· Ejemplo de diagrama de paquetes IPv4

Doble Muestra Ejemplo
El ejemplo de la sonda doble se ha comentado anteriormente. El programa de ejemplo se puede encontrar
in src / stats / examples / double-probe-example.cc. Para resumir lo que ocurre en este programa,
hay un emisor que exporta un contador que se incrementa según un proceso de Poisson.
En particular, se muestran dos formas de emitir datos:

1. a través de una variable rastreada conectada a una sonda:

TracedValue m_counter; // normalmente este sería de tipo entero

2. a través de un contador cuyo valor se contabiliza en una segunda sonda, referenciada por su nombre en
el sistema de configuración:

vacío
Emisor :: Recuento (vacío)
{
NS_LOG_FUNCTION (esto);
NS_LOG_DEBUG ("Contando en" << Simulator :: Now () .GetSeconds ());
m_contador + = 1.0;
DoubleProbe :: SetValueByPath ("/ Nombres / StaticallyAccessedProbe", m_counter);
Simulator :: Schedule (Seconds (m_var-> GetValue ()), & Emitter :: Count, this);
}

Echemos un vistazo a la sonda con más atención. Las sondas pueden recibir sus valores en múltiples
formas:

1. mediante el acceso de la sonda a la fuente de seguimiento directamente y la conexión de un receptor de seguimiento

2. por la sonda accediendo a la fuente de seguimiento a través del espacio de nombres de configuración y conectando un
rastro del sumidero hasta él

3. por el código de llamada que llama explícitamente a la sonda Valor ajustado() Método

4. por el código de llamada que llama explícitamente EstablecerValorPorRuta
("/ ruta / a / Config / espacio de nombres", ...)

Se espera que las dos primeras técnicas sean las más comunes. También en el ejemplo, el
Se muestra el enganche de una función de devolución de llamada normal, como se hace normalmente en ns-3. Esto
La función de devolución de llamada no está asociada con un objeto Probe. A este caso lo llamaremos 0) a continuación.

// Esta es una función para probar el enganche de una función sin formato a la fuente de seguimiento
vacío
NotifyViaTraceSource (std :: contexto de cadena, doble oldVal, doble newVal)
{
NS_LOG_DEBUG ("contexto:" << contexto << "antiguo" << antiguoVal << "nuevo" << nuevoValor);
}

Primero, el emisor debe configurarse:

Ptr emisor = CreateObject ();
Nombres :: Agregar ("/ Nombres / Emisor", emisor);

// El objeto Emitter no está asociado con un nodo ns-3, por lo que
// no se iniciará automáticamente, por lo que debemos hacerlo nosotros mismos
Simulador :: Programación (Segundos (0.0), & Emisor :: Inicio, emisor);

Los distintos DoubleProbes interactúan con el emisor en el ejemplo, como se muestra a continuación.

Caso 0):

// A continuación se muestra la funcionalidad típica sin sonda
// (conecta una función de sumidero a una fuente de rastreo)
//
conectado = emisor-> TraceConnect ("Contador", "contexto de muestra", MakeCallback (& ​​NotifyViaTraceSource));
NS_ASSERT_MSG (conectado, "Fuente de seguimiento no conectada");

caso 1):

//
// Probe1 se conectará directamente al objeto de origen de seguimiento del emisor
//

// probe1 se conectará a la fuente de seguimiento del emisor
Ptr probe1 = CreateObject ();
// el nombre de la sonda puede servir como contexto en el rastreo
probe1-> SetName ("ObjectProbe");

// Conecta la sonda al Contador del emisor
conectado = sonda1-> ConnectByObject ("Contador", emisor);
NS_ASSERT_MSG (conectado, "Fuente de rastreo no conectada a probe1");

caso 2):

//
// Probe2 se conectará al objeto de origen de seguimiento del emisor mediante
// acceder a él por el nombre de la ruta en la base de datos de configuración
//

// Crea otra sonda similar; esto se conectará a través de una ruta de configuración
Ptr probe2 = CreateObject ();
probe2-> SetName ("PathProbe");

// Tenga en cuenta que aquí no se comprueba ningún valor de retorno
probe2-> ConnectByPath ("/ Nombres / Emisor / Contador");

caso 4) (el caso 3 no se muestra en este ejemplo):

//
// Probe3 será llamado por el emisor directamente a través del
// método estático SetValueByPath ().
//
Ptr probe3 = CreateObject ();
probe3-> SetName ("StaticallyAccessedProbe");
// Debemos agregarlo a la base de datos de configuración
Nombres :: Agregar ("/ Nombres / Sondas", probe3-> GetName (), probe3);

Y finalmente, el ejemplo muestra cómo se pueden conectar las sondas para generar resultados:

// La propia sonda debería generar una salida. El contexto que brindamos
// a esta sonda (en este caso, el nombre de la sonda) ayudará a eliminar la ambigüedad
// la fuente del rastro
conectado = sonda3-> TraceConnect ("Salida",
"/ Nombres / Sondas / Sonda de acceso estático / Salida",
MakeCallback (& ​​NotifyViaProbe));
NS_ASSERT_MSG (conectado, "Fuente de seguimiento no ... conectada a la salida probe3");

La siguiente devolución de llamada está conectada a Probe en este ejemplo con fines ilustrativos;
normalmente, la sonda se conectaría a un objeto recopilador.

// Esta es una función para probar y conectarla a la salida de la sonda
vacío
NotifyViaProbe (std :: contexto de cadena, doble oldVal, doble newVal)
{
NS_LOG_DEBUG ("contexto:" << contexto << "antiguo" << antiguoVal << "nuevo" << nuevoValor);
}

IPv4 Paquete Parcela Ejemplo
El ejemplo del diagrama de paquetes IPv4 se basa en el quinto ejemplo .cc del ns-3 Tutorial. Eso
puede encontrarse en src / stats / examples / ipv4-packet-plot-example.cc.

nodo 0 nodo 1
+ ---------------- + + ---------------- +
| TCP ns-3 | | TCP ns-3 |
+ ---------------- + + ---------------- +
| 10.1.1.1 | | 10.1.1.2 |
+ ---------------- + + ---------------- +
| punto a punto | | punto a punto |
+ ---------------- + + ---------------- +
| |
+ --------------------- +

Solo veremos la sonda, ya que ilustra que las sondas también pueden descomprimir valores de
estructuras (en este caso, paquetes) e informan esos valores como salidas de origen de seguimiento, en lugar de
que simplemente pasar por el mismo tipo de datos.

Hay otros aspectos de este ejemplo que se explicarán más adelante en la documentación.
Los dos tipos de datos que se exportan son el paquete en sí (Salida) y un recuento de los
número de bytes en el paquete (bytes de salida).

ID de tipo
Ipv4PacketProbe :: GetTypeId ()
{
estático TypeId tid = TypeId ("ns3 :: Ipv4PacketProbe")
.SetParent ()
.AddConstructor ()
.AddTraceSource ("Salida",
"El paquete más su objeto IPv4 y la interfaz que sirven como salida para esta sonda",
MakeTraceSourceAccessor (& Ipv4PacketProbe :: m_output))
.AddTraceSource ("OutputBytes",
"El número de bytes en el paquete",
MakeTraceSourceAccessor (& Ipv4PacketProbe :: m_outputBytes))
;
volver tid;
}

Cuando el receptor de seguimiento de la sonda recibe un paquete, si la sonda está habilitada, generará
el paquete en su Salida fuente de seguimiento, pero también generará el número de bytes en el
bytes de salida fuente de rastreo.

vacío
Ipv4PacketProbe :: TraceSink (Ptr paquete, Ptr ipv4, interfaz uint4_t)
{
NS_LOG_FUNCTION (esta interfaz << paquete << ipv4 <<);
si (IsEnabled ())
{
m_packet = paquete;
m_ipv4 = ipv4;
m_interface = interfaz;
m_output (paquete, ipv4, interfaz);

uint32_t packetSizeNew = paquete-> GetSize ();
m_outputBytes (m_packetSizeOld, paqueteSizeNew);
m_packetSizeOld = paqueteTamañoNuevo;
}
}

Referencias
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, "Un marco integrado para
Habilitación de la recopilación de datos y el análisis estadístico efectivos con ns2, Taller sobre
ns-2 (WNS2), Pisa, Italia, octubre de 2006.

Colectores
Esta sección es un marcador de posición para detallar las funcionalidades proporcionadas por el recopilador.
clase a un ns-3 simulación, y da ejemplos sobre cómo codificarlos en un programa.

Nota: A partir de ns-3.18, los recopiladores aún están en desarrollo y aún no se proporcionan como parte
del marco.

Agregadores
Esta sección detalla las funcionalidades proporcionadas por la clase Aggregator a un ns-3
simulación. Esta sección está dirigida a usuarios interesados ​​en desarrollar simulaciones con el
ns-3 herramientas y utilizando el marco de recopilación de datos, de los cuales la clase Aggregator es una
parte, para generar salida de datos con los resultados de su simulación.

Aggregator Resumen
Se supone que un objeto Aggregator está conectado a una o más fuentes de seguimiento para
recibir entrada. Los agregadores son el punto final de los datos recopilados por la red de
Sondas y colectores durante la simulación. Es el trabajo del agregador tomar estos
valores y transformarlos en su formato de salida final, como archivos de texto sin formato,
archivos de hoja de cálculo, gráficos o bases de datos.

Normalmente, un agregador está conectado a uno o más recopiladores. De esta manera, siempre que
Las fuentes de seguimiento de los recopiladores exportan nuevos valores, el agregador puede procesar el valor para
que se puede utilizar en el formato de salida final donde los valores de datos residirán después de la
simulación.

Tenga en cuenta lo siguiente acerca de los agregadores:

· Los agregadores pueden activarse y desactivarse dinámicamente durante la simulación con llamadas a
Permitir() y Desactivar(). Por ejemplo, la agregación de datos puede desactivarse durante
la fase de calentamiento de la simulación, lo que significa que esos valores no se incluirán en la
medio de salida.

· Los agregadores reciben datos de los recopiladores a través de devoluciones de llamada. Cuando un coleccionista está asociado
a un agregador, se realiza una llamada a TraceConnect para establecer el seguimiento del agregador
método de sumidero como devolución de llamada.

Hasta la fecha, se han implementado dos agregadores:

· Agregador Gnuplot

· Agregador de archivos

Agregador de Gnuplot
GnuplotAggregator produce archivos de salida que se utilizan para hacer gnuplots.

El GnuplotAggregator creará 3 archivos diferentes al final de la simulación:

· Un archivo de datos gnuplot separado por espacios

· Un archivo de control de gnuplot

· Un script de shell para generar el gnuplot

contenido SEO
Aquí se creará un objeto de tipo GnuplotAggregator para mostrar lo que se debe hacer.

Uno declara un GnuplotAggregator en memoria dinámica usando la clase de puntero inteligente
(Ptr ). Para crear un GnuplotAggregator en memoria dinámica con punteros inteligentes, uno solo
necesita llamar al ns-3 Método Crear objeto(). El siguiente código de
src / stats / examples / gnuplot-aggregator-example.cc muestra cómo hacer esto:

string fileNameWithoutExtension = "gnuplot-aggregator";

// Crea un agregador.
Ptr agregador =
Crear objeto (fileNameWithoutExtension);

El primer argumento del constructor, fileNameWithoutExtension, es el nombre del
gnuplot para escribir archivos sin extensión. Este GnuplotAggregator creará un
archivo de datos gnuplot separado por espacios llamado "gnuplot-aggregator.dat", un archivo de control gnuplot
llamado "gnuplot-aggregator.plt", y un script de shell para generar el gnuplot llamado +
"gnuplot-aggregator.sh".

El gnuplot que se crea puede tener su clave en 4 ubicaciones diferentes:

· No hay llave

· Clave dentro de la trama (por defecto)

· Clave sobre la trama

· Clave debajo de la trama

Los siguientes valores de enumeración de ubicación de clave de gnuplot pueden especificar la posición de la clave:

enumerar ubicación clave {
NO HAY LLAVE,
CLAVE_INTERIOR,
CLAVE_ARRIBA,
CLAVE_ABAJO
};

Si se deseaba tener la clave debajo en lugar de la posición predeterminada del interior, entonces
podría hacer lo siguiente.

agregador-> SetKeyLocation (GnuplotAggregator :: KEY_BELOW);

Ejemplos
Un ejemplo se discutirá en detalle aquí:

· Ejemplo de agregador de Gnuplot

parcela Aggregator Ejemplo
Un ejemplo que ejercita el GnuplotAggregator se puede encontrar en
src / stats / examples / gnuplot-aggregator-example.cc.

El siguiente gnuplot 2-D se creó utilizando el ejemplo.
[imagen] Gnuplot 2-D Creado por gnuplot-aggregator-example.cc Ejemplo..UNINDENT

Este código del ejemplo muestra cómo construir el GnuplotAggregator como se discutió
anterior.

vacío Create2dPlot ()
{
Using namespace std;

string fileNameWithoutExtension = "gnuplot-aggregator";
string plotTitle = "Gráfica del agregador de Gnuplot";
string plotXAxisHeading = "Tiempo (segundos)";
string plotYAxisHeading = "Valores dobles";
string plotDatasetLabel = "Valores de datos";
string datasetContext = "Conjunto de datos / Contexto / Cadena";

// Crea un agregador.
Ptr agregador =
Crear objeto (fileNameWithoutExtension);

Se establecen varios atributos de GnuplotAggregator, incluido el conjunto de datos 2-D que se
trazado.

// Establecer las propiedades del agregador.
agregador-> SetTerminal ("png");
agregador-> SetTitle (plotTitle);
agregador-> SetLegend (plotXAxisHeading, plotYAxisHeading);

// Agrega un conjunto de datos al agregador.
agregador-> Add2dDataset (datasetContext, plotDatasetLabel);

// el agregador debe estar encendido
agregador-> Habilitar ();

A continuación, se calculan los valores 2-D y cada uno se escribe individualmente en el
GnuplotAggregator usando el Write2d () función.

doble tiempo;
valor doble;

// Crea el conjunto de datos 2-D.
para (tiempo = -5.0; tiempo <= +5.0; tiempo + = 1.0)
{
// Calcula la curva 2-D
//
/ / 2
// valor = tiempo.
//
valor = tiempo * tiempo;

// Agrega este punto a la trama.
agregador-> Write2d (datasetContext, tiempo, valor);
}

// Deshabilitar el registro de datos para el agregador.
agregador-> Desactivar ();
}

Agregador de archivos
FileAggregator envía los valores que recibe a un archivo.

FileAggregator puede crear 4 tipos diferentes de archivos:

· Formateado

· Separado por espacios (predeterminado)

· Separado por comas

· Separado por tabuladores

Los archivos formateados utilizan cadenas de formato de estilo C y la función sprintf () para imprimir su
valores en el archivo que se está escribiendo.

contenido SEO
Aquí se creará un objeto de tipo FileAggregator para mostrar lo que se debe hacer.

Uno declara un FileAggregator en memoria dinámica usando la clase de puntero inteligente (Ptr ).
Para crear un FileAggregator en memoria dinámica con punteros inteligentes, solo necesita llamar
los ns-3 método CreateObject. El siguiente código de
src / stats / examples / file-aggregator-example.cc muestra cómo hacer esto:

string fileName = "file-aggregator-formatted-values.txt";

// Cree un agregador que tendrá valores formateados.
Ptr agregador =
Crear objeto (fileName, FileAggregator :: FORMATTED);

El primer argumento del constructor, nombre de archivo, es el nombre del archivo a escribir; el
El segundo argumento, fileType, es el tipo de archivo que se va a escribir. Este FileAggregator creará un
archivo llamado "file-aggregator-formatted-values.txt" con sus valores impresos según lo especificado por
fileType, es decir, formateado en este caso.

Se permiten los siguientes valores de enumeración de tipo de archivo:

enumerar tipo de archivo {
FORMATEADO,
ESPACIO_SEPARADO,
SEPARADO POR COMAS,
TAB_SEPARADO
};

Ejemplos
Un ejemplo se discutirá en detalle aquí:

· Ejemplo de agregador de archivos

Archive Aggregator Ejemplo
Un ejemplo que ejercita el FileAggregator se puede encontrar en
src / stats / examples / file-aggregator-example.cc.

El siguiente archivo de texto con 2 columnas de valores separados por comas fue creado usando el
ejemplo.

-5,25
-4,16
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9
4,16
5,25

Este código del ejemplo muestra cómo construir FileAggregator como se discutió
anterior.

vacío CreateCommaSeparatedFile ()
{
Using namespace std;

string fileName = "agregador-de-archivos-separado por comas.txt";
string datasetContext = "Conjunto de datos / Contexto / Cadena";

// Crea un agregador.
Ptr agregador =
Crear objeto (nombre de archivo, agregador de archivos :: COMMA_SEPARATED);

Se establecen los atributos de FileAggregator.

// el agregador debe estar encendido
agregador-> Habilitar ();

A continuación, se calculan los valores 2-D y cada uno se escribe individualmente en el
FileAggregator usando el Write2d () función.

doble tiempo;
valor doble;

// Crea el conjunto de datos 2-D.
para (tiempo = -5.0; tiempo <= +5.0; tiempo + = 1.0)
{
// Calcula la curva 2-D
//
/ / 2
// valor = tiempo.
//
valor = tiempo * tiempo;

// Agrega este punto a la trama.
agregador-> Write2d (datasetContext, tiempo, valor);
}

// Deshabilitar el registro de datos para el agregador.
agregador-> Desactivar ();
}

El siguiente archivo de texto con 2 columnas de valores formateados también se creó utilizando el
ejemplo.

Tiempo = -5.000e + 00 Valor = 25
Tiempo = -4.000e + 00 Valor = 16
Tiempo = -3.000e + 00 Valor = 9
Tiempo = -2.000e + 00 Valor = 4
Tiempo = -1.000e + 00 Valor = 1
Tiempo = 0.000e + 00 Valor = 0
Tiempo = 1.000e + 00 Valor = 1
Tiempo = 2.000e + 00 Valor = 4
Tiempo = 3.000e + 00 Valor = 9
Tiempo = 4.000e + 00 Valor = 16
Tiempo = 5.000e + 00 Valor = 25

Este código del ejemplo muestra cómo construir FileAggregator como se discutió
anterior.

vacío CreateFormattedFile ()
{
Using namespace std;

string fileName = "file-aggregator-formatted-values.txt";
string datasetContext = "Conjunto de datos / Contexto / Cadena";

// Cree un agregador que tendrá valores formateados.
Ptr agregador =
Crear objeto (fileName, FileAggregator :: FORMATTED);

Se establecen los atributos de FileAggregator, incluida la cadena de formato de estilo C que se utilizará.

// Establece el formato de los valores.
agregador-> Set2dFormat ("Tiempo =% .3e \ tValue =% .0f");

// el agregador debe estar encendido
agregador-> Habilitar ();

A continuación, se calculan los valores 2-D y cada uno se escribe individualmente en el
FileAggregator usando el Write2d () función.

doble tiempo;
valor doble;

// Crea el conjunto de datos 2-D.
para (tiempo = -5.0; tiempo <= +5.0; tiempo + = 1.0)
{
// Calcula la curva 2-D
//
/ / 2
// valor = tiempo.
//
valor = tiempo * tiempo;

// Agrega este punto a la trama.
agregador-> Write2d (datasetContext, tiempo, valor);
}

// Deshabilitar el registro de datos para el agregador.
agregador-> Desactivar ();
}

Adaptadores
Esta sección detalla las funcionalidades proporcionadas por la clase Adapter a un ns-3
simulación. Esta sección está dirigida a usuarios interesados ​​en desarrollar simulaciones con el
ns-3 herramientas y utilizando el marco de recopilación de datos, del cual la clase Adaptador es parte,
para generar salida de datos con los resultados de su simulación.

Nota: el término 'adaptador' también puede escribirse 'adaptador'; elegimos la ortografía alineada
con el estándar C ++.

Adaptador Resumen
Se utiliza un adaptador para realizar conexiones entre diferentes tipos de objetos DCF.

Hasta la fecha, se ha implementado un adaptador:

· Adaptador TimeSeries

Hora SeriesTTTT Adaptador
TimeSeriesAdaptor permite que las sondas se conecten directamente a los agregadores sin necesidad de
Coleccionista en el medio.

Ambos ayudantes DCF implementados utilizan TimeSeriesAdaptors para tomar
valores de diferentes tipos y la salida de la hora actual más el valor con ambos convertidos
a dobles.

El papel de la clase TimeSeriesAdaptor es el de un adaptador, que toma valores sin procesar
sondear datos de diferentes tipos y generar una tupla de dos valores dobles. El primero es un
marca de tiempo, que puede establecerse en diferentes resoluciones (por ejemplo, segundos, milisegundos, etc.) en
el futuro, pero que actualmente está codificado en Segundos. El segundo es la conversión de un
valor no doble a un valor doble (posiblemente con pérdida de precisión).

Alcance / Limitaciones
Esta sección analiza el alcance y las limitaciones del marco de recopilación de datos.

Actualmente, solo estas sondas se han implementado en DCF:

· Sonda booleana

· Doble sonda

· Uinteger8Probe

· Uinteger16Probe

· Uinteger32Probe

· Sonda de tiempo

· Sonda de paquetes

· Sonda de paquete de aplicación

· Sonda de paquetes Ipv4

Actualmente, no hay recopiladores disponibles en el DCF, aunque hay un BasicStatsCollector bajo
el desarrollo sostenible.

Actualmente, solo estos agregadores se han implementado en DCF:

· Agregador Gnuplot

· Agregador de archivos

Actualmente, solo este Adaptador se ha implementado en DCF:

Adaptador de serie temporal.

Futuro Trabaja
En esta sección se analiza el trabajo futuro que se realizará en el marco de recopilación de datos.

Aquí hay algunas cosas que aún deben hacerse:

· Conecta más fuentes de rastreo en ns-3 código para obtener más valores del simulador.

· Implementar más tipos de sondas de las que existen actualmente.

· Implemente más que el único recopilador 2-D actual, BasicStatsCollector.

· Implementar más agregadores.

· Implementar algo más que adaptadores.

Estadístico Marco conceptual
Este capítulo describe el trabajo sobre la recopilación de datos de simulación y el marco estadístico para
ns-3.

El código fuente del marco estadístico vive en el directorio src / stats.

Goals
Los objetivos principales de este esfuerzo son los siguientes:

· Proporcionar funcionalidad para registrar, calcular y presentar datos y estadísticas para su análisis
de simulaciones de red.

· Impulse el rendimiento de la simulación al reducir la necesidad de generar extensos registros de seguimiento en
orden de recopilar datos.

· Habilitar el control de la simulación a través de estadísticas en línea, por ejemplo, finalizar simulaciones o
repitiendo ensayos.

Los objetivos secundarios derivados y otras características objetivo incluyen lo siguiente:

· Integración con el sistema de rastreo ns-3 existente como marco básico de instrumentación
del motor de simulación interno, por ejemplo, pilas de red, dispositivos de red y canales.

· Permitir a los usuarios utilizar el marco de estadísticas sin necesidad de utilizar el seguimiento
.

· Ayudar a los usuarios a crear, agregar y analizar datos en múltiples ensayos.

· Compatibilidad con la instrumentación creada por el usuario, por ejemplo, de eventos específicos de la aplicación y
medidas.

· Baja sobrecarga de memoria y CPU cuando el paquete no está en uso.

· Aprovechar al máximo las herramientas de análisis y resultados existentes. El marco puede
proporcionar algunas estadísticas básicas, pero la atención se centra en recopilar datos y hacerlos
accesible para la manipulación en herramientas establecidas.

· El soporte eventual para la distribución de replicaciones independientes es importante pero no está incluido
en la primera ronda de características.

Resumen
El marco de estadísticas incluye las siguientes características:

· El marco central y dos recopiladores de datos básicos: un contador y un mínimo/máximo/promedio/total
observador.

· Extensiones de aquellas para trabajar fácilmente con tiempos y paquetes.

· Salida de texto sin formato con formato para OMNet ++.

· Salida de base de datos usando SQLite, un motor SQL autónomo, ligero y de alto rendimiento.

· Metadatos obligatorios y abiertos para describir y trabajar con ejecuciones.

· Un ejemplo basado en el experimento teórico de examinar las propiedades de NS-3
rendimiento WiFi ad hoc predeterminado. Incorpora lo siguiente:

· Construye una red WiFi ad hoc de dos nodos, con los nodos a una distancia parametrizada
aparte.

· Aplicaciones de fuente y sumidero de tráfico UDP con un comportamiento ligeramente diferente y
ganchos de medición que las clases de stock.

· Recopilación de datos del núcleo NS-3 a través de señales de rastreo existentes, en particular datos sobre
tramas transmitidas y recibidas por los objetos WiFi MAC.

· Instrumentación de aplicaciones personalizadas mediante la conexión de nuevas señales de rastreo a la estadística
marco, así como a través de actualizaciones directas. Se registra información sobre el total de paquetes.
enviados y recibidos, bytes transmitidos y retardo de extremo a extremo.

· Un ejemplo del uso de etiquetas de paquetes para rastrear el retraso de extremo a extremo.

· Una secuencia de comandos de control simple que ejecuta una serie de pruebas del experimento en diferentes
distancias y consulta la base de datos resultante para producir un gráfico usando GNUPlot.

Que hacer
Los elementos de alta prioridad incluyen:

· Inclusión de código de estadísticas en línea, por ejemplo, para intervalos de confianza eficientes en memoria.

· Disposiciones en los colectores de datos para la finalización de las ejecuciones, es decir, cuando un umbral o
se cumple la confianza.

· Recolectores de datos para registrar muestras a lo largo del tiempo y enviarlos a varios formatos.

· Demostrar escribir pegamento de eventos cíclicos simples para encuestar regularmente algún valor.

Cada uno de ellos debería resultar sencillo de incorporar en el marco actual.

Un nuevo enfoque
El marco se basa en los siguientes principios básicos:

· Una prueba de experimento es conducida por una instancia de un programa de simulación, ya sea en
paralelo o en serie.

· Un script de control ejecuta instancias de la simulación, variando los parámetros según sea necesario.

· Los datos se recopilan y almacenan para trazar y analizar utilizando scripts externos y
herramientas existentes.

· Las medidas dentro del núcleo ns-3 se toman conectando el marco stat a los existentes
trazar señales.

· Se pueden usar señales de seguimiento o manipulación directa del marco para instrumentar
código de simulación.

Esos componentes básicos del marco y sus interacciones se describen en el
siguiente figura. [imagen]

Ejemplo
Esta sección repasa el proceso de construcción de un experimento en el marco y
producir datos para el análisis (gráficos) a partir de él, demostrando la estructura y la API a lo largo
la manera.

Pregunta
''¿Cuál es el rendimiento (simulado) de WiFi NetDevices de ns-3 (utilizando el valor predeterminado
ajustes)? ¿A qué distancia pueden estar los nodos inalámbricos en una simulación antes de que no puedan
comunicarse de manera confiable?''

· Hipótesis: Con base en el conocimiento del desempeño de la vida real, los nodos deben comunicarse
razonablemente bien a por lo menos 100 m de distancia. La comunicación más allá de 200 m no debería ser
factible.

Aunque no es una pregunta muy común en contextos de simulación, esta es una propiedad importante
de los cuales los desarrolladores de simulación deben tener una comprensión básica. También es común
estudio realizado en hardware vivo.

Simulación Programa
Lo primero que se debe hacer al implementar este experimento es desarrollar la simulación.
programa. El código para este ejemplo se puede encontrar en ejemplos/estadísticas/wifi-ejemplo-sim.cc.
Realiza los siguientes pasos principales.

· Declarar parámetros y analizar la línea de comando usando ns3::Línea de comandos.

doble distancia = 50.0;
formato de cadena ("OMNet++");
experimento de cuerdas ("wifi-distancia-prueba");
estrategia de cadenas ("wifi-default");
cadena ID de ejecución;

Línea de comando cmd;
cmd.AddValue("distancia", "Distancia entre nodos para colocar (en metros)", distancia);
cmd.AddValue("formato", "Formato a utilizar para salida de datos.", formato);
cmd.AddValue("experimento", "Identificador del experimento.", experimento);
cmd.AddValue("estrategia", "Identificador de estrategia.", estrategia);
cmd.AddValue("ejecutar", "Identificador para ejecutar.", runID);
cmd. Parse (argc, argv);

· Creación de nodos y pilas de red usando ns3::Contenedor de nodo, ns3::WiFiAyudantey
ns3::InternetStackHelper.

nodos NodeContainer;
nodos.Crear(2);

wifi ayudante wifi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(nodos);

InternetStackHelperinternet;
internet.Instalar(nodos);
Ipv4AddressHelper direcciones IP;
DireccionesIP.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(nodeDevices);

· Posicionamiento de los nudos mediante ns3::Ayudante de movilidad. Por defecto los nodos tienen estática
movilidad y no se moverá, pero debe colocarse a la distancia dada aparte. Existen
varias formas de hacer esto; se hace aquí usando ns3::ListPositionAsignador, que dibuja
posiciones de una lista dada.

MobilityHelper movilidad;
ptr positionAlloc =
Crear objeto ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, distancia, 0.0));
movilidad.SetPositionAllocator(positionAlloc);
movilidad.Instalar(nodos);

· Instalación de un generador de tráfico y un sumidero de tráfico. La acción Aplicaciones podría ser
utilizado, pero el ejemplo incluye objetos personalizados en src/prueba/prueba02-aplicaciones.(cc|h). Estas
tienen un comportamiento simple, generando un número dado de paquetes espaciados en un intervalo dado.
Como solo hay uno de cada uno, se instalan manualmente; para un conjunto más grande el
ns3::Ayudante de aplicaciones podría usarse la clase. el comentado Configuración::Establecer cambios de línea
el destino de los paquetes, configurado para transmitir de forma predeterminada en este ejemplo. Tenga en cuenta que
en general, WiFi puede tener un rendimiento diferente para tramas de transmisión y unidifusión debido a
diferentes políticas de control de velocidad y retransmisión MAC.

ptr appSource = Lista de nodos::ObtenerNodo(0);
ptr remitente = CreateObject ();
appSource->AddApplication(remitente);
remitente->Inicio(Segundos(1));

ptr appSink = lista de nodos::ObtenerNodo(1);
ptr receptor = CreateObject ();
appSink->AddApplication(receptor);
receptor->Inicio(Segundos(0));

// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destino",
// ValorDirecciónIpv4("192.168.0.2"));

· Configuración de los datos y estadísticas a recopilar. El paradigma básico es que un
ns3::Recopilador de datos se crea un objeto para contener información sobre esta ejecución en particular, para
que los observadores y las calculadoras están conectados para generar datos. En tono rimbombante,
ejecutar información incluye etiquetas para el "experimento", "estrategia", "entrada" y
''correr''. Estos se utilizan para identificar y agrupar fácilmente los datos de múltiples ensayos más tarde.

· El experimento es el estudio del que forma parte este ensayo. Aquí está en WiFi
rendimiento y distancia.

· La estrategia es el código o los parámetros que se examinan en este juicio. En este ejemplo
está arreglado, pero una extensión obvia sería investigar diferentes bits de WiFi
tasas, cada una de las cuales sería una estrategia diferente.

· La entrada es el problema particular dado a este ensayo. Aquí es simplemente el
distancia entre los dos nodos.

· El runID es un identificador único para esta prueba con la que se etiqueta su información
para su identificación en análisis posteriores. Si no se proporciona un ID de ejecución, el programa de ejemplo realiza
un ID de ejecución (débil) utilizando la hora actual.

Esas cuatro piezas de metadatos son necesarias, pero se pueden desear más. Se pueden agregar
al registro usando el ns3::Collector de datos::AddMetadata() método.

datos del recopilador de datos;
data.DescribeRun(experimento, estrategia, entrada, runID);
data.AddMetadata("autor", "tjkopena");

La observación real y el cálculo se realizan mediante ns3::Calculadora de datos objetos, de los cuales
existen varios tipos diferentes. Estos son creados por el programa de simulación, adjunto a
informe o código de muestreo, y luego registrado con el ns3::Recopilador de datos así lo harán
ser consultado más tarde para su salida. Un mecanismo fácil de observación es utilizar
rastrear fuentes, por ejemplo, para instrumentar objetos en el núcleo ns-3 sin cambiar su
código. Aquí se adjunta un contador directamente a una señal de rastreo en la capa WiFi MAC en
el nodo de destino.

ptr totalRx = CreateObject ();
totalRx->SetKey("wifi-rx-marcos");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
datos.AddDataCalculator(totalRx);

Las calculadoras también se pueden manipular directamente. En este ejemplo, se crea un contador y
pasa a la aplicación de sumidero de tráfico para que se actualice cuando se reciban los paquetes.

ptr > appRx = CrearObjeto >();
appRx->SetKey("receptor-rx-paquetes");
receptor->EstablecerContador(appRx);
datos.AddDataCalculator(appRx);

Para incrementar el conteo, el código de procesamiento de paquetes del sumidero llama a uno de los
métodos de actualización de la calculadora.

m_calc->Actualizar();

El programa también incluye varios otros ejemplos, usando tanto la primitiva
calculadoras como ns3::ContadorCalculador y los adaptados para observar paquetes y
veces. En src/prueba/prueba02-aplicaciones.(cc|h) también crea una etiqueta personalizada simple que utiliza
para rastrear el retraso de extremo a extremo para los paquetes generados, informando los resultados a un
ns3::TimeMinMaxAvgTotalCalculator calculadora de datos

· Ejecutar la simulación, que es muy sencilla una vez construida.

Simulador::Ejecutar();

· Generar ya sea OMNet ++ or SQLite salida, dependiendo de los argumentos de la línea de comandos. A
haz esto un ns3::Interfaz de salida de datos el objeto es creado y configurado. El tipo específico
de esto determinará el formato de salida. A este objeto se le da entonces la
ns3::Recopilador de datos objeto que interroga para producir la salida.

ptr producción;
si (formato == "OMNet++") {
NS_LOG_INFO("Creando salida de datos con formato OMNet++.");
salida = CreateObject ();
} Else {
#ifdef STAT_USE_DB
NS_LOG_INFO("Creando salida de datos con formato SQLite.");
salida = CreateObject ();
# terminara si
}

salida->Salida(datos);

· Liberar cualquier memoria utilizada por la simulación. Esto debe venir al final de la principal
función para el ejemplo.

Simulador::Destruir();

Inicio de sesión
Para ver lo que el programa de ejemplo, las aplicaciones y el marco de estadísticas están haciendo en detalle, configure
los NS_LOG variable adecuadamente. Lo siguiente proporcionará un resultado copioso de todos
Tres.

$ exportar NS_LOG=WiFiDistanceExperiment:WiFiDistanceApps

Tenga en cuenta que esto ralentiza extraordinariamente la simulación.

Muestra Salida
Compilando y simplemente ejecutando el programa de prueba se agregará OMNet ++ salida formateada como
lo siguiente a datos.sca.

ejecutar ejecutar-1212239121

experimento attr "prueba de distancia wifi"
estrategia attr "wifi-default"
entrada de atributo "50"
atributo descripción ""

attr "autor" "tjkopena"

escalar wifi-tx-frames cuenta 30
escalar wifi-rx-frames cuenta 30
escalar remitente-tx-paquetes cuentan 30
escalar receptor-rx-paquetes cuentan 30
recuento escalar tx-pkt-size 30
escalar tx-pkt-tamaño total 1920
escalar tx-pkt-tamaño promedio 64
escalar tx-pkt-tamaño máximo 64
escalar tx-pkt-tamaño min 64
cuenta de retardo escalar 30
retraso escalar total 5884980ns
retraso escalar promedio 196166ns
retardo escalar máximo 196166ns
retardo escalar min 196166ns

Control Guión
Para automatizar la recopilación de datos en una variedad de entradas (distancias), un simple Bash
script se utiliza para ejecutar una serie de simulaciones. Se puede encontrar en
ejemplos/estadísticas/wifi-ejemplo-db.sh. El script está destinado a ejecutarse desde el ejemplos/estadísticas/
directorio.

El guión se ejecuta a través de un conjunto de distancias, recopilando los resultados en un SQLite
base de datos. En cada distancia se realizan cinco intentos para dar una mejor idea de lo esperado
actuación. Todo el experimento tarda solo unas pocas docenas de segundos en ejecutarse en un extremo bajo
máquina ya que no hay salida durante la simulación y se genera poco tráfico.

#!/ Bin / sh

DISTANCIAS="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
PRUEBAS="1 2 3 4 5"

Ejemplo de experimento echo WiFi

si [-e datos.db]
then
echo Matar data.db?
leer respuesta
si [ "$ANS" = "sí" -o "$ANS" = "s" ]
then
echo Eliminando base de datos
rm datos.db
fi
fi

para prueba en $TRIALS
do
para distancia en $DISTANCES
do
echo Prueba $prueba, distancia $distancia
./bin/test02 --format=db --distance=$distancia --run=ejecutar-$distancia-$prueba
done
done

Analisis y Conclusión
Una vez que se han realizado todas las pruebas, el script ejecuta una consulta SQL simple sobre el
base de datos usando el SQLite programa de línea de comandos. La consulta calcula la pérdida promedio de paquetes en
cada conjunto de intentos asociados con cada distancia. No tiene en cuenta diferentes
estrategias, pero la información está presente en la base de datos para hacer algunas extensiones simples
y hazlo Luego, los datos recopilados se pasan a GNUPlot para graficarlos.

CMD="seleccione exp.input,avg(100-((rx.value*100)/tx.value)) \
de Singletons rx, Singletons tx, Experiments exp \
donde rx.ejecutar = tx.ejecutar Y \
rx.ejecutar = exp.ejecutar Y \
rx.name='receptor-rx-paquetes' Y \
tx.name='remitente-tx-paquetes' \
grupo por exp.entrada \
ordenar por abs(exp.input) ASC;"

sqlite3 -noheader data.db "$CMD" > wifi-default.data
sed -i "s/|/ /" wifi-predeterminado.datos
gnuplot wifi-ejemplo.gnuplot

El script GNUPlot que se encuentra en ejemplos/estadísticas/wifi-ejemplo.gnuplot simplemente define la salida
formato y algunos formatos básicos para el gráfico.

establecer terminal postscript retrato mejorado lw 2 "Helvetica" 14

establecer tamaño 1.0, 0.66

#------------------------------------------------- ------
establece "wifi-default.eps"
#set title "Pérdida de paquetes por distancia"
establecer xlabel "Distancia (m) --- promedio de 5 intentos por punto"
establecer rango X [0:200]
establecer ylabel "% de pérdida de paquetes"
establecer rango y [0: 110]

trace "wifi-default.data" con el título de líneas "Valores predeterminados de WiFi"

End Resultado
El gráfico resultante no proporciona evidencia de que el rendimiento del modelo WiFi predeterminado sea
necesariamente irrazonable y da cierta confianza a una fidelidad al menos simbólica a
la realidad. Más importante aún, esta simple investigación se ha llevado a cabo durante todo el proceso.
utilizando el marco estadístico. ¡Éxito! [imagen]

RealTime
ns-3 ha sido diseñado para la integración en entornos de máquinas virtuales y banco de pruebas. A
integrarse con pilas de red reales y emitir/consumir paquetes, un programador en tiempo real es
necesario para tratar de bloquear el reloj de simulación con el reloj de hardware. Describimos aquí un
componente de esto: el planificador RealTime.

El propósito del programador en tiempo real es provocar la progresión del reloj de simulación
ocurrir sincrónicamente con respecto a alguna base de tiempo externa. sin la presencia de
una base de tiempo externa (reloj de pared), el tiempo de simulación salta instantáneamente de uno simulado
tiempo para el siguiente.

Comportamiento
Cuando utilice un programador que no sea en tiempo real (el valor predeterminado en ns-3), el simulador avanza el
tiempo de simulación hasta el próximo evento programado. Durante la ejecución del evento, el tiempo de simulación es
congelado. Con el programador en tiempo real, el comportamiento es similar desde la perspectiva de
modelos de simulación (es decir, el tiempo de simulación se congela durante la ejecución del evento), pero entre
eventos, el simulador intentará mantener el reloj de simulación alineado con la máquina
reloj.

Cuando un evento termina de ejecutarse y el programador pasa al siguiente evento, el
El programador compara el tiempo de ejecución del próximo evento con el reloj de la máquina. si el siguiente
el evento está programado para un tiempo futuro, el simulador duerme hasta que se alcanza ese tiempo real
y luego ejecuta el siguiente evento.

Puede ocurrir que, debido al procesamiento inherente a la ejecución de los eventos de simulación,
que el simulador no puede seguir el ritmo en tiempo real. En tal caso, corresponde al usuario
configuración qué hacer. Hay dos ns-3 atributos que rigen la conducta. los
primero es ns3::RealTimeSimulatorImpl::Modo de sincronización. Las dos entradas posibles para
este atributo son Mejor esfuerzo (el predeterminado) o límite duro. En el modo "BestEffort", el
simulador simplemente intentará ponerse al día en tiempo real mediante la ejecución de eventos hasta que llegue a un
punto donde el próximo evento está en el futuro (en tiempo real), o de lo contrario la simulación termina. En
modo BestEffort, entonces, es posible que la simulación consuma más tiempo que el
hora del reloj de pared. La otra opción "HardLimit" hará que la simulación aborte si el
se supera el umbral de tolerancia. Este atributo es ns3::RealTimeSimulatorImpl::HardLimit
y el valor predeterminado es 0.1 segundos.

Un modo diferente de operación es aquel en el que el tiempo simulado es no congelado durante un evento
ejecución. Este modo de simulación en tiempo real se implementó pero se eliminó del ns-3 tree
debido a dudas sobre si sería útil. Si los usuarios están interesados ​​en un tiempo real
simulador para el cual el tiempo de simulación no se congela durante la ejecución del evento (es decir, cada
llamar a Simulador::Ahora() devuelve la hora actual del reloj de pared, no la hora a la que
evento comenzó a ejecutarse), comuníquese con la lista de correo de ns-developers.

Uso
El uso del simulador en tiempo real es sencillo, desde la perspectiva de las secuencias de comandos.
Los usuarios solo necesitan establecer el atributo Tipo de implementación del simulador al tiempo real
simulador, como sigue:

GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl"));

Hay un guión en ejemplos/realtime/realtime-udp-echo.cc que tiene un ejemplo de cómo
configurar el comportamiento en tiempo real. Probar:

$ ./waf --ejecutar realtime-udp-echo

Se rige si el simulador funcionará en el mejor esfuerzo o en la política de límite estricto.
por los atributos explicados en el apartado anterior.

Implementación
La implementación está contenida en los siguientes archivos:

· src/core/model/realtime-simulator-impl.{cc,h}

· src/core/model/wall-clock-synchronizer.{cc,h}

Para crear un programador en tiempo real, en una primera aproximación, solo desea causar
el tiempo de simulación salta para consumir tiempo real. Proponemos hacer esto usando una combinación de
dormir y ocupado espera. Sleep-waits hace que el proceso de llamada (subproceso) produzca el
procesador durante cierto tiempo. A pesar de que esta cantidad de tiempo especificada se puede pasar
a una resolución de nanosegundos, en realidad se convierte a una granularidad específica del sistema operativo. En
Linux, la granularidad se llama Jiffy. Por lo general, esta resolución es insuficiente para
nuestras necesidades (del orden de diez milisegundos), por lo que redondeamos hacia abajo y dormimos durante algunos
menor número de Jiffies. Luego, el proceso se despierta después del número especificado de
Jiffies ha pasado. En este momento, tenemos algo de tiempo residual para esperar. esta vez es
generalmente más pequeño que el tiempo mínimo de sueño, por lo que esperamos ocupados el resto del
tiempo. Esto significa que el subproceso simplemente se encuentra en un bucle for que consume ciclos hasta que el
llega el momento deseado. Después de la combinación de esperas de sueño y ocupados, el tiempo real transcurrido
(pared) reloj debe coincidir con el tiempo de simulación del próximo evento y la simulación
producto.

Ayudantes
Los capítulos anteriores le presentaron varios ns-3 conceptos de programación como smart
punteros para la gestión de memoria contada por referencias, atributos, espacios de nombres, devoluciones de llamada, etc.
Los usuarios que trabajan en esta API de bajo nivel pueden interconectarse ns-3 objetos con granularidad fina.
Sin embargo, un programa de simulación escrito completamente con la API de bajo nivel sería bastante largo.
y tedioso para codificar. Por este motivo, se ha superpuesto una denominada "API de ayuda" independiente.
en el núcleo ns-3 API. Si has leído el ns-3 tutorial, ya te será familiar
con la API de ayuda, ya que es la API que los nuevos usuarios suelen conocer primero.
En este capítulo, presentamos la filosofía de diseño de la API auxiliar y la contrastamos con
la API de bajo nivel. Si se convierte en un gran usuario de ns-3, es probable que te muevas de un lado a otro
entre estas API incluso en el mismo programa.

La API auxiliar tiene algunos objetivos:

1. el resto de src / no tiene dependencias en la API auxiliar; cualquier cosa que se pueda hacer con
la API auxiliar también se puede codificar en la API de bajo nivel

2. Contenedores: A menudo, las simulaciones necesitarán realizar una serie de acciones idénticas a los grupos.
de objetos La API de ayuda hace un uso intensivo de contenedores de objetos similares a los que
se pueden realizar operaciones similares o idénticas.

3. La API auxiliar no es genérica; no se esfuerza por maximizar la reutilización del código. Asi que,
Las construcciones de programación como el polimorfismo y las plantillas que logran la reutilización del código son
no tan prevalente. Por ejemplo, hay ayudantes CsmaNetDevice separados y
Ayudantes de PointToPointNetDevice, pero no se derivan de una base común de NetDevice
clase.

4. La API de ayuda normalmente funciona con objetos asignados a la pila (frente a objetos asignados al montón). Para
algunos programas, ns-3 Es posible que los usuarios no tengan que preocuparse por la creación o creación de objetos de bajo nivel.
manejo de ptr; pueden arreglárselas con contenedores de objetos y ayudantes asignados a la pila
que operan sobre ellos.

La API de ayuda se trata realmente de hacer ns-3 programas más fáciles de escribir y leer, sin
quitando el poder de la interfaz de bajo nivel. El resto de este capítulo proporciona algunos
ejemplos de las convenciones de programación de la API auxiliar.

Realizar Parcelas usando los parcela Clase
Hay 2 métodos comunes para hacer un diagrama usando ns-3 y gnuplot (‐
http://www.gnuplot.info):

1. Cree un archivo de control gnuplot usando ns-3de la clase Gnuplot.

2. Cree un archivo de datos gnuplot usando valores generados por ns-3.

Esta sección trata sobre el método 1, es decir, se trata de cómo hacer un diagrama usando ns-3's Gnuplot
clase. Si está interesado en el método 2, consulte la subsección "Un ejemplo real" bajo el
sección "Rastreo" en el ns-3 Tutorial.

Creamos Parcelas Gracias a los parcela Clase
Se deben seguir los siguientes pasos para crear una trama usando ns-3Clase de Gnuplot:

1. Modifica tu código para que use la clase Gnuplot y sus funciones.

2. Ejecute su código para que cree un archivo de control gnuplot.

3. Llame a gnuplot con el nombre del archivo de control de gnuplot.

4. Vea el archivo de gráficos que se produjo en su visor de gráficos favorito.

Consulte el código de los gráficos de ejemplo que se analizan a continuación para obtener detalles sobre el paso 1.

An Ejemplo Programa que Usos los parcela Clase
Un programa de ejemplo que utiliza ns-3La clase Gnuplot de se puede encontrar aquí:

src/stats/examples/gnuplot-example.cc

Para ejecutar este ejemplo, haga lo siguiente:

$ ./cáscara waf
$ cd compilación/depuración/src/estadísticas/ejemplos
$ ./gnuplot-ejemplo

Esto debería producir los siguientes archivos de control de gnuplot en el directorio donde se encuentra el ejemplo.
se encuentra:

trama-2d.plt
trama-2d-con-barras-de-error.plt
trama-3d.plt

Para procesar estos archivos de control gnuplot, haga lo siguiente:

$ gnuplot trama-2d.plt
$ gnuplot plot-2d-con-barras-de-error.plt
$ gnuplot trama-3d.plt

Esto debería producir los siguientes archivos de gráficos en el directorio donde está el ejemplo
situado:

trama-2d.png
trama-2d-con-barras-de-error.png
trama-3d.png

Puede ver estos archivos de gráficos en su visor de gráficos favorito. Si tienes gimp
instalado en su máquina, por ejemplo, puede hacer esto:

$ gimp trama-2d.png
$ gimp plot-2d-con-barras-de-error.png
$ gimp trama-3d.png

An Ejemplo 2 dimensiones Parcela
La siguiente trama bidimensional
[imagen]

fue creado usando el siguiente código de gnuplot-example.cc:

Using namespace std;

string fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Gráfica 2-D";
string dataTitle = "Datos 2-D";

// Crea una instancia de la trama y establece su título.
Gráfico Gnuplot (graphicsFileName);
plot.SetTitle (parcelaTitle);

// Cree el archivo de gráficos, que el archivo de trazado creará cuando se
// se usa con Gnuplot, sea un archivo PNG.
trama.SetTerminal ("png");

// Establecer las etiquetas para cada eje.
plot.SetLegend ("Valores X", "Valores Y");

// Establecer el rango para el eje x.
plot.AppendExtra ("establecer rango x [-6:+6]");

// Crear una instancia del conjunto de datos, establecer su título y hacer que los puntos sean
// trazado junto con las líneas de conexión.
conjunto de datos Gnuplot2dDataset;
conjunto de datos.SetTitle (título de datos);
conjunto de datos.SetStyle (Gnuplot2dDataset::LINES_POINTS);

doble X;
doble y;

// Crea el conjunto de datos 2-D.
para (x = -5.0; x <= +5.0; x += 1.0)
{
// Calcula la curva 2-D
//
/ / 2
// y = x.
//
y = x * x;

// Agrega este punto.
conjunto de datos. Agregar (x, y);
}

// Agrega el conjunto de datos a la gráfica.
plot.AddDataset (conjunto de datos);

// Abre el archivo de trazado.
ofstream plotFile (plotFileName.c_str());

// Escribir el archivo de trazado.
plot.GenerateOutput (plotFile);

// Cierra el archivo de trazado.
plotFile.cerrar ();

An Ejemplo 2 dimensiones Parcela con Error Barras
La siguiente gráfica bidimensional con barras de error en las direcciones x e y
[imagen]

fue creado usando el siguiente código de gnuplot-example.cc:

Using namespace std;

string fileNameWithNoExtension = "plot-2d-with-error-bars";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Gráfico 2-D con barras de error";
string dataTitle = "Datos 2-D con barras de error";

// Crea una instancia de la trama y establece su título.
Gráfico Gnuplot (graphicsFileName);
plot.SetTitle (parcelaTitle);

// Cree el archivo de gráficos, que el archivo de trazado creará cuando se
// se usa con Gnuplot, sea un archivo PNG.
trama.SetTerminal ("png");

// Establecer las etiquetas para cada eje.
plot.SetLegend ("Valores X", "Valores Y");

// Establecer el rango para el eje x.
plot.AppendExtra ("establecer rango x [-6:+6]");

// Crear una instancia del conjunto de datos, establecer su título y hacer que los puntos sean
// trazado sin líneas de conexión.
conjunto de datos Gnuplot2dDataset;
conjunto de datos.SetTitle (título de datos);
conjunto de datos.SetStyle (Gnuplot2dDataset::POINTS);

// Hacer que el conjunto de datos tenga barras de error en las direcciones x e y.
conjunto de datos.SetErrorBars (Gnuplot2dDataset::XY);

doble X;
doble xErrorDelta;
doble y;
doble yErrorDelta;

// Crea el conjunto de datos 2-D.
para (x = -5.0; x <= +5.0; x += 1.0)
{
// Calcula la curva 2-D
//
/ / 2
// y = x.
//
y = x * x;

// Hacer que la incertidumbre en la dirección x sea constante y hacer
// la incertidumbre en la dirección y sea una fracción constante de
// valor de y.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;

// Agregue este punto con incertidumbres tanto en x como en y
// dirección.
conjunto de datos. Agregar (x, y, xErrorDelta, yErrorDelta);
}

// Agrega el conjunto de datos a la gráfica.
plot.AddDataset (conjunto de datos);

// Abre el archivo de trazado.
ofstream plotFile (plotFileName.c_str());

// Escribir el archivo de trazado.
plot.GenerateOutput (plotFile);

// Cierra el archivo de trazado.
plotFile.cerrar ();

An Ejemplo 3 dimensiones Parcela
La siguiente trama bidimensional
[imagen]

fue creado usando el siguiente código de gnuplot-example.cc:

Using namespace std;

string fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + ".png";
string plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "Gráfica 3-D";
string dataTitle = "Datos 3-D";

// Crea una instancia de la trama y establece su título.
Gráfico Gnuplot (graphicsFileName);
plot.SetTitle (parcelaTitle);

// Cree el archivo de gráficos, que el archivo de trazado creará cuando se
// se usa con Gnuplot, sea un archivo PNG.
trama.SetTerminal ("png");

// Girar el gráfico 30 grados alrededor del eje x y luego girar el
// traza 120 grados alrededor del nuevo eje z.
plot.AppendExtra ("establecer vista 30, 120, 1.0, 1.0");

// Hacer que el cero para el eje z esté en el plano del eje x y del eje y.
plot.AppendExtra ("establecer ticslevel 0");

// Establecer las etiquetas para cada eje.
plot.AppendExtra ("establecer xlabel 'X Values'");
plot.AppendExtra ("establecer ylabel 'Valores Y'");
plot.AppendExtra ("establecer zlabel 'Valores Z'");

// Establecer los rangos para los ejes x e y.
plot.AppendExtra ("establecer rango x [-5:+5]");
plot.AppendExtra ("establecer yrange [-5:+5]");

// Crear una instancia del conjunto de datos, establecer su título y hacer que los puntos sean
// conectado por líneas.
conjunto de datos Gnuplot3dDataset;
conjunto de datos.SetTitle (título de datos);
dataset.SetStyle ("con líneas");

doble X;
doble y;
doble z;

// Crea el conjunto de datos 3-D.
para (x = -5.0; x <= +5.0; x += 1.0)
{
para (y = -5.0; y <= +5.0; y += 1.0)
{
// Calcular la superficie 3-D
//
// 2 2
// z = x * y .
//
z = x * x * y * y;

// Agrega este punto.
conjunto de datos. Agregar (x, y, z);
}

// La línea en blanco es necesaria al final de los datos de cada valor x
// puntos para que funcione la cuadrícula de superficie 3-D.
conjunto de datos.AddEmptyLine ();
}

// Agrega el conjunto de datos a la gráfica.
plot.AddDataset (conjunto de datos);

// Abre el archivo de trazado.
ofstream plotFile (plotFileName.c_str());

// Escribir el archivo de trazado.
plot.GenerateOutput (plotFile);

// Cierra el archivo de trazado.
plotFile.cerrar ();

Gracias a Python a Ejecutar ns-3
Los enlaces de Python permiten que el código C++ entre ns-3 para ser llamado desde Python.

Este capítulo le muestra cómo crear un script de Python que pueda ejecutarse ns-3 y también el
proceso de creación de enlaces de Python para un C++ ns-3 módulo.

Introducción
El objetivo de los enlaces de Python para ns-3 son dos veces:

1. Permita que el programador escriba scripts de simulación completos en Python (‐
http://www.python.org);

2. Crear prototipos de nuevos modelos (p. ej., protocolos de enrutamiento).

Por el momento, el enfoque principal de las fijaciones es el primer objetivo, pero el segundo
finalmente, el objetivo también será compatible. Enlaces de Python para ns-3 están siendo desarrollados
usando una nueva herramienta llamada PyBindGen (http://code.google.com/p/pybindgen).

An Ejemplo Python Guión que Ron ns-3
Aquí hay un código de ejemplo que está escrito en Python y que se ejecuta ns-3, que está escrito
en C++. Este ejemplo de Python se puede encontrar en ejemplos/tutorial/primero.py:

importar ns.aplicaciones
importar ns.core
importar ns.internet
importar ns.network
importar ns.punto_a_punto

ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)

nodos = ns.network.NodeContainer()
nodos.Crear(2)

puntoAPunto = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("Velocidad de datos", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Retraso", ns.core.StringValue("2ms"))

dispositivos = pointToPoint.Install(nodos)

pila = ns.internet.InternetStackHelper()
pila.Instalar(nodos)

dirección = ns.internet.Ipv4AddressHelper()
dirección.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))

interfaces = dirección. Asignar (dispositivos);

servidoreco = ns.applications.UdpEchoServerHelper(9)

aplicaciones de servidor = echoServer.Install(nodos.Obtener(1))
servidorApps.Start(ns.core.Seconds(1.0))
servidorApps.Stop(ns.core.Seconds(10.0))

echoClient = ns.aplicaciones.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
echoClient.SetAttribute("Paquetes máximos", ns.core.UintegerValue(1))
echoClient.SetAttribute("Intervalo", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("Tamaño del paquete", ns.core.UintegerValue(1024))

clientApps = echoClient.Install(nodos.Obtener(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))

ns.core.Simulador.Ejecutar()
ns.core.Simulator.Destroy()

Correr Python Scripts
waf contiene algunas opciones que actualizan automáticamente la ruta de python para encontrar el ns3
módulo. Para ejecutar programas de ejemplo, hay dos formas de usar waf para encargarse de esto. Una
es ejecutar un shell waf; p.ej:

$ ./waf --cáscara
$ python ejemplos/inalámbrico/mixto-inalámbrico.py

y la otra es usar la opción --pyrun para waf:

$ ./waf --pyrun ejemplos/inalámbrico/mixto-inalámbrico.py

Para ejecutar un script de python bajo el depurador de C:

$ ./waf --cáscara
$ gdb --args python ejemplos/inalámbrico/mixto-inalámbrico.py

Para ejecutar su propio script de Python que llama ns-3 y que tiene este camino,
/ruta/a/su/ejemplo/mi-script.py, Haz lo siguiente:

$ ./waf --cáscara
$ python /ruta/a/su/ejemplo/mi-script.py

Advertencias
Enlaces de Python para ns-3 son un trabajo en progreso, y algunas limitaciones son conocidas por
desarrolladores Algunas de estas limitaciones (no todas) se enumeran aquí.

Incompleto Cobertura
En primer lugar, tenga en cuenta que Python no admite el 100 % de la API. Algunos de los
las razones son:

1. algunas de las API implican punteros, que requieren saber qué tipo de memoria
pasar la semántica (quién es dueño de qué memoria). Tal conocimiento no es parte de la función
firmas, y está documentado o, a veces, ni siquiera está documentado. Las anotaciones son
necesarios para vincular esas funciones;

2. A veces se utiliza un tipo de datos fundamental inusual o una construcción de C++ que aún no está
apoyado por PyBindGen;

3. GCC-XML no informa clases basadas en plantillas a menos que se instancian.

La mayoría de las API que faltan se pueden empaquetar, con suficiente tiempo, paciencia y experiencia, y
probablemente se terminará si se envían informes de errores. Sin embargo, no presente un informe de error
diciendo "las encuadernaciones están incompletas", porque no tenemos mano de obra para completar el 100% de las
fijaciones.

Conversión Constructores
Conversión constructores no son totalmente compatibles con PyBindGen todavía, y siempre actúan como
constructores explícitos al traducir una API a Python. Por ejemplo, en C++ puedes hacer
modo:

Ipv4AddressHelper direcciones IP;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Asignar (backboneDevices);

En Python, por el momento tienes que hacer:

direccionesip = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(dispositivos troncales)

Línea de comando
Línea de Comando::AñadirValor() funciona de manera diferente en Python que en ns-3. En Python, el
El primer parámetro es una cadena que representa el nombre de la opción de la línea de comandos. Cuando la opción
se establece, un atributo con el mismo nombre que el nombre de la opción se establece en el Línea de comando()
objeto. Ejemplo:

NUM_NODES_SIDE_DEFAULT = 3

cmd = ns3.Línea de comandos()

cmd.NumNodesSide = Ninguno
cmd.AddValue("NumNodesSide", "Número de nodos del lado de la cuadrícula (el número total de nodos será este número al cuadrado)")

cmd. Parse (argv)

[...]

si cmd.NumNodesSide es Ninguno:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
más:
num_nodes_side = int(cmd.NumNodesSide)

Rastreo
El seguimiento basado en devolución de llamadas aún no es compatible correctamente con Python, como nuevo ns-3 La API necesita
proporcionarse para que esto sea compatible.

La escritura de archivos Pcap se admite a través de la API normal.

El rastreo ASCII es compatible desde ns-3.4 a través de la API C++ normal traducida a Python.
Sin embargo, el rastreo ascii requiere la creación de un objeto ostream para pasar al código ascii.
métodos de rastreo. En Python, C++ std::ofstream se ha ajustado mínimamente para permitir
este. Por ejemplo:

ascii = ns3.ofstream("wifi-ap.tr") # crear el archivo
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulador.Ejecutar()
ns3.Simulador.Destruir()
ascii.close() # cerrar el archivo

Hay una advertencia: no debe permitir que el objeto de archivo se recopile como basura mientras ns-3
todavía lo está usando. Eso significa que no se debe permitir que la variable 'ascii' de arriba vaya
fuera del alcance o de lo contrario el programa se bloqueará.

Cygwin la limitación
Los enlaces de Python no funcionan en Cygwin. Esto se debe a un error de gccxml.

Puede salirse con la suya volviendo a escanear las definiciones de API desde cygwin
entorno (./waf --python-scan). Sin embargo, la solución más probable probablemente tendrá que
sea ​​que deshabilitemos los enlaces de python en CygWin.

Si realmente le importan los enlaces de Python en Windows, intente compilar con mingw y native
pitón en su lugar. O bien, para construir sin enlaces de python, deshabilite los enlaces de python en el
etapa de configuración:

$ ./waf configurar --disable-python

Acoplar con Python encuadernaciones
Actualmente hay dos tipos de enlaces de Python en ns-3:

1. Los enlaces monolíticos contienen definiciones de API para todos los módulos y se pueden encontrar en
un solo directorio, enlaces/pitón.

2. Los enlaces modulares contienen definiciones de API para un solo módulo y se pueden encontrar en cada
módulo de Enlaces directorio.

Python encuadernaciones Flujo de trabajo
El proceso por el cual se manejan los enlaces de Python es el siguiente:

1. Periódicamente, un desarrollador utiliza un GCC-XML (http://www.gccxml.org) análisis de API basado
script, que guarda la definición de la API escaneada como enlaces/python/ns3_module_*.py archivos
o como archivos de Python en cada módulo Enlaces directorio. Estos archivos se mantienen bajo
control de versiones en el principal ns-3 repositorio;

2. Otros desarrolladores clonan el repositorio y usan las definiciones de API ya escaneadas;

3. Al configurar ns-3, pybindgen se descargará automáticamente si no lo está ya
instalado. Liberado ns-3 tarballs enviará una copia de pybindgen.

Si algo sale mal con la compilación de enlaces de Python y simplemente desea ignorarlos
y continúa con C++, puedes deshabilitar Python con:

$ ./waf --disable-python

Instrucciones for Manejo Nuevo archivos or cambiado API
Así que has estado cambiando las existentes ns-3 ¿Las API y los enlaces de Python ya no se compilan? Hacer
no se desespere, puede volver a escanear los enlaces para crear nuevos enlaces que reflejen los cambios
En el correo electrónico “Su Cuenta de Usuario en su Nuevo Sistema XNUMXCX”. ns-3 API.

Dependiendo de si está utilizando encuadernaciones monolíticas o modulares, consulte las discusiones a continuación para
aprenda cómo volver a escanear sus enlaces de Python.

Monolítico Python encuadernaciones
Escaneado los Monolítico Python encuadernaciones
Para escanear los enlaces monolíticos de Python, haga lo siguiente:

$ ./waf --python-escaneo

Organización of los Monolítico Python encuadernaciones
Las definiciones monolíticas de la API de Python están organizadas de la siguiente manera. Para cada ns-3 módulo
, el archivo enlaces/python/ns3_module_ .py describe su API. cada uno de esos
Los archivos tienen 3 funciones de nivel superior:

1. def tipos_de_registro(módulo)(): esta función se encarga de dar de alta nuevos tipos (ej.
Clases de C++, enumeraciones) que se definen en ese módulo;

2. def métodos_de_registro(módulo)(): esta función llama, para cada clase , otro
función register_methods_Ns3 (módulo). Estas últimas funciones agregan método
definiciones para cada clase;

3. def registrar_funciones(módulo)(): esta función registra ns-3 funciones que pertenecen
ese módulo.

Modular Python encuadernaciones
Resumen
Desde ns 3.11, se añaden las fijaciones modulares, en paralelo a las antiguas monolíticas
fijaciones.

Los nuevos enlaces de python se generan en un espacio de nombres 'ns', en lugar de 'ns3' para el antiguo
ataduras Ejemplo:

desde el nodo de importación ns.network
n1 = Nodo()

Con enlaces Python modulares:

1. Hay un módulo de extensión de Python separado para cada ns-3 módulo;

2. Las definiciones de API de escaneo (apidefs) se realizan por módulo ns;

3. Los archivos apidefs de cada módulo se almacenan en un subdirectorio 'bindings' del módulo
directorio;

Escaneado los Modular Python encuadernaciones
Para escanear los enlaces modulares de Python para el módulo central, por ejemplo, haga lo siguiente:

$ ./waf --apiscan=núcleo

Para escanear los enlaces modulares de Python para todos los módulos, haga lo siguiente:

$ ./waf --apiscan=todos

Creamos a Nuevo Módulo
Si está agregando un nuevo módulo, los enlaces de Python continuarán compilando pero no
cubrir el nuevo módulo.

Para cubrir un nuevo módulo, debe crear un enlaces/python/ns3_module_ .py archivo,
similar a lo descrito en los apartados anteriores, y registrarlo en la variable
MÓDULOS_LOCALES() in enlaces/python/ns3modulegen.py

Adición Modular encuadernaciones A A Ya eres Módulo
Para agregar soporte para enlaces modulares a un existente ns-3 módulo, simplemente agregue lo siguiente
línea a su función wscript build():

bld.ns3_python_bindings()

Organización of los Modular Python encuadernaciones
El origen/ /encuadernaciones directorio puede contener los siguientes archivos, algunos de ellos
Opcional:

· lista_devoluciones de llamada.py: este es un archivo escaneado, NO TOCAR. Contiene una lista de
Instancias de plantilla de devolución de llamada<...> encontradas en los encabezados escaneados;

· módulogen__gcc_LP64.py: este es un archivo escaneado, NO TOCAR. Definiciones de API escaneadas
para la arquitectura GCC, LP64 (64 bits)

· módulogen__gcc_ILP32.py: este es un archivo escaneado, NO TOCAR. Definiciones de API escaneadas
para la arquitectura GCC, ILP32 (32 bits)

· modulegen_personalizaciones.py: opcionalmente puede agregar este archivo para personalizar el
generación de código pybindgen

· encabezado-escaneo.h: opcionalmente puede agregar este archivo para personalizar qué archivo de encabezado se escanea
para el módulo. Básicamente, este archivo se escanea en lugar de ns3/ -módulo.h.
Por lo general, la primera instrucción es #include "ns3/ -module.h", además de algunos otros
cosas para forzar instanciaciones de plantillas;

· module_helpers.cc: puede agregar archivos adicionales, como este, para vincularlos a python
módulo de extensión, pero tienen que estar registrados en el wscript. Mirar
src/core/wscript para ver un ejemplo de cómo hacerlo;

· .py: si este archivo existe, se convierte en el módulo de python "frontend" para ns3
y el módulo de extensión (archivo .so) se convierte en _ .así que en lugar de .asi que.
los El archivo .py tiene que importar todos los símbolos del módulo _ (esto es más
complicado de lo que parece, consulte src/core/bindings/core.py para ver un ejemplo), y luego puede agregar
algunas definiciones adicionales de Python puro.

Más Información for Desarrolladores
Si eres un desarrollador y necesitas más información sobre ns-3enlaces de Python, consulte el
Python encuadernaciones wiki página.

Examenes
Resumen
Este documento se refiere a la prueba y validación de ns-3 software.

Este documento proporciona

· antecedentes sobre terminología y pruebas de software (Capítulo 2);

· una descripción del marco de prueba de ns-3 (Capítulo 3);

· una guía para desarrolladores de modelos o contribuyentes de nuevos modelos sobre cómo escribir pruebas (Capítulo
4);

En resumen, los primeros tres capítulos deben ser leídos por los desarrolladores y colaboradores de ns que
necesita entender cómo contribuir con el código de prueba y los programas validados, y el resto
del documento brinda espacio para que las personas informen sobre qué aspectos de los modelos seleccionados
han sido validados.

Antecedentes
Este capítulo pueden be salteado by lectores familiar con los Conceptos básicos of software pruebas.

Escribir software libre de defectos es una propuesta difícil. Hay muchas dimensiones en el
problema y hay mucha confusión con respecto a lo que significan los diferentes términos en
diferentes contextos. Hemos encontrado que vale la pena dedicar un poco de tiempo a revisar el
tema y definiendo algunos términos.

La prueba de software puede definirse vagamente como el proceso de ejecutar un programa con la
intención de encontrar errores. Cuando uno entra en una discusión sobre pruebas de software,
rápidamente se hace evidente que hay muchas mentalidades distintas con las que uno puede
acercarse al tema.

Por ejemplo, uno podría dividir el proceso en amplias categorías funcionales como
"pruebas de corrección", "pruebas de rendimiento", "pruebas de solidez" y "pruebas de seguridad".
pruebas". Otra forma de ver el problema es por ciclo de vida: "pruebas de requisitos".
"pruebas de diseño", "pruebas de aceptación" y "pruebas de mantenimiento".
es por el alcance del sistema probado. En este caso se puede hablar de "pruebas unitarias",
"pruebas de componentes", "pruebas de integración" y "pruebas de sistemas". Estos términos son
tampoco estandarizados de ninguna manera, por lo que las "pruebas de mantenimiento" y "regresión"
testing'' puede escucharse indistintamente. Además, estos términos a menudo se usan incorrectamente.

También hay una serie de enfoques filosóficos diferentes para las pruebas de software. Para
Por ejemplo, algunas organizaciones abogan por escribir programas de prueba antes de implementarlos realmente.
el software deseado, produciendo "desarrollo basado en pruebas". Algunas organizaciones abogan
pruebas desde la perspectiva del cliente tan pronto como sea posible, siguiendo un paralelo con el
proceso de desarrollo ágil: "prueba temprano y prueba a menudo". Esto a veces se llama
"pruebas ágiles". Parece que hay al menos un enfoque de prueba para cada
metodología de desarrollo.

El ns-3 proyecto no está en el negocio de abogar por cualquiera de estos procesos, pero
el proyecto como un todo tiene requisitos que ayudan a informar el proceso de prueba.

Como todos los principales productos de software, ns-3 tiene una serie de cualidades que deben estar presentes para
el producto para tener éxito. Desde una perspectiva de prueba, algunas de estas cualidades que deben ser
abordados son que ns-3 debe ser "correcto", "robusto", "rendimiento" y
"mantenible". Idealmente, debería haber métricas para cada una de estas dimensiones que sean
verificado por las pruebas para identificar cuando el producto no cumple con sus expectativas /
• Requisitos.

Exactitud
El propósito esencial de las pruebas es determinar que una pieza de software se comporta
"correctamente". Para ns-3 esto significa que si simulamos algo, la simulación debería
representar fielmente alguna entidad física o proceso con una precisión específica y
precisión.

Resulta que hay dos perspectivas desde las que uno puede ver la corrección.
Verificar que un modelo en particular se implemente de acuerdo con su especificación es
genéricamente llamado verificación. El proceso de decidir que el modelo es correcto para
su uso previsto se denomina genéricamente validación.

Validación y Verificación
Un modelo de computadora es una representación matemática o lógica de algo. Puede
representan un vehículo, un elefante (ver David de harel hablar del modelado an elefante at
SIMUHerramientas 2009o una tarjeta de red. Los modelos también pueden representar procesos como global
el calentamiento global, el flujo de tráfico de la autopista o una especificación de un protocolo de red. Los modelos pueden ser
representaciones completamente fieles de una especificación de proceso lógico, pero
necesariamente nunca puede simular completamente un objeto o proceso físico. En la mayoría de los casos, un
Se hacen varias simplificaciones al modelo para hacer la simulación computacionalmente.
manejable.

Cada modelo tiene un dirigidos te que está tratando de simular. El primer paso en
crear un modelo de simulación es identificar este sistema de destino y el nivel de detalle y
precisión que se desea reproducir en la simulación. En el caso de un proceso lógico,
el sistema de destino puede identificarse como ''TCP según lo definido por RFC 793''. En este caso,
probablemente será deseable crear un modelo que reproduzca completa y fielmente RFC
793. Tratándose de un proceso físico esto no será posible. Si, por ejemplo, usted
le gustaría simular una tarjeta de red inalámbrica, puede determinar que necesita, "un
implementación precisa a nivel MAC de la especificación 802.11 y una no tan lenta
Modelo de nivel PHY de la especificación 802.11a.''

Una vez hecho esto, se puede desarrollar un modelo abstracto del sistema de destino. Esto es
típicamente un ejercicio en la gestión de las compensaciones entre la complejidad, los requisitos de recursos
y precisión. El proceso de desarrollo de un modelo abstracto ha sido llamado modelo
calificación en la literatura. En el caso de un protocolo TCP, este proceso da como resultado una
diseño para una colección de objetos, interacciones y comportamientos que implementarán completamente
RFC 793 en ns-3. En el caso de la tarjeta inalámbrica, este proceso da como resultado una serie de
compensaciones para permitir la simulación de la capa física y el diseño de un dispositivo de red
y canal para ns-3, junto con los objetos, interacciones y comportamientos deseados.

Este modelo abstracto se desarrolla luego en un ns-3 modelo que implementa el resumen
modelo como un programa de computadora. El proceso de lograr que la implementación esté de acuerdo con el
modelo abstracto se llama modelo verificación en la literatura.

El proceso hasta ahora es de bucle abierto. Lo que queda es hacer una determinación de que un ns-3 dado
modelo tiene alguna conexión con alguna realidad, que un modelo es una representación precisa de
un sistema real, ya sea un proceso lógico o una entidad física.

Si uno va a utilizar un modelo de simulación para tratar de predecir cómo funciona un sistema real
comportarse, debe haber alguna razón para creer en sus resultados, es decir, ¿puede uno confiar en que
una inferencia hecha del modelo se traduce en una predicción correcta para el sistema real.
El proceso de hacer que el comportamiento del modelo ns-3 esté de acuerdo con el sistema de destino deseado
el comportamiento definido por el proceso de cualificación del modelo se denomina modelo validación en la categoría Industrial.
literatura. En el caso de una implementación de TCP, es posible que desee comparar el comportamiento de
su modelo ns-3 TCP a alguna implementación de referencia para validar su modelo. En
el caso de una simulación de capa física inalámbrica, es posible que desee comparar el comportamiento de
su modelo al de hardware real en un entorno controlado,

El ns-3 entorno de prueba proporciona herramientas para permitir la validación del modelo y
las pruebas y fomenta la publicación de los resultados de la validación.

Robustez
Robustez es la cualidad de ser capaz de soportar esfuerzos, o cambios en los ambientes,
entradas o cálculos, etc. Un sistema o diseño es "robusto" si puede manejar tales
cambios con una pérdida mínima de funcionalidad.

Este tipo de prueba generalmente se realiza con un enfoque particular. Por ejemplo, el sistema como
un todo se puede ejecutar en muchas configuraciones diferentes del sistema para demostrar que puede
funcionar correctamente en un gran número de entornos.

El sistema también se puede estresar operando cerca o más allá de su capacidad generando
o simulando el agotamiento de recursos de varios tipos. Este género de prueba se llama
''pruebas de estrés.''

El sistema y sus componentes pueden estar expuestos a las llamadas "pruebas limpias" que demuestran
un resultado positivo, es decir, que el sistema funciona correctamente en respuesta a un gran
variación de las configuraciones esperadas.

El sistema y sus componentes también pueden estar expuestos a "pruebas sucias" que proporcionan entradas
fuera del rango esperado. Por ejemplo, si un módulo espera una cadena terminada en cero
representacin de un nmero entero, una prueba sucia podra proporcionar una cadena no terminada de aleatorio
caracteres para verificar que el sistema no falla como resultado de esta entrada inesperada.
Desafortunadamente, detectar tal entrada "sucia" y tomar medidas preventivas para asegurar la
el sistema no falla catastróficamente puede requerir una gran cantidad de gastos generales de desarrollo.
Con el fin de reducir el tiempo de desarrollo, se tomó la decisión al principio del proyecto de
minimizar la cantidad de validación de parámetros y manejo de errores en el ns-3 base de código. Para
Por esta razón, no dedicamos mucho tiempo a las pruebas sucias; simplemente descubriría el
resultados de la decisión de diseño que sabemos que tomamos.

Queremos demostrar que ns-3 el software funciona en un conjunto de condiciones. Nosotros
tome prestadas un par de definiciones para reducir esto un poco. los dominio of aplicabilidad is
un conjunto de condiciones prescritas para las cuales el modelo ha sido probado, en comparación con
la realidad en la medida de lo posible, y juzgado adecuado para su uso. los distancia of la exactitud es un
acuerdo entre el modelo computarizado y la realidad dentro de un dominio de aplicabilidad.

El ns-3 entorno de prueba proporciona herramientas para permitir la configuración y ejecución de pruebas
entornos en múltiples sistemas (buildbot) y proporciona clases para fomentar la limpieza
pruebas para verificar la operación del sistema sobre el "dominio de aplicabilidad" esperado
y "rango de precisión".

potente
Vale, "performant" no es una palabra inglesa real. Es, sin embargo, un neologismo muy conciso.
que se usa muy a menudo para describir lo que queremos ns-3 ser: lo suficientemente potente y rápido como para
Termina el trabajo.

Esto es realmente sobre el amplio tema de las pruebas de rendimiento del software. una de las claves
cosas que se hace es comparar dos sistemas para encontrar cuál funciona mejor (cf.
puntos de referencia). Esto se usa para demostrar que, por ejemplo, ns-3 puede realizar un tipo básico
de simulación al menos tan rápido como una herramienta de la competencia, o se puede utilizar para identificar partes de
el sistema que funciona mal.

En Los ns-3 marco de prueba, brindamos soporte para cronometrar varios tipos de pruebas.

Mantenibilidad
Un producto de software debe ser mantenible. Esta es, de nuevo, una declaración muy amplia, pero una
marco de prueba puede ayudar con la tarea. Una vez que un modelo ha sido desarrollado, validado y
verificado, podemos ejecutar repetidamente el conjunto de pruebas para todo el sistema para garantizar
que sigue siendo válido y verificado a lo largo de su vida.

Cuando una característica deja de funcionar según lo previsto después de algún tipo de cambio en el sistema.
integrada, se denomina genéricamente regresión. Originalmente el término regresión
se refería a un cambio que provocó la reaparición de un error corregido previamente, pero el término ha
evolucionado para describir cualquier tipo de cambio que rompa la funcionalidad existente. Hay muchos
tipos de regresiones que pueden ocurrir en la práctica.

A local regresión es aquella en la que un cambio afecta directamente al componente modificado. Para
ejemplo, si un componente se modifica para asignar y liberar memoria pero los punteros obsoletos son
utilizado, el componente mismo falla.

A sanaciones regresión es aquel en el que un cambio en un componente rompe la funcionalidad en
otro componente Esto refleja la violación de una obligación implícita pero posiblemente no reconocida.
contrato entre componentes.

An desenmascarado regresión es uno que crea una situación en la que un error previamente existente
que no tuvo ningún efecto es repentinamente expuesto en el sistema. Esto puede ser tan simple como hacer ejercicio
una ruta de código por primera vez.

A actuación regresión es aquel que hace que los requisitos de rendimiento del sistema
ser violado. Por ejemplo, hacer algún trabajo en una función de bajo nivel que puede repetirse
un gran número de veces puede hacer que el sistema quede inutilizable repentinamente desde ciertas perspectivas.

El ns-3 marco de prueba proporciona herramientas para automatizar el proceso utilizado para validar y
verifique el código en conjuntos de pruebas nocturnas para ayudar a identificar rápidamente posibles regresiones.

Pruebas marco
ns-3 consta de un motor central de simulación, un conjunto de modelos, programas de ejemplo y pruebas.
Con el tiempo, los nuevos colaboradores aportan modelos, pruebas y ejemplos. Un programa de prueba de Python
prueba.py sirve como administrador de ejecución de pruebas; prueba.py puede ejecutar código de prueba y ejemplos para
buscar regresiones, puede generar los resultados en varios formularios y puede administrar el código
herramientas de análisis de cobertura. Además de esto, ponemos capas Construir robots que son compilaciones automatizadas
robots que realizan pruebas de robustez ejecutando el marco de prueba en diferentes sistemas
y con diferentes opciones de configuración.

ConstruirBots
En el nivel más alto de las pruebas de ns-3 se encuentran los buildbots (robots de construcción). Si usted es
no está familiarizado con este sistema, mire http://djmitche.github.com/buildbot/docs/0.7.11/.
Este es un sistema automatizado de código abierto que permite ns-3 para ser reconstruido y probado cada
tiempo algo ha cambiado. Al ejecutar los buildbots en varios sistemas diferentes,
puede asegurar que ns-3 compila y se ejecuta correctamente en todos sus sistemas compatibles.

Los usuarios (y desarrolladores) normalmente no interactuarán con el sistema buildbot más que para
lea sus mensajes con respecto a los resultados de las pruebas. Si se detecta una falla en uno de los
trabajos de compilación y prueba automatizados, el buildbot enviará un correo electrónico al ns-desarrolladores
lista de correo. Este correo electrónico se verá algo así como

En la URL de detalles completos que se muestra en el correo electrónico, se puede buscar la palabra clave fracasado y
seleccionar el stdio link del paso correspondiente para ver el motivo del fallo.

El buildbot hará su trabajo en silencio si no hay errores, y el sistema se someterá
construya y pruebe los ciclos todos los días para verificar que todo esté bien.

Prueba.py
Los buildbots usan un programa de Python, prueba.py, que es responsable de ejecutar todos los
pruebas y recopilar los informes resultantes en un formato legible por humanos. este programa es
también disponible para uso de usuarios y desarrolladores.

prueba.py es muy flexible al permitir al usuario especificar el número y tipo de pruebas a
correr; y también la cantidad y tipo de salida a generar.

Antes de correr prueba.py, asegúrese de que los ejemplos y las pruebas de ns3 se hayan creado haciendo
el siguiente

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

De forma predeterminada, prueba.py ejecutará todas las pruebas disponibles e informará el estado de manera muy concisa
forma. Ejecutando el comando

$ ./prueba.py

dará como resultado una serie de PASS, FALLO, CRASH or OMITIR indicaciones seguidas del tipo de
prueba que se ejecutó y su nombre para mostrar.

Waf: Ingresando al directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: dejando el directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'compilación' finalizada correctamente (0.939 s)
FALLA: TestSuite ns3-wifi-propagation-loss-models
APROBADO: TestSuite objeto-nombre-servicio
APROBADO: TestSuite pcap-archivo-objeto
APROBADO: TestSuite ns3-tcp-cwnd
...
APROBADO: TestSuite ns3-tcp-interoperabilidad
APROBADO: Ejemplo csma-broadcast
APROBADO: Ejemplo csma-multidifusión

Este modo está destinado a ser utilizado por usuarios interesados ​​en determinar si su
distribución está funcionando correctamente, y por los desarrolladores que están interesados ​​en determinar si
los cambios que han hecho han causado alguna regresión.

Hay una serie de opciones disponibles para controlar el comportamiento de prueba.py. si tu corres
prueba.py --ayuda deberías ver un resumen de comandos como:

Uso: test.py [opciones]

Opciones:
-h, --help mostrar este mensaje de ayuda y salir
-b RUTA DE CONSTRUCCIÓN, --ruta de compilación=RUTA DE CONSTRUCCIÓN
especifique la ruta donde se creó ns-3 (el valor predeterminado es
directorio de compilación para la variante actual)
-c TIPO, --restricción=TIPO
restringir al corredor de prueba por tipo de prueba
-e EJEMPLO, --ejemplo=EJEMPLO
especificar un solo ejemplo para ejecutar (no se indica ninguna ruta relativa)
necesario)
-g, --grind ejecutar los conjuntos de pruebas y ejemplos usando valgrind
-k, --kinds imprime los tipos de pruebas disponibles
-l, --list imprime la lista de pruebas conocidas
-m, --multiple informa múltiples fallas de conjuntos de pruebas y prueba
cases
-n, --nowaf no ejecuta waf antes de comenzar la prueba
-p PYEJEMPLO, --pyejemplo=PYEJEMPLO
especificar un solo ejemplo de python para ejecutar (con relativa
camino)
-r, --retain retiene todos los archivos temporales (que normalmente son
eliminado)
-s SUITE DE PRUEBA, --suite=SUITE DE PRUEBA
especificar un solo conjunto de pruebas para ejecutar
-t ARCHIVO-TEXTO, --text=ARCHIVO-TEXTO
escriba los resultados detallados de la prueba en TEXT-FILE.txt
-v, --verbose progreso de impresión y mensajes informativos
-w ARCHIVO-HTML, --web=ARCHIVO-HTML, --html=ARCHIVO-HTML
escriba los resultados detallados de la prueba en HTML-FILE.html
-x ARCHIVO-XML, --xml=ARCHIVO-XML
escribir resultados de prueba detallados en XML-FILE.xml

Si se especifica un estilo de salida opcional, se pueden generar descripciones detalladas del
pruebas y estado. Los estilos disponibles son texto y HTML. Los buildbots seleccionarán el HTML
opción para generar informes de prueba HTML para las compilaciones nocturnas usando

$ ./prueba.py --html=noche.html

En este caso, se crearía un archivo HTML llamado ''nightly.html'' con un bonito resumen
de las pruebas realizadas. Un formato ''legible por humanos'' está disponible para los usuarios interesados ​​en el
Detalles.

$ ./prueba.py --text=resultados.txt

En el ejemplo anterior, el conjunto de pruebas que verifica el ns-3 pérdida de propagación del dispositivo inalámbrico
fallaron los modelos. De forma predeterminada, no se proporciona más información.

Para explorar más a fondo la falla, prueba.py permite especificar un único conjunto de pruebas.
Ejecutando el comando

$ ./test.py --suite=ns3-modelos-de-pérdida-de-propagación-wifi

o equivalente

$ ./test.py -s ns3-wifi-propagación-los-modelos-de-pérdida

da como resultado que se ejecute ese único conjunto de pruebas.

FALLA: TestSuite ns3-wifi-propagation-loss-models

Para encontrar información detallada sobre la falla, se debe especificar el tipo de salida
deseado. Por ejemplo, la mayoría de la gente probablemente estará interesada en un archivo de texto:

$ ./test.py --suite=ns3-modelos-de-pérdida-de-propagación-wifi --text=results.txt

Esto dará como resultado que ese único conjunto de pruebas se ejecute con el estado de prueba escrito en el
archivo ''resultados.txt''.

Debería encontrar algo similar a lo siguiente en ese archivo

FALLO: Test Suite ''ns3-wifi-propagation-loss-models'' (real 0.02 usuario 0.01 sistema 0.00)
APROBADO: Caso de prueba "Verificar... Friis... modelo..." (real 0.01 usuario 0.00 sistema 0.00)
FALLA: Caso de prueba "Comprobar... Log Distancia... modelo" (real 0.01 usuario 0.01 sistema 0.00)
Detalles:
Mensaje: Obtuve un valor de SNR inesperado
Condición: [descripción larga de lo que realmente falló]
Real: 176.395
Límite: 176.407 +- 0.0005
Archivo: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Línea: 360

Tenga en cuenta que Test Suite se compone de dos casos de prueba. El primer caso de prueba comprobó la
Friis modelo de pérdida de propagación y aprobado. El segundo caso de prueba falló al verificar el registro
Modelo de propagación a distancia. En este caso, se encontró una SNR de 176.395 y la prueba
esperaba un valor de 176.407 correcto con tres decimales. El archivo que implementó
se enumera la prueba fallida, así como la línea de código que desencadenó la falla.

Si lo desea, podría haber escrito fácilmente un archivo HTML usando el --html opción
como se describió anteriormente.

Por lo general, un usuario ejecutará todas las pruebas al menos una vez después de la descarga ns-3 para asegurar eso
su entorno se ha construido correctamente y está generando resultados correctos
de acuerdo con las suites de prueba. Los desarrolladores normalmente ejecutarán las suites de prueba antes y
después de hacer un cambio para asegurarse de que no han introducido una regresión con su
cambios. En este caso, es posible que los desarrolladores no deseen ejecutar todas las pruebas, sino solo un subconjunto. Para
Por ejemplo, es posible que el desarrollador solo desee ejecutar las pruebas unitarias periódicamente mientras realiza
cambios en un repositorio. En este caso, prueba.py se le puede decir que restrinja los tipos de
pruebas que se ejecutan a una clase particular de pruebas. El siguiente comando dará como resultado solo
las pruebas unitarias que se están ejecutando:

$ ./test.py --constrain=unidad

De manera similar, el siguiente comando dará como resultado que solo se ejecuten las pruebas de humo de ejemplo:

$ ./test.py --constrain=unidad

Para ver una lista rápida de los tipos legales de restricciones, puede solicitar que se incluyan en la lista.
El siguiente comando

$ ./prueba.py --tipos

dará como resultado que se muestre la siguiente lista:

Waf: Ingresando al directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: dejando el directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'construir' terminó con éxito (0.939s)Waf: Ingresando al directorio '/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt: Pruebas de verificación de compilación (para ver si la compilación se completó correctamente)
core: ejecutar todas las pruebas basadas en TestSuite (excluir ejemplos)
ejemplo: ejemplos (para ver si los programas de ejemplo se ejecutan correctamente)
rendimiento: Pruebas de rendimiento (verifique si el sistema es tan rápido como se esperaba)
sistema: Pruebas del sistema (amplía los módulos para verificar la integración de los módulos)
unidad: Pruebas unitarias (dentro de los módulos para verificar la funcionalidad básica)

Cualquiera de estos tipos de prueba se puede proporcionar como una restricción usando el --restricción .

Para ver una lista rápida de todos los conjuntos de pruebas disponibles, puede solicitar que se
listado. El siguiente comando,

$ ./prueba.py --lista

dará como resultado una lista del conjunto de pruebas que se muestra, similar a

Waf: Ingresando al directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: dejando el directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'compilación' finalizada correctamente (0.939 s)
histograma
ns3-wifi-interferencia
ns3-tcp-cwnd
ns3-tcp-interoperabilidad
muestra
dispositivos-malla-llama
dispositivos-malla-dot11s
malla de dispositivos
...
servicio de nombre de objeto
llamar de vuelta
atributos
config
valor global
De línea de comandos
número aleatorio básico
objeto

Cualquiera de estos conjuntos enumerados se puede seleccionar para que se ejecute por sí mismo usando el --suite opción como
mostrado anteriormente.

De manera similar a las suites de prueba, uno puede ejecutar un solo programa de ejemplo de C++ usando el --ejemplo
opción. Tenga en cuenta que no es necesario incluir la ruta relativa para el ejemplo y que
los ejecutables creados para los ejemplos de C++ no tienen extensiones. Entrando

$ ./prueba.py --ejemplo=udp-echo

da como resultado que se ejecute ese único ejemplo.

APROBADO: ejemplos de ejemplo/udp/udp-echo

Puede especificar el directorio donde se construyó ns-3 usando el --Construir camino opción como
de la siguiente manera.

$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc

Uno puede ejecutar un solo programa de ejemplo de Python usando el --pyejemplo opción. Tenga en cuenta que el
Se debe incluir la ruta relativa para el ejemplo y que los ejemplos de Python necesitan su
extensiones Entrando

$ ./prueba.py --pyexample=ejemplos/tutorial/primero.py

da como resultado que se ejecute ese único ejemplo.

APROBADO: ejemplos de ejemplo/tutorial/first.py

Debido a que los ejemplos de Python no están construidos, no necesita especificar el directorio donde ns-3
fue construido para ejecutarlos.

Normalmente, cuando se ejecutan programas de ejemplo, escriben una gran cantidad de datos de archivos de rastreo.
Esto normalmente se guarda en el directorio base de la distribución (por ejemplo,
/inicio/usuario/ns-3-dev). Cuando prueba.py ejecuta un ejemplo, realmente es completamente despreocupado
con los archivos de seguimiento. Solo quiere determinar si el ejemplo se puede compilar y ejecutar.
sin error. Dado que este es el caso, los archivos de seguimiento se escriben en un
/tmp/rastros no verificados directorio. Si ejecuta el ejemplo anterior, debería poder encontrar
la asociada udp-echo.tr y udp-echo-n-1.pcap archivos allí.

La lista de ejemplos disponibles está definida por el contenido del directorio ''examples'' en
la distribución. Si selecciona un ejemplo para la ejecución utilizando el --ejemplo opción,
prueba.py no intentará decidir si el ejemplo ha sido configurado o no,
simplemente intentará ejecutarlo e informar el resultado del intento.

Cuándo prueba.py se ejecuta, por defecto primero se asegurará de que el sistema ha sido completamente
construido. Esto se puede anular seleccionando el --ahora .

$ ./prueba.py --lista --nowaf

dará como resultado que se muestre una lista de los conjuntos de pruebas construidos actualmente, similar a:

ns3-wifi-propagación-pérdida-modelos
ns3-tcp-cwnd
ns3-tcp-interoperabilidad
pcap-archivo-objeto
servicio de nombre de objeto
generadores de números aleatorios

Nótese la ausencia del Waf construir mensajes.

prueba.py también admite la ejecución de conjuntos de pruebas y ejemplos en valgrind. Valgrind es un
programa flexible para depurar y perfilar ejecutables de Linux. Por defecto, valgrind ejecuta
una herramienta llamada memcheck, que realiza una variedad de funciones de verificación de memoria, que incluyen
detección de accesos a la memoria no inicializada, uso indebido de la memoria asignada (dobles liberaciones,
acceso después de libre, etc.) y detección de pérdidas de memoria. Esto se puede seleccionar usando el
--moler .

$ ./prueba.py --grind

Mientras corre, prueba.py y los programas que ejecuta indirectamente, generan un gran número de
archivos temporales. Por lo general, el contenido de estos archivos no es interesante, sin embargo, en algunos
casos puede ser útil (con fines de depuración) para ver estos archivos. prueba.py proporciona una
--conservar opción que hará que estos archivos temporales se conserven después de que finalice la ejecución.
terminado. Los archivos se guardan en un directorio llamado testpy-salida bajo un subdirectorio
nombrado de acuerdo con el Tiempo Universal Coordinado actual (también conocido como Media de Greenwich)
Tiempo).

$ ./prueba.py --retener

Finalmente, prueba.py proporciona una --verboso opción que imprimirá grandes cantidades de información
sobre su progreso. No se espera que esto sea terriblemente útil a menos que haya
un error. En este caso, puede obtener acceso a la salida estándar y al error estándar
informado mediante la ejecución de conjuntos de pruebas y ejemplos. Seleccione detallado de la siguiente manera:

$ ./prueba.py --verbose

Todas estas opciones se pueden mezclar y combinar. Por ejemplo, para ejecutar todo el núcleo ns-3
suites de prueba bajo valgrind, en modo detallado, mientras genera un archivo de salida HTML, uno
haría:

$ ./test.py --verbose --grind --constrain=núcleo --html=resultados.html

Taxonomía de prueba
Como se mencionó anteriormente, las pruebas se agrupan en una serie de clasificaciones ampliamente definidas para
permitir a los usuarios ejecutar pruebas selectivamente para abordar los diferentes tipos de pruebas que necesitan
para acabar.

· Pruebas de verificación de compilación

· Pruebas unitarias

· Pruebas del sistema

· Ejemplos

· Pruebas de rendimiento

Pruebas de verificación de compilación
Estas son pruebas relativamente simples que se construyen junto con la distribución y se utilizan
para asegurarse de que la compilación funciona bastante bien. Nuestras pruebas unitarias actuales viven en el
archivos fuente del código que prueban y están integrados en los módulos ns-3; y así encajar el
descripción de los BVT. Los BVT viven en el mismo código fuente que está integrado en el código ns-3.
Nuestras pruebas actuales son ejemplos de este tipo de prueba.

Unidad Examenes
Las pruebas unitarias son pruebas más complicadas que entran en detalles para asegurarse de que una parte del código
funciona como se anuncia de forma aislada. Realmente no hay razón para que este tipo de prueba sea
integrado en un módulo ns-3. Resulta, por ejemplo, que la unidad prueba el objeto
servicio de nombres son aproximadamente del mismo tamaño que el propio código de servicio de nombres de objetos. Pruebas unitarias
son pruebas que verifican un solo bit de funcionalidad que no está integrado en el código ns-3,
pero vive en el mismo directorio que el código que prueba. Es posible que estas pruebas
verifique la integración de múltiples archivos de implementación en un módulo también. El archivo
src/core/test/names-test-suite.cc es un ejemplo de este tipo de prueba. El archivo
src/network/test/pcap-file-test-suite.cc es otro ejemplo que utiliza un buen pcap conocido
archivo como un archivo de vector de prueba. Este archivo se almacena localmente en el directorio src/network.

System Examenes
Las pruebas del sistema son aquellas que involucran más de un módulo en el sistema. tenemos muchos
este tipo de prueba se ejecuta en nuestro marco de regresión actual, pero por lo general son
ejemplos sobrecargados. Proporcionamos un nuevo lugar para este tipo de prueba en el directorio.
src / prueba. El archivo src/test/ns3tcp/ns3-interop-test-suite.cc es un ejemplo de este tipo
de prueba Utiliza NSC TCP para probar la implementación de ns-3 TCP. A menudo habrá prueba
vectores requeridos para este tipo de prueba, y se almacenan en el directorio donde se encuentra el
vidas de prueba. Por ejemplo, ns3tcp-interop-response-vectors.pcap es un archivo que consta de un
número de encabezados TCP que se utilizan como respuestas esperadas del ns-3 TCP bajo prueba
a un estímulo generado por el NSC TCP que se utiliza como una implementación ''bien conocida''.

Ejemplos
Los ejemplos son probados por el marco para asegurarse de que se construyeron y se ejecutarán. Nada es
marcado, y actualmente los archivos pcap solo se escriben en / Tmp para ser descartado. Si
los ejemplos se ejecutan (no fallan) pasan esta prueba de humo.

Performance Examenes
Las pruebas de desempeño son aquellas que ejercitan una parte particular del sistema y determinan
si las pruebas se han ejecutado hasta su finalización en un tiempo razonable.

Correr Examenes
Las pruebas normalmente se ejecutan utilizando el nivel alto prueba.py programa. Para obtener una lista de los
opciones de línea de comandos disponibles, ejecute prueba.py --ayuda

el programa de prueba prueba.py ejecutará ambas pruebas y aquellos ejemplos que se han agregado a
la lista a revisar. La diferencia entre pruebas y ejemplos es la siguiente. Pruebas
generalmente verifica que la salida o los eventos específicos de la simulación se ajusten al comportamiento esperado.
Por el contrario, la salida de los ejemplos no se comprueba y el programa de prueba simplemente comprueba la
salir del estado del programa de ejemplo para asegurarse de que se ejecuta sin errores.

Brevemente, para ejecutar todas las pruebas, primero se deben configurar las pruebas durante la etapa de configuración, y
también (opcionalmente) ejemplos si se van a comprobar los ejemplos:

$ ./waf --configure --habilitar-ejemplos --habilitar-pruebas

Luego, construya ns-3, y después de que esté construido, simplemente ejecute prueba.py. prueba.py -h mostrará un número
de opciones de configuración que modifican el comportamiento de test.py.

El programa prueba.py invoca, para pruebas y ejemplos de C++, un programa de C++ de nivel inferior llamado
corredor de pruebas para ejecutar realmente las pruebas. Como se discute a continuación, este corredor de pruebas puede ser una
forma útil de depurar pruebas.

Depuración Examenes
La depuración de los programas de prueba se realiza mejor ejecutando el corredor de prueba de bajo nivel
programa. El corredor de pruebas es el puente entre el código Python genérico y ns-3 código. Está
escrito en C++ y utiliza el proceso automático de detección de pruebas en el ns-3 código para encontrar y
permitir la ejecución de todas las diversas pruebas.

La principal razón por la que prueba.py no es adecuado para la depuración es que no está permitido para
registro para ser encendido usando el NS_LOG variable ambiental cuando se ejecuta test.py. Este
la limitación no se aplica al ejecutable del corredor de prueba. Por lo tanto, si desea ver el registro
resultado de sus pruebas, debe ejecutarlas usando el corredor de prueba directamente.

Para ejecutar el corredor de prueba, lo ejecuta como cualquier otro ejecutable ns-3, usando
waf. Para obtener una lista de las opciones disponibles, puede escribir:

$ ./waf --ejecutar "test-runner --help"

Deberías ver algo como lo siguiente

Waf: Ingresando al directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: dejando el directorio `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
'compilación' finalizada correctamente (0.353 s)
--assert: Dile a las pruebas que segfault (como aseverar) si se detecta un error
--basedir=dir: establece el directorio base (dónde encontrar src) en ''dir''
--tempdir=dir: establece el directorio temporal (donde encontrar archivos de datos) en ''dir''
--constrain=test-type: Restringe las comprobaciones para probar conjuntos de tipo ''test-type''
--ayuda: Imprime este mensaje
--kinds: enumera todos los tipos de pruebas disponibles
--list: enumera todos los conjuntos de pruebas (opcionalmente restringido por tipo de prueba)
--out=nombre-de-archivo: Establecer el archivo de salida de estado de prueba en ''nombre-de-archivo''
--suite=nombre-del-conjunto: Ejecute el conjunto de pruebas llamado ''nombre-del-conjunto''
--verbose: activa los mensajes en los conjuntos de pruebas de ejecución

Hay una serie de cosas disponibles para usted que le resultarán familiares si tiene
miró prueba.py. Esto debería esperarse ya que el corredor de pruebas es solo una interfaz
entre prueba.py y ns-3. Puede notar que faltan comandos relacionados con ejemplos aquí.
Eso es porque los ejemplos realmente no son ns-3 pruebas. prueba.py los ejecuta como si fueran
para presentar un entorno de prueba unificado, pero en realidad son completamente diferentes y no
para ser encontrado aquí.

La primera opción nueva que aparece aquí, pero no en test.py es la --afirmar opción. Esta
La opción es útil al depurar un caso de prueba cuando se ejecuta bajo un depurador como gdb. Cuando el
seleccionada, esta opción le dice al caso de prueba subyacente que provoque una violación de segmentación si
se detecta un error. Esto tiene el agradable efecto secundario de hacer que la ejecución del programa se detenga.
(irrumpir en el depurador) cuando se detecta un error. Si está usando gdb, podría usar
esta opción algo así como,

$ ./cáscara waf
$ cd compilación/depuración/utilidades
$ gdb corredor de pruebas
$ ejecutar --suite=valor-global --assert

Si luego se encuentra un error en el conjunto de pruebas de valor global, se generaría un error de segmento
y el depurador (nivel de origen) se detendría en el NS_TEST_ASSERT_MSG que detectó la
error.

Otra nueva opción que aparece aquí es la --basado opción. resulta que algunos
las pruebas pueden necesitar hacer referencia al directorio de origen del ns-3 distribución para encontrar local
datos, por lo que siempre se requiere un directorio base para ejecutar una prueba.

Si ejecuta una prueba desde test.py, el programa Python proporcionará la opción basedir para
tú. Para ejecutar una de las pruebas directamente desde el corredor de pruebas usando waf, necesitaras
especifique el conjunto de pruebas para ejecutar junto con el directorio base. Entonces podrías usar el caparazón
y hacer:

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"

Tenga en cuenta las comillas ''hacia atrás'' en el pwd mando.

Si está ejecutando el conjunto de pruebas desde un depurador, puede ser bastante doloroso recordar
y escriba constantemente la ruta absoluta del directorio base de distribución. Porque
esto, si omite el nombre de base, el corredor de pruebas intentará encontrar uno para usted. Eso
comienza en el directorio de trabajo actual y sube por el árbol de directorios en busca de un
archivo de directorio con archivos llamados VERSION y LICENCIA. Si encuentra uno, asume que
debe ser el basedir y lo proporciona para usted.

Prueba salida
Muchas suites de prueba necesitan escribir archivos temporales (como archivos pcap) en el proceso de
ejecutando las pruebas. Luego, las pruebas necesitan un directorio temporal para escribir. el pitón
La utilidad de prueba (test.py) proporcionará un archivo temporal automáticamente, pero si se ejecuta de forma independiente
se debe proporcionar este directorio temporal. Al igual que en el caso de basedir, puede ser
molesto tener que proporcionar continuamente un --tempdir, por lo que el corredor de prueba calculará uno
por usted si no proporciona uno. Primero busca variables de entorno nombradas TMP
y TEMP y los usa. si ninguno TMP ni TEMP se definen recoge / Tmp. El código
luego agrega un identificador que indica qué creó el directorio (ns-3) y luego la hora
(hh.mm.ss) seguido de un gran número aleatorio. El corredor de prueba crea un directorio de ese
nombre que se utilizará como directorio temporal. Los archivos temporales luego van a un directorio que
se llamará algo como

/tmp/ns-3.10.25.37.61537845

El tiempo se proporciona como una pista para que pueda reconstruir con relativa facilidad lo que
se usó el directorio si necesita regresar y mirar los archivos que se colocaron en ese
directorio.

Otra clase de salida es la salida de prueba, como las trazas de pcap que se generan para compararlas con
salida de referencia El programa de prueba generalmente los eliminará después de que todos los conjuntos de prueba
correr. Para deshabilitar la eliminación de la salida de prueba, ejecute prueba.py con la opción "retener":

$ ./prueba.py -r

y la salida de prueba se puede encontrar en el testpy-salida/ directorio.

Informes of test fallas
Cuando ejecuta un conjunto de pruebas con el ejecutor de pruebas, ejecutará la prueba en silencio de forma predeterminada.
La única indicación de que obtendrá que la prueba pasó es la ausencia de un mensaje
obtenidos de waf diciendo que el programa devolvió algo más que un código de salida cero. Llegar
algún resultado de la prueba, debe especificar un archivo de salida al que se aplicarán las pruebas.
escribir su estado XML usando el --fuera opción. Debe tener cuidado al interpretar el
resultados porque los conjuntos de pruebas anexar resultados en este archivo. Probar,

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"

Si miras el archivo miarchivo.xml deberías ver algo como,


pcap-archivo-objeto

Verifique que PcapFile::Open with mode ''w'' funcione
PASAR
real 0.00 usuario 0.00 sistema 0.00


Verifique que PcapFile::Open with mode ''r'' funcione
PASAR
real 0.00 usuario 0.00 sistema 0.00


Verifique que PcapFile::Open with mode ''a'' funcione
PASAR
real 0.00 usuario 0.00 sistema 0.00


Verifique que PcapFileHeader se administre correctamente
PASAR
real 0.00 usuario 0.00 sistema 0.00


Verifique que PcapRecordHeader se administre correctamente
PASAR
real 0.00 usuario 0.00 sistema 0.00


Verifique que PcapFile pueda leer un archivo pcap bueno conocido
PASAR
real 0.00 usuario 0.00 sistema 0.00

PASAR
real 0.00 usuario 0.00 sistema 0.00


Si está familiarizado con XML, esto debería explicarse por sí mismo. tampoco es un
archivo XML completo ya que las suites de prueba están diseñadas para tener su salida adjunta a un maestro
archivo de estado XML como se describe en el prueba.py .

Depuración test suite fallas
Para depurar fallos de prueba, como

ACCIDENTE: TestSuite ns3-wifi-interferencia

Puede acceder al programa de ejecución de pruebas subyacente a través de gdb de la siguiente manera y luego pasar el
argumento "--basedir=`pwd`" para ejecutar (también puede pasar otros argumentos según sea necesario, pero
basedir es el mínimo necesario):

$ ./waf --command-template="gdb %s" --ejecutar "test-runner"
Waf: Ingresando al directorio `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf: Dejando el directorio `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
'compilación' finalizada correctamente (0.380 s)
GNU gdb 6.8-debian
Copyright (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+: GNU GPL versión 3 o posteriorhttp://gnu.org/licenses/gpl.html>
Este es un software gratuito: puede cambiarlo y redistribuirlo.
NO HAY GARANTÍA, en la medida permitida por la ley. Escriba "Mostrar copia"
y "mostrar garantía" para más detalles.
Este GDB se configuró como "x86_64-linux-gnu"...
(gdb) r --basedir=`pwd`
Programa de inicio: <..>/build/debug/utils/test-runner --basedir=`pwd`
[Depuración de subprocesos con libthread_db habilitado]
afirmación fallida. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...

Aquí hay otro ejemplo de cómo usar valgrind para depurar un problema de memoria como:

VALGR: TestSuite dispositivos-malla-punto11s-regresión

$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --ejecutar test-runner

Clase Corredor de pruebas
Los ejecutables que ejecutan programas de prueba dedicados usan una clase TestRunner. Esta clase
proporciona el registro y listado automático de pruebas, así como una forma de ejecutar la
pruebas individuales. Las suites de prueba individuales usan constructores globales de C++ para agregarse a
una colección de suites de prueba administradas por el corredor de prueba. El corredor de prueba se utiliza para enumerar
todas las pruebas disponibles y seleccionar una prueba para ejecutar. Esta es una clase bastante simple.
que proporciona tres métodos estáticos para proporcionar o Agregar y obtener conjuntos de pruebas a un
colección de pruebas. Ver el doxygen para la clase ns3::PruebaRunner para obtener más detalles.

Prueba Suite
Todo ns-3 Las pruebas se clasifican en conjuntos de pruebas y casos de prueba. Un conjunto de pruebas es un
colección de casos de prueba que ejercitan completamente un tipo dado de funcionalidad. Como
descrito anteriormente, los conjuntos de pruebas se pueden clasificar como,

· Pruebas de verificación de compilación

· Pruebas unitarias

· Pruebas del sistema

· Ejemplos

· Pruebas de rendimiento

Esta clasificación se exporta desde la clase TestSuite. Esta clase es bastante simple,
existiendo solo como un lugar para exportar este tipo y acumular casos de prueba. de un usuario
perspectiva, para crear un nuevo TestSuite en el sistema solo hay que definir un nuevo
clase que hereda de clase Banco de pruebas y realizar estos dos deberes.

El siguiente código definirá una nueva clase que puede ser ejecutada por prueba.py como una prueba de ''unidad''
con el nombre para mostrar, nombre-de-mi-suite-de-pruebas.

clase MySuite: TestSuite público
{
pública:
Mi Suite de Pruebas ();
};

MiTestSuite::MiTestSuite ()
: TestSuite ("mi-nombre-del-conjunto-de-pruebas", UNIT)
{
AddTestCase (nuevo MyTestCase);
}

MiTestSuite miTestSuite;

La clase base se encarga de todo el registro y los informes necesarios para ser una buena
ciudadano en el marco de la prueba.

Prueba Caso
Las pruebas individuales se crean utilizando una clase TestCase. Modelos comunes para el uso de una prueba
caso incluyen "un caso de prueba por función" y "un caso de prueba por método". mezclas de
estos modelos pueden ser utilizados.

Para crear un nuevo caso de prueba en el sistema, todo lo que uno tiene que hacer es heredar del
Caso de prueba clase base, anule el constructor para dar un nombre al caso de prueba y anule
los DoRun método para ejecutar la prueba.

clase MyTestCase: TestCase público
{
MiCasoDePrueba ();
vacío virtual DoRun (vacío);
};

MiCasoDePrueba::MiCasoDePrueba ()
: TestCase ("Comprobar un poco de funcionalidad")
{
}

vacío
MyTestCase::DoRun (vacío)
{
NS_TEST_ASSERT_MSG_EQ (verdadero, verdadero, "Algún mensaje de error");
}

Departamento de Servicios Públicos
Hay una serie de utilidades de varios tipos que también forman parte de las pruebas.
estructura. Los ejemplos incluyen un archivo pcap generalizado útil para almacenar vectores de prueba; a
contenedor genérico útil para el almacenamiento transitorio de vectores de prueba durante la ejecución de la prueba; y
herramientas para generar presentaciones basadas en resultados de pruebas de validación y verificación.

Estas utilidades no están documentadas aquí, pero, por ejemplo, vea cómo prueba TCP
encontrado en src/prueba/ns3tcp/ use archivos pcap y salida de referencia.

Cómo a escribir pruebas
Un objetivo principal del proyecto ns-3 es ayudar a los usuarios a mejorar la validez y
credibilidad de sus resultados. Hay muchos elementos para obtener modelos válidos y
simulaciones y pruebas es un componente importante. Si aportas modelos o ejemplos a
ns-3, se le puede pedir que contribuya con el código de prueba. Se utilizarán los modelos que aportes
durante muchos años por otras personas, que probablemente no tienen idea a primera vista si el
el modelo es correcto El código de prueba que escriba para su modelo ayudará a evitar futuras
regresiones en la salida y ayudará a los futuros usuarios a comprender la verificación y
límites de aplicabilidad de sus modelos.

Hay muchas formas de verificar la corrección de la implementación de un modelo. En esto
sección, esperamos cubrir algunos casos comunes que pueden usarse como guía para escribir nuevos
pruebas.

Muestra Banco de pruebas esqueleto
Al comenzar desde cero (es decir, no agregar un TestCase a un TestSuite existente), estos
las cosas deben decidirse por adelantado:

· Cómo se llamará el conjunto de pruebas

· Qué tipo de prueba será (prueba de verificación de compilación, prueba unitaria, prueba del sistema o
Prueba de rendimiento)

· Dónde residirá el código de prueba (ya sea en un módulo ns-3 existente o por separado en
directorio src/prueba/). Tendrá que editar el archivo wscript en ese directorio para
compila tu nuevo código, si es un archivo nuevo.

Un programa llamado src/create-module.py es un buen punto de partida. Este programa puede ser
invocado como crear-modulo.py Router para un hipotético nuevo módulo llamado Router. Una vez
haces esto, verás un Router directorio, y un prueba/router-prueba-suite.cc Banco de pruebas.
Este archivo puede ser un punto de partida para su prueba inicial. Este es un conjunto de pruebas de trabajo,
aunque las pruebas reales realizadas son triviales. Cópielo en la prueba de su módulo
directorio, y haga una sustitución global de "Router" en ese archivo por algo relacionado
al modelo que desea probar. También puede editar cosas como una más descriptiva
nombre del caso de prueba.

También debe agregar un bloque en su wscript para compilar esta prueba:

módulo_prueba.fuente = [
'prueba/enrutador-prueba-suite.cc',
]

Antes de comenzar a hacer que esto haga cosas útiles, puede ser útil intentar ejecutar el
esqueleto. Asegúrese de que ns-3 se haya configurado con la opción "--enable-tests".
Supongamos que su nuevo conjunto de pruebas se llama "enrutador", como aquí:

EnrutadorTestSuite::EnrutadorTestSuite ()
: TestSuite ("enrutador", UNIDAD)

Prueba este comando:

$ ./test.py -s enrutador

Se debe producir una salida como la siguiente:

APROBADO: enrutador TestSuite
1 de 1 pruebas aprobadas (1 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Consulte src/lte/test/test-lte-antenna.cc para ver un ejemplo práctico.

Prueba macros
Hay una serie de macros disponibles para verificar la salida del programa de prueba con el esperado
producción. Estas macros se definen en src/núcleo/modelo/prueba.h.

El conjunto principal de macros que se utilizan incluye lo siguiente:

NS_TEST_ASSERT_MSG_EQ(real, límite, mensaje)
NS_TEST_ASSERT_MSG_NE(real, límite, mensaje)
NS_TEST_ASSERT_MSG_LT(real, límite, mensaje)
NS_TEST_ASSERT_MSG_GT(real, límite, mensaje)
NS_TEST_ASSERT_MSG_EQ_TOL(real, límite, tol, mensaje)

El primer argumento real es el valor bajo prueba, el segundo valor límitar es lo esperado
valor (o el valor contra el que se va a realizar la prueba) y el último argumento MSG es el mensaje de error para
imprimir si la prueba falla.

Las primeras cuatro macros anteriores prueban la igualdad, la desigualdad, menor que o mayor que,
respectivamente. La quinta macro anterior prueba la igualdad, pero dentro de cierta tolerancia.
Esta variante es útil cuando se prueba la igualdad de números de punto flotante frente a un límite,
donde desea evitar una falla de prueba debido a errores de redondeo.

Finalmente, existen variantes de las anteriores donde la palabra clave AFIRMAR es reemplazado por ESPERAR.
Estas variantes están diseñadas especialmente para su uso en métodos (especialmente devoluciones de llamada) que devuelven
vacío. Reserve su uso para las devoluciones de llamada que utilice en sus programas de prueba; de lo contrario, utilice
los AFIRMAR variantes.

Cómo a add an (aqui) programa a los test suite
Uno puede "probar con humo" que los ejemplos se compilan y ejecutan con éxito hasta su finalización (sin
fugas de memoria) usando el ejemplos-para-ejecutar.py script ubicado en el directorio de prueba de su módulo.
Brevemente, al incluir una instancia de este archivo en su directorio de prueba, puede hacer que el
corredor de prueba para ejecutar los ejemplos enumerados. Por lo general, es mejor asegurarse de que usted
seleccione ejemplos que tengan tiempos de ejecución razonablemente cortos para no atascar las pruebas. Ver
el ejemplo en src/lte/prueba/ directorio.

Pruebas for booleano resultados
Pruebas resultados when aleatoriedad is involucra
Pruebas salida datos en contra a known
Proporcionar no trivial Las opciones de entrada vectores of datos
Almacenamiento y referenciando no trivial salida datos
presentaciones a tu manera salida test datos
Soporte
Creamos a new ns-3 modelo
Este capítulo recorre el proceso de diseño de un ns-3 modelo. En muchos casos de investigación,
Los usuarios no estarán satisfechos con simplemente adaptar los modelos existentes, sino que querrán ampliar la
núcleo del simulador de una manera novedosa. Usaremos el ejemplo de agregar un ErrorModel a un
simples ns-3 enlace como un ejemplo motivador de cómo uno podría abordar este problema y
proceder a través de un diseño e implementación.

NOTA:
Documentación

Aquí nos enfocamos en el proceso de creación de nuevos modelos y nuevos módulos, y algunos de los
opciones de diseño involucradas. En aras de la claridad, aplazamos la discusión de la mecánica
de documentar modelos y código fuente para el Documentación .

Design Un nuevo enfoque
Considere cómo quiere que funcione; que deberia hacer Piensa en estas cosas:

· funcionalidad: ¿Qué funcionalidad debería tener? ¿Qué atributos o configuración es
expuesto al usuario?

· reutilización: ¿Cuánto deberían poder reutilizar otros mi diseño? ¿Puedo reutilizar el código de
ns-2 ¿Para empezar? ¿Cómo integra un usuario el modelo con el resto de otro?
¿simulación?

· dependencias: ¿Cómo puedo reducir la introducción de dependencias externas en mi nuevo código?
tanto como sea posible (para hacerlo más modular)? Por ejemplo, ¿debería evitar cualquier
dependencia de IPv4 si quiero que también sea utilizada por IPv6? ¿Debo evitar cualquier dependencia?
en IP en absoluto?

No dude en ponerse en contacto con el ns-3-usuarios or ns-desarrolladores lista si tiene preguntas.
En particular, es importante pensar en la API pública de su nuevo modelo y solicitar
retroalimentación. También ayuda a que otros conozcan su trabajo en caso de que esté interesado en
colaboradores.

Ejemplo: Modelo de error
Existe un modelo de error en ns-2. Permite pasar paquetes a un objeto con estado que
determina, basándose en una variable aleatoria, si el paquete está dañado. La persona que llama puede
luego decida qué hacer con el paquete (soltarlo, etc.).

La API principal del modelo de error es una función para pasar un paquete y el valor de retorno de
esta función es un booleano que le dice a la persona que llama si ocurrió algún daño. Nota
que dependiendo del modelo de error, el búfer de datos del paquete puede o no estar dañado.
Llamemos a esta función "IsCorrupt()".

Hasta ahora, en nuestro diseño, tenemos:

clase ErrorModel
{
pública:
/ **
* \devuelve verdadero si el paquete debe considerarse con error/corrupto
* \param pkt Paquete al que aplicar el modelo de error
*/
bool está corrupto (Ptr paquete);
};

Tenga en cuenta que no pasamos un puntero const, lo que permite que la función modifique el
paquete si IsCorrupt() devuelve verdadero. No todos los modelos de error modificarán realmente el paquete;
se debe documentar si el búfer de datos del paquete está dañado o no.

También podemos querer versiones especializadas de esto, como en ns-2, así que aunque no es el
única opción de diseño para polimorfismo, asumimos que subclasificaremos una clase base
ErrorModel para clases especializadas, como RateErrorModel, ListErrorModel, etc., como
está hecho en ns-2.

Puede que esté pensando en este punto, "¿Por qué no hacer de IsCorrupt() un método virtual?". Eso es
una aproximación; la otra es hacer que la función pública no virtual sea indirecta a través de una
función virtual privada (esto en C++ se conoce como el lenguaje de interfaz no virtual y es
adoptado en el ns-3 clase ErrorModel).

A continuación, ¿este dispositivo debería tener alguna dependencia de IP u otros protocolos? No queremos
para crear dependencias en los protocolos de Internet (el modelo de error debe ser aplicable a
protocolos que no son de Internet), por lo que lo tendremos en cuenta más adelante.

Otra consideración es cómo los objetos incluirán este modelo de error. Nos imaginamos poner
un setter explícito en ciertas implementaciones de NetDevice, por ejemplo:

/ **
* Adjunte un modelo de error de recepción al PointToPointNetDevice.
*
* PointToPointNetDevice puede incluir opcionalmente un ErrorModel en
* la cadena de recepción de paquetes.
*
* @ver modelo de error
* @param em Ptr para ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr em);

Nuevamente, esta no es la única opción que tenemos (los modelos de error podrían agregarse a muchos
otros objetos), pero satisface nuestro caso de uso principal, que es permitir que un usuario fuerce
errores en transmisiones de paquetes exitosas, a nivel de NetDevice.

Después de pensar un poco y mirar los existentes ns-2 código, aquí hay una API de muestra de una base
clase y primera subclase que podría publicarse para revisión inicial:

clase ErrorModel
{
pública:
ModeloError ();
~ModeloError virtual ();
bool está corrupto (Ptr paquete);
anular Restablecer (anular);
anular Habilitar (anular);
vacío Desactivar (vacío);
bool IsEnabled (vacío) const;
privada:
bool virtual DoCorrupt (Ptr paquete) = 0;
vacío virtual DoReset (vacío) = 0;
};

unidad de error de enumeración
{
UE_BIT,
UE_BYTE,
UE_PKT
};

// Determinar qué paquetes tienen errores correspondientes a un subyacente
// distribución de variables aleatorias, tasa de error y unidad para la tasa.
clase RateErrorModel: public ErrorModel
{
pública:
TasaErrorModelo ();
~RateErrorModel virtual ();
enumeración ErrorUnit GetUnit (void) const;
void SetUnit (enumeración ErrorUnit error_unit);
doble GetRate (vacío) const;
void SetRate (tarifa doble);
void SetRandomVariable (const RandomVariable &ranvar);
privada:
bool virtual DoCorrupt (Ptr paquete);
vacío virtual DoReset (vacío);
};

Andamio
Digamos que está listo para comenzar a implementar; tienes una imagen bastante clara de
lo que desea construir, y es posible que haya solicitado alguna revisión inicial o sugerencias de
la lista. Una forma de abordar el siguiente paso (implementación) es crear un andamiaje y
rellene los detalles a medida que el diseño madure.

Esta sección recorre muchos de los pasos que debe considerar para definir scaffolding, o
un esqueleto no funcional de lo que eventualmente implementará su modelo. suele ser bueno
Practique no esperar a que estos detalles se integren al final, sino a sondear un
esqueleto de su modelo en el sistema temprano y luego agregue funciones más tarde una vez que la API y
la integración parece correcta.

Tenga en cuenta que querrá modificar algunas cosas en la presentación a continuación para su modelo
ya que si sigue el modelo de error al pie de la letra, el código que produzca chocará con el
modelo de error existente. El siguiente es solo un resumen de cómo se construyó ErrorModel que usted
Se puede adaptar a otros modelos.

Reseña los ns-3 Codificación Estilo Comparación de
En este punto, es posible que desee hacer una pausa y leer el ns-3 documento de estilo de codificación, especialmente
si está considerando contribuir con su código al proyecto. El estilo de codificación
el documento está vinculado a la página principal del proyecto: ns-3 codificación style.

Decidir Dónde in los Fuente Árbol los Modelo Debería Residir
Todas las ns-3 el código fuente del modelo está en el directorio src /. Tendrás que elegir cuál
subdirectorio en el que reside. Si se trata de código de modelo nuevo de algún tipo, tiene sentido ponerlo
en el src / directorio en algún lugar, particularmente para facilitar la integración con la compilación
.

En el caso del modelo de error, está muy relacionado con la clase de paquete, por lo que tiene sentido
implementar esto en el src/red/ módulo donde ns-3 se implementan los paquetes.

waf y wscript
ns-3 utiliza el Waf sistema de construcción. Querrás integrar tu nuevo ns-3 usa el waf
sistema de construcción. Querrá integrar sus nuevos archivos fuente en este sistema. Este
requiere que agregue sus archivos a la wscript archivo que se encuentra en cada directorio.

Comencemos con los archivos vacíos error-model.h y error-model.cc, y agreguemos esto a
src/red/wscript. En realidad, solo es cuestión de agregar el archivo .cc al resto del
archivos de origen y el archivo .h a la lista de archivos de encabezado.

Ahora, acceda al directorio de nivel superior y escriba "./test.py". No deberías haberte roto
nada por esta operación.

Incluir Guardias
A continuación, agreguemos algunos incluir guardias en nuestro archivo de cabecera.:

#ifndef ERROR_MODEL_H
#definir ERROR_MODEL_H
...
#terminara si

espacio de nombres ns3
ns-3 utiliza el ns-3 espacio de nombres para aislar sus símbolos de otros espacios de nombres. Típicamente, un
el usuario pondrá a continuación un ns-3 bloque de espacio de nombres en el archivo cc y h.:

espacio de nombres ns3 {
...
}

En este punto, tenemos algunos archivos básicos en los que podemos comenzar a definir nuestras nuevas clases.
El archivo de encabezado tiene este aspecto:

#ifndef ERROR_MODEL_H
#definir ERROR_MODEL_H

espacio de nombres ns3 {

} // espacio de nombres ns3
#terminara si

mientras que el error-modelo.cc archivo simplemente se ve así:

#include "modelo-error.h"

espacio de nombres ns3 {

} // espacio de nombres ns3

Estos archivos deberían compilarse ya que en realidad no tienen ningún contenido. Ahora estamos listos para
empezar a añadir clases.

Inicial Implementación
En este punto, todavía estamos trabajando en algunos andamios, pero podemos comenzar a definir nuestro
clases, con la funcionalidad que se agregará más adelante.

Heredar obtenidos de los Objeto ¿Clase?
Este es un paso de diseño importante; si usar la clase Objeto como clase base para su nueva
clases.

Como se describe en el capítulo sobre el ns-3 Modelo de objeto, clases que heredan de la clase.
Objeto obtener propiedades especiales:

· los ns-3 tipo y sistema de atributos (ver Atributos)

· Un sistema de agregación de objetos

· un sistema de conteo de referencia de puntero inteligente (clase Ptr)

Clases que derivan de class Base de objetos} obtener las dos primeras propiedades anteriores, pero no
obtener punteros inteligentes. Clases que derivan de class BaseRecuentoRef obtener solo el puntero inteligente
sistema de conteo de referencia.

En la práctica, la clase Objeto es la variante de las tres anteriores que el ns-3 el desarrollador lo hará
encuentro más común.

En nuestro caso, queremos hacer uso del sistema de atributos, y pasaremos instancias
de este objeto a través del ns-3 API pública, por lo que la clase Objeto es apropiado para nosotros.

Inicial Clases
Una forma de proceder es comenzar definiendo las funciones mínimas y ver si funcionan.
compilar. Revisemos todo lo que se necesita implementar cuando derivamos de la clase Object.:

#ifndef ERROR_MODEL_H
#definir ERROR_MODEL_H

#include "ns3/objeto.h"

espacio de nombres ns3 {

clase ErrorModel: objeto público
{
pública:
TypeId estático GetTypeId (vacío);

ModeloError ();
~ModeloError virtual ();
};

clase RateErrorModel: public ErrorModel
{
pública:
TypeId estático GetTypeId (vacío);

TasaErrorModelo ();
~RateErrorModel virtual ();
};
#terminara si

Algunas cosas a tener en cuenta aquí. Necesitamos incluir objeto.h. La convención en ns-3 es que si
el archivo de encabezado se encuentra en el mismo directorio, puede incluirse sin ninguna ruta
prefijo. Por lo tanto, si estuviéramos implementando ErrorModel en origen/núcleo/modelo directorio, nosotros
podría haber dicho "#incluir "objeto.h"". Pero estamos en src/red/modelo, entonces debemos
incluirlo como "#incluir "ns3/objeto.h"". Tenga en cuenta también que esto va fuera del espacio de nombres
declaración.

En segundo lugar, cada clase debe implementar una función miembro pública estática llamada ObtenerIdTipo (vacío).

En tercer lugar, es una buena idea implementar constructores y destructores en lugar de dejar que el
compilador generarlos, y hacer que el destructor sea virtual. En C++, tenga en cuenta también que copiar
el operador de asignación y los constructores de copia se generan automáticamente si no están definidos, por lo que
si no los quiere, debe implementarlos como miembros privados. Este aspecto de
C++ se trata en el libro de C++ eficaz de Scott Meyers. artículo 45.

Veamos ahora un código de implementación esquelético correspondiente en el archivo .cc:

#include "modelo-error.h"

espacio de nombres ns3 {

NS_OBJECT_ENSURE_REGISTERED (Modelo de error);

TypeId ErrorModel::GetTypeId (vacío)
{
TypeId estático tid = TypeId ("ns3::ErrorModel")
.SetParent ()
;
volver tid;
}

ModeloError::ModeloError ()
{
}

ModeloError::~ModeloError ()
{
}

NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);

TypeId RateErrorModel::GetTypeId (vacío)
{
TypeId estático tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
;
volver tid;
}

RateErrorModel::RateErrorModel ()
{
}

Modelo de error de tasa::~Modelo de error de tasa ()
{
}

¿Qué es el ObtenerIdTipo (vacío) ¿función? Esta función hace algunas cosas. registra un
cadena única en el sistema TypeId. Establece la jerarquía de los objetos en el
sistema de atributos (a través de Padre de familia). También declara que ciertos objetos se pueden crear a través de
el marco de creación de objetos (AñadirConstructor).

La macro NS_OBJECT_ENSURE_REGISTERED (nombre de la clase) se necesita también una vez para cada clase que
define un nuevo método GetTypeId y realiza el registro real de la clase en el
sistema. El capítulo Modelo de objetos analiza esto con más detalle.

Incluye Externo archivos
Inicio de sesión Soporte
Aquí, escribir a bit del la adición de |ns3| registro macros Nota que LOG_COMPONENT_DEFINE is
done outside los espacio de nombres ns3

Constructor, Vacío Función prototipos
Clave Variables (Defecto Valores, atributos)
Prueba Programa 1
Objeto Marco conceptual
Adición a Muestra Guión
En este punto, es posible que desee intentar tomar el andamiaje básico definido anteriormente y agregarlo
en el sistema. Realizar este paso ahora permite usar un modelo más simple al realizar la plomería
en el sistema y también puede revelar si es necesario realizar modificaciones de diseño o API.
hecha. Una vez hecho esto, volveremos a construir la funcionalidad del
ErrorModels ellos mismos.

Añadir el archivo Basic Soporte in los Clase
/* dispositivo-de-red-punto-a-punto.h */
clase ModeloError;

/ **
* Modelo de error para recibir eventos de paquetes
*/
ptr m_receiveErrorModel;

Añadir el archivo Accesor
vacío
PointToPointNetDevice::SetReceiveErrorModel (Ptr em)
{
NS_LOG_FUNCTION (este << em);
m_receiveErrorModel = em;
}

.AddAttribute ("ReceiveErrorModel",
"El modelo de error del receptor utilizado para simular la pérdida de paquetes",
Valor del puntero (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
HacerPointerChecker ())

plomada Dentro los System
void PointToPointNetDevice::Receive (Ptr paquete)
{
NS_LOG_FUNCTION (este << paquete);
protocolo uint16_t = 0;

if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
{
//
// Si tenemos un modelo de error e indica que es hora de perder un
// paquete corrupto, no reenvíe este paquete, déjelo ir.
//
m_dropTrace (paquete);
}
más
{
//
// Pulse el gancho de seguimiento de recepción, elimine el encabezado del protocolo punto a punto
// y reenviar este paquete a la pila de protocolos.
//
m_rxTrace (paquete);
ProcessHeader(paquete, protocolo);
m_rxCallback (este, paquete, protocolo, GetRemote ());
si (!m_promiscCallback.IsNull ())
{ m_promiscCallback (este, paquete, protocolo, GetRemote()),
GetAddress(), NetDevice::PACKET_HOST);
}
}
}

Crea Nulo Funcional Guión
/* modelo-de-error-simple.cc */

// modelo de error
// Queremos agregar un modelo de error al NetDevice del nodo 3
// Podemos obtener un identificador de NetDevice a través del canal y el nodo
// punteros
ptr nd3 = PointToPointTopology::GetNetDevice
(n3, canal2);
ptr em = Crear ();
nd3->SetReceiveErrorModel (em);

bool
ErrorModel::DoCorrupt (Paquete&p)
{
NS_LOG_FUNCIÓN;
NS_LOG_UNCOND("¡Corrupto!");
falso retorno;
}

En este punto, podemos ejecutar el programa con nuestro modelo de error trivial conectado a la recepción
ruta del PointToPointNetDevice. Imprime la cadena "¡Corrupto!" por cada paquete
recibido en el nodo n3. A continuación, volvemos al modelo de error para agregar una subclase que realiza
modelado de errores más interesante.

Añadir el archivo a Subclase
La clase base trivial ErrorModel no hace nada interesante, pero proporciona una
interfaz de clase base útil (Dañado () y Restablecer ()), reenviado a funciones virtuales que
puede ser subclasificado. A continuación, consideremos lo que llamamos un BasicErrorModel que se basa en
los ns-2 Clase ErrorModel (en ns-2/cola/modelo de error.{cc,h}).

¿Qué propiedades queremos que tenga, desde la perspectiva de la interfaz de usuario? Nos gustaría
para que el usuario pueda intercambiar trivialmente el tipo de ErrorModel utilizado en el
Dispositivo de red. También nos gustaría la capacidad de establecer parámetros configurables.

Aquí hay algunos requisitos simples que consideraremos:

· Capacidad para establecer la variable aleatoria que gobierna las pérdidas (por defecto es UniformVariable)

· Habilidad para establecer la unidad (bit, byte, paquete, tiempo) de granularidad sobre la cual se detectan los errores
aplicado.

· Capacidad para establecer la tasa de errores (por ejemplo, 10 ^ -3) correspondiente a la unidad anterior de
granularidad

· Capacidad para habilitar/deshabilitar (el valor predeterminado es habilitado)

Cómo a Subclase
Declaramos que BasicErrorModel es una subclase de ErrorModel de la siguiente manera:

clase BasicErrorModel: público ErrorModel
{
pública:
TypeId estático GetTypeId (vacío);
...
privada:
// Implementar funciones virtuales puras de clase base
bool virtual DoCorrupt (Ptr pags);
booleano virtual DoReset (vacío);
...
}

y configure la función GetTypeId de la subclase configurando una cadena TypeId única y
configurando el Padre a ErrorModel:

TypeId RateErrorModel::GetTypeId (vacío)
{
TypeId estático tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
...

CONSTRUIR Core Funciones y Unidad Examenes
Afirmar Macros
Escribiendo Unidad Examenes
Adición a Nuevo Módulo a ns-3
Cuando haya creado un grupo de clases, ejemplos y pruebas relacionadas, se pueden
combinados en un ns-3 módulo para que puedan ser utilizados con los existentes ns-3 módulos
y por otros investigadores.

Este capítulo lo guía a través de los pasos necesarios para agregar un nuevo módulo a ns-3.

Paso 0 - Módulo Disposición
Todos los módulos se pueden encontrar en el src directorio. Cada módulo se puede encontrar en un directorio.
que tiene el mismo nombre que el módulo. por ejemplo, el espectro El módulo se puede encontrar aquí:
origen/espectro. Estaremos citando de la espectro módulo para ilustración.

Un módulo prototípico tiene la siguiente estructura de directorios y archivos necesarios:

src /
Nombre del módulo/
encuadernaciones/
Doc/
ejemplos /
wscript
ayudante/
modelo /
las pruebas /
ejemplos-para-ejecutar.py
wscript

No todos los directorios estarán presentes en cada módulo.

Paso 1 - Crea a Módulo Esqueleto
Se proporciona un programa de python en el directorio de origen que creará un esqueleto para un nuevo
módulo. Para los propósitos de esta discusión, asumiremos que su nuevo módulo se llama
módulo nuevo. Desde src directorio, haga lo siguiente para crear el nuevo módulo:

$ ./create-module.py nuevo módulo

Siguiente, cd into módulo nuevo; encontrará este diseño de directorio:

$ cd módulo nuevo
$ls
doc ejemplos asistente modelo prueba wscript

Con más detalle, el crear-modulo.py script creará los directorios, así como inicial
esqueleto wscript, .h, . CC y .primero archivos El módulo completo con archivos de esqueleto se ve
Me gusta esto:

src /
módulo nuevo/
Doc/
módulo-nuevo.rst
ejemplos /
nuevo-módulo-ejemplo.cc
wscript
ayudante/
nuevo-modulo-helper.cc
ayudante-nuevo-módulo.h
modelo /
módulo-nuevo.cc
módulo-nuevo.h
las pruebas /
nuevo-módulo-prueba-suite.cc
wscript

(Si se requiere el encuadernaciones/ directorio listado en Step-0 se creará automáticamente durante
La construcción.)

A continuación, veremos cómo personalizar este módulo. informando waf sobre los archivos que
la creación de su módulo se realiza editando los dos wscript archivos Caminaremos por el
pasos principales de este capítulo.

Todo ns-3 Los módulos dependen de la centro módulo y generalmente en otros módulos. Esta dependencia
se especifica en el wscript archivo (en el nivel superior del módulo, no el separado wscript
presentar en el ejemplos ¡directorio!). en el esqueleto wscript la llamada que declarará su
nuevo módulo para waf se verá así (antes de editar):

def construir (bld):
módulo = bld.create_ns3_module('nuevo-módulo', ['núcleo'])

Supongamos que módulo nuevo depende de Internet, movilidady aodv módulos. Después
editándolo el wscript el archivo debería verse así:

def construir (bld):
módulo = bld.create_ns3_module('nuevo-módulo', ['internet', 'movilidad', 'aodv'])

Tenga en cuenta que solo se deben enumerar las dependencias del módulo de primer nivel, razón por la cual eliminamos
centro; El Internet módulo a su vez depende de centro.

Lo más probable es que su módulo tenga archivos fuente modelo. Esqueletos iniciales (que
compilar con éxito) se crean en modelo/nuevo-módulo.cc y modelo/nuevo-módulo.h.

Si su módulo tendrá archivos fuente de ayuda, entonces irán a la ayudante/
directorio; nuevamente, los esqueletos iniciales se crean en ese directorio.

Finalmente, es una buena práctica escribir pruebas y ejemplos. Estos serán casi con toda seguridad
requerido para que los nuevos módulos sean aceptados en el oficial ns-3 árbol de fuentes Un esqueleto
el conjunto de pruebas y el caso de prueba se crean en el las pruebas / directorio. El conjunto de pruebas de esqueleto
contienen el siguiente constructor, que declara una nueva prueba unitaria llamada módulo nuevo, Con un
caso de prueba único que consta de la clase Nuevo caso de prueba de módulo 1:

NuevoModuleTestSuite::NuevoModuleTestSuite ()
: TestSuite ("nuevo módulo", UNIDAD)
{
AddTestCase (nuevo NewModuleTestCase1);
}

Paso 3 - Declarar Fuente archivos
El encabezado público y los archivos de código fuente para su nuevo módulo deben especificarse en el
wscript archivo modificándolo con su editor de texto.

A modo de ejemplo, después de declarar el espectro módulo, el src/espectro/wscript especifica el
archivos de código fuente con la siguiente lista:

def construir (bld):

módulo = bld.create_ns3_module('espectro', ['internet', 'propagación', 'antena', 'aplicaciones'])

módulo.fuente = [
'modelo/espectro-modelo.cc',
'modelo/valor-espectro.cc',
.
.
.
'modelo/microondas-horno-espectro-valor-ayudante.cc',
'ayudante/espectro-helper.cc',
'ayudante/adhoc-aloha-noack-ideal-phy-helper.cc',
'ayudante/generador de forma de onda-ayudante.cc',
'ayudante/analizador-de-espectro-helper.cc',
]

Los objetos resultantes de la compilación de estas fuentes se ensamblarán en una biblioteca de enlaces,
que estará vinculado a cualquier programa que dependa de este módulo.

Pero, ¿cómo aprenden estos programas la API pública de nuestro nuevo módulo? ¡Sigue leyendo!

Paso 4 - Declarar Público: Encabezamiento archivos
Los archivos de encabezado que definen la API pública de su modelo y los ayudantes también deben ser
especificado en el wscript archivo.

Continuando con el espectro ilustración del modelo, se especifican los archivos de encabezado público
con la siguiente estrofa. (Nótese que el argumento de la BLD función dice waf a
instale los encabezados de este módulo con el otro ns-3 encabezados):

encabezados = bld (características = 'ns3header')

headers.module = 'espectro'

encabezados.fuente = [
'modelo/espectro-modelo.h',
'modelo/valor-espectro.h',
.
.
.
'modelo/microondas-horno-espectro-valor-ayudante.h',
'ayudante/ayudante de espectro.h',
'ayudante/adhoc-aloha-noack-ideal-phy-helper.h',
'ayudante/generador de forma de onda-ayudante.h',
'ayudante/analizador de espectro-ayudante.h',
]

Los encabezados que se hagan públicos de esta manera serán accesibles para los usuarios de su modelo con incluir
declaraciones como

#include "ns3/espectro-modelo.h"

Los encabezados utilizados estrictamente internamente en su implementación no deben incluirse aquí. Ellos
todavía son accesibles para su implementación mediante declaraciones de inclusión como

#include "implementación-mi-módulo.h"

Paso 5 - Declarar Examenes
Si su nuevo módulo tiene pruebas, entonces deben especificarse en su wscript presentar por
modificarlo con su editor de texto.

El espectro Las pruebas modelo se especifican con la siguiente estrofa:

prueba_módulo = bld.create_ns3_module_test_library('espectro')

módulo_prueba.fuente = [
'prueba/espectro-interferencia-prueba.cc',
'prueba/valor-espectro-prueba.cc',
]

See Examenes para obtener más información sobre cómo escribir casos de prueba.

Paso 6 - Declarar Ejemplos
Si su nuevo módulo tiene ejemplos, entonces deben especificarse en su ejemplos/wscript
expediente. (El esqueleto de nivel superior wscript incluirá recursivamente ejemplos/wscript sólo si
los ejemplos se habilitaron en el momento de la configuración).

El espectro modelo define su primer ejemplo en src/espectro/ejemplos/wscript con

def construir (bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
['espectro', 'movilidad'])
obj.fuente = 'adhoc-aloha-ideal-phy.cc'

Tenga en cuenta que el segundo argumento de la función crear_ns3_programa() es la lista de modulos
del que depende el programa que se está creando; de nuevo, no olvides incluir módulo nuevo in
la lista. Es una buena práctica enumerar solo las dependencias directas del módulo y dejar que waf
deducir el árbol de dependencia completo.

De vez en cuando, para mayor claridad, es posible que desee dividir la implementación de su ejemplo entre
varios archivos fuente. En este caso, simplemente incluya esos archivos como explícitos adicionales.
fuentes del ejemplo:

obj = bld.create_ns3_program('nuevo-módulo-ejemplo', [nuevo-módulo])
obj.source = ['nuevo-módulo-ejemplo.cc', 'nuevo-módulo-ejemplo-parte.cc']

Los ejemplos de Python se especifican mediante la siguiente llamada de función. Tenga en cuenta que el segundo
argumento de la función registrarse_ns3_script() es la lista de módulos que Python
ejemplo depende de:

bld.register_ns3_script('nuevo-módulo-ejemplo.py', ['nuevo-módulo'])

Paso 7 - Ejemplos Ejecutar as Examenes
Además de ejecutar código de prueba explícito, el marco de prueba también se puede instrumentar para
ejecute programas de ejemplo completos para intentar detectar regresiones en los ejemplos. Sin embargo, no todos
los ejemplos son adecuados para las pruebas de regresión. El archivo prueba/ejemplos-para-ejecutar.py controla el
invocación de los ejemplos cuando se ejecuta el marco de prueba.

El espectro ejemplos de modelos dirigidos por prueba.py se especifican en
src/spectrum/test/examples-to-run.py usando las siguientes dos listas de C++ y Python
ejemplos:

# Una lista de ejemplos de C++ para ejecutar con el fin de garantizar que permanezcan
# construible y ejecutable con el tiempo. Cada tupla de la lista contiene
#
# (ejemplo_nombre, hacer_ejecutar, hacer_valgrind_ejecutar).
#
# Consulte test.py para obtener más información.
cpp_ejemplos = [
("adhoc-aloha-ideal-phy", "Verdadero", "Verdadero"),
("adhoc-aloha-ideal-phy-con-microondas", "Verdadero", "Verdadero"),
("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "Verdadero", "Verdadero"),
]

# Una lista de ejemplos de Python para ejecutar con el fin de garantizar que permanezcan
# ejecutable con el tiempo. Cada tupla de la lista contiene
#
# (ejemplo_nombre, hacer_ejecutar).
#
# Consulte test.py para obtener más información.
python_ejemplos = [
("ejemplo-simulador.py", "Verdadero"),
]

Como se indica en el comentario, cada entrada en la lista de ejemplos para ejecutar de C++ contiene el
tupla (ejemplo_nombre, hacer_correr, hacer_valgrind_run), donde el

· nombre_ejemplo es el ejecutable a ejecutar,

· hacer_correr es una condición bajo la cual ejecutar el ejemplo, y

· do_valgrind_run es una condición bajo la cual ejecutar el ejemplo bajo valgrind. (Este
es necesario porque NSC provoca bloqueos de instrucción ilegales con algunas pruebas cuando
se ejecutan bajo valgrind.)

Tenga en cuenta que las dos condiciones son declaraciones de Python que pueden depender de waf configuración
variables Por ejemplo,

("tcp-nsc-lfn", "NSC_ENABLED == Verdadero", "NSC_ENABLED == Falso"),

Cada entrada en la lista de Python de ejemplos para ejecutar contiene la tupla (ejemplo_nombre,
hacer_correr), donde, en cuanto a los ejemplos de C++,

· nombre_ejemplo es el script de Python que se va a ejecutar, y

· hacer_correr es una condición bajo la cual ejecutar el ejemplo.

Nuevamente, la condición es una declaración de Python que puede depender de waf variables de configuración.
Por ejemplo,

("realtime-udp-echo.py", "ENABLE_REAL_TIME == Falso"),

Paso 8 - Configurar y CONSTRUIR
Ahora puede configurar, compilar y probar su módulo con normalidad. Debe reconfigurar el
proyecto como un primer paso para que waf almacena en caché la nueva información en su wscript archivos, o
de lo contrario, su nuevo módulo no se incluirá en la compilación.

$ ./waf configurar --enable-examples --enable-tests
$ ./waf compilación
$ ./prueba.py

Busque el conjunto de pruebas de su nuevo módulo (y los programas de ejemplo, si su módulo tiene algún
activado) en la salida de prueba.

Paso 9 - Python encuadernaciones
Agregar enlaces de Python a su módulo es opcional, y el paso está comentado por
por defecto en el crear-modulo.py guión.

# bld.ns3_python_bindings()

Si desea incluir enlaces de Python (necesario solo si desea escribir Python ns-3
programas en lugar de programas C++ ns-3), debe descomentar lo anterior e instalar el
sistema de escaneo API de Python (cubierto en otra parte de este manual) y escanee su módulo para
generar nuevos enlaces.

Creamos Documentación
ns-3 proporciona dos tipos de documentación: capítulos expositivos estilo "guía del usuario" y
Documentación de la API del código fuente.

Los capítulos de la "guía del usuario" están escritos a mano en reStructuredText formato (.primero), cual es
procesado por el sistema de documentación de Python Esfinge para generar páginas web y archivos pdf.
La documentación de la API se genera a partir del propio código fuente, utilizando Doxygen, para generar
páginas web con enlaces cruzados. Ambos son importantes: los capítulos de la Esfinge explican la porque
y descripción general del uso de un modelo; la documentación de la API explica el how Detalles.

Este capítulo brinda una descripción general rápida de estas herramientas, enfatizando el uso preferido y
personalizaciones para ns-3.

Para construir toda la documentación estándar:

$ ./documentos waf

Para opciones más especializadas, sigue leyendo.

Documentando con Esfinge
Utilizamos Esfinge generar capítulos expositivos que describan el diseño y el uso de cada
módulo. Ahora mismo estás leyendo el Documentación Capítulo. los Mostrar Fuente eslabón de la
La barra lateral le mostrará la fuente de reStructuredText para este capítulo.

Adición Nuevo Comités
Agregar un nuevo capítulo requiere tres pasos (descritos con más detalle a continuación):

Escoge ¿Dónde? los archivos de documentación vivirán.

2. Enlace de una página existente a la nueva documentación.

3. Agregue el nuevo archivo a la Makefile.

¿Dónde?
Documentación para un módulo específico, foo, normalmente debería entrar src/foo/doc/. Por ejemplo
src/foo/doc/foo.rst sería el documento de nivel superior para el módulo. los
src/create-module.py script creará este archivo para usted.

Algunos modelos requieren varios .primero archivos y figuras; todos estos deben ir en el
src/foo/doc/ directorio. Los documentos en realidad están construidos por un Sphinx Makefile. para especialmente
documentación involucrada, puede ser útil tener un local Makefile en la categoría Industrial. src/foo/doc/
directorio para simplificar la construcción de la documentación para este módulo (Antena es un ejemplo).
Configurar esto no es particularmente difícil, pero está más allá del alcance de este capítulo.

En algunos casos, la documentación abarca múltiples modelos; la Nuestra red El capítulo es un ejemplo. En
estos casos agregando el .primero archivos directamente a doc/modelos/fuente/ podría ser apropiado

Enlace
Esfinge tiene que saber donde debería aparecer tu nuevo capítulo. En la mayoría de los casos, un nuevo modelo
debe aparecer el capitulo en Modelos libro. Para agregar su capítulo allí, edite
doc/modelos/fuente/index.rst

.. árbol de toc::
:máxima profundidad: 1

organización
animación
antena
aodv
aplicaciones
...

Agregue el nombre de su documento (sin el .primero extensión) a esta lista. por favor mantenga el
Modele los capítulos en orden alfabético para facilitar el escaneo visual de capítulos específicos.

Makefile
También debe agregar su documento a la correspondiente Makefile, asi que make sabe comprobarlo
para actualizaciones El Makefile del libro Models es doc/modelos/Makefile, el Makefile del libro Manual es
doc/manual/Makefile.

# enumerar todos los archivos .rst de la biblioteca de modelos que deben copiarse en $SOURCETEMP
FUENTES = \
fuente/conf.py \
fuente/_static \
fuente/índice.rst \
fuente/reemplazar.txt \
fuente/organización.rst \
...
$(SRC)/antena/doc/fuente/antena.rst \
...

Añades tu .primero archivos a la FUENTES variable. Para agregar cifras, lea los comentarios en el
Makefile para ver qué variable debe contener sus archivos de imagen. Nuevamente, conserve estos
en orden alfabético

Contruyendo Esfinge Docs
Crear la documentación de Sphinx es bastante simple. Para construir toda la Esfinge
documentación:

$ ./waf esfinge

Para construir solo la documentación de Modelos:

$ hacer -C doc/modelos

Para ver la documentación generada apunte su navegador a doc/modelos/construir/html.

Como puede ver, Sphinx usa Make para guiar el proceso. El objetivo predeterminado construye todo
formularios de salida habilitados, que en ns-3 son las de varias paginas html, única página solterohtmly
pdf (látex). Para construir solo el html de varias páginas, agrega el html objetivo:

$ hacer -C doc/modelos html

Esto puede ser útil para reducir el tiempo de compilación (y el tamaño de la charla de compilación) a medida que
están escribiendo su capítulo.

Antes de enviar su documentación al repositorio, compruebe que se compila sin
errores o advertencias. El proceso de compilación genera muchos resultados (principalmente conversaciones normales).
de LaTeX), lo que puede dificultar ver si hay advertencias de Sphinx o
errores Para encontrar advertencias y errores importantes, cree solo el html versión, luego busque
el registro de compilación para advertencia or error.

ns-3 Detalles específicos
La Esfinge documentación y tutoriales son bastante buenos No duplicaremos los conceptos básicos.
aquí, en lugar de centrarse en el uso preferido para ns-3.

· Inicia los documentos con estas dos líneas:

.. incluir:: reemplazar.txt
.. resaltar:: cpp

La primera línea permite algunos reemplazos simples. Por ejemplo, escribir |ns3| se rinde como
ns-3. El segundo establece el lenguaje de resaltado del código fuente predeterminado explícitamente para el
archivo, ya que la conjetura del analizador no siempre es precisa. (También es posible configurar el
lenguaje explícitamente para un solo bloque de código, ver más abajo).

· Secciones:

Sphinx es bastante liberal a la hora de marcar los títulos de las secciones. Por convención, preferimos este
jerarquía:

.. jerarquía de encabezados:
------------- Capítulo
************* Sección (#.#)
============= Subsección (#.#.#)
############# Subsubsección

· Resaltado de sintaxis:

Para usar el resaltador de sintaxis predeterminado, simplemente inicie un bloque de código fuente:

" ───────────────────────────────┐
│ Fuente Sphinx │ Salida renderizada │
" ───────────────────────────────┤
│ │ El frobniz se accede por: │
│ Se accede al ``Frobnitz`` mediante:: │ │
│ │ Foo::Frobnitz frob; │
│ Foo::Frobnitz frob; │ frob.Establecer (...); │
│ frob.Establecer (...); │ │
" ───────────────────────────────┘

Para usar un resaltador de sintaxis específico, por ejemplo, golpear comandos de shell:

" ───┐
│ Fuente Sphinx │ Salida renderizada │
" ───┤
│ │ │
│ .. código fuente:: bash │ $ ls │
│ │ │
│ $ ls │ │
" ───┘

· Notaciones Taquigráficas:

Estas abreviaturas se definen:

-
│ Fuente Sphinx │ Salida renderizada │
├────────────────────────┼────── ─
│ │ ns-3
│ |ns3| │ │
├────────────────────────┼────── ─
│ │ ns-2
│ |ns2| │ │
├────────────────────────┼────── ─
│ │ │
│ |comprobar| │ │
├────────────────────────┼────── ─
│ │ RFC 6282
│ :rfc:`6282` │ │
└────────────────────────┴─────────── ────

Documentando con Doxygen
Utilizamos Doxygen para generar navegable Documentación de la API. Doxygen proporciona una serie de
características útiles:

· Tabla resumen de todos los miembros de la clase.

· Gráficos de herencia y colaboración para todas las clases.

· Enlaces al código fuente implementando cada función.

· Enlaces a todos los lugares donde se utiliza un miembro.

· Enlaces a cada objeto utilizado en la implementación de una función.

· Agrupación de clases relacionadas, como todas las clases relacionadas con un protocolo específico.

Además, usamos el ID de tipo sistema para agregar a la documentación para cada clase

· Los Config caminos por los cuales tales objetos pueden ser alcanzados.

· Documentación para cualquier Atributos, incluyendo Atributos definido en las clases padre.

· Documentación para cualquier Trace fuentes definidas por la clase.

Doxygen opera escaneando el código fuente, buscando comentarios especialmente marcados. Eso
también crea una referencia cruzada, indicando donde cada archivo, clase, método y variable es
usado.

Preferido Estilo
El estilo preferido para los comentarios de Doxygen es el estilo JavaDoc:

/ **
* Breve descripción de esta clase o método.
* Las líneas adyacentes se convierten en un solo párrafo.
*
* Descripción más larga, con muchos detalles.
*
* Líneas en blanco separan párrafos.
*
* Explicar qué hace la clase o el método, usando qué algoritmo.
* Explicar las unidades de los argumentos y los valores devueltos.
*
* \note Tenga en cuenta cualquier limitación o error.
*
* (Para funciones con argumentos o valores de retorno :)
* \param foo Frase nominal breve que describe este argumento.
* \param bar Nota Caso de oración y período de finalización.
* \return Frase nominal breve que describe el valor.
*
* \interno
*
* También puede discutir los detalles de implementación interna.
* Comprender este material no debería ser necesario para usar
* la clase o método.
*/
ejemplo de clase

En este estilo, el bloque de comentarios de Doxygen comienza con dos caracteres `*': / **, y precede
el elemento que se documenta.

Para artículos que solo necesitan una breve descripción, cualquiera de estas formas abreviadas es apropiada:

/** Implementación del destructor. */
anular DoDispose ();

int m_cuenta; //!< Cuenta de...

Tenga en cuenta la forma especial del comentario al final de la línea, //!, indicando que se refiere a la
anterior ít.

Algunos elementos a tener en cuenta:

· Usar la oración en mayúsculas, incluida la mayúscula inicial.

· Usar puntuación, especialmente `.'s al final de oraciones o frases.

· Los \breve no se necesita etiqueta; la primera oración se utilizará como breve
descripción.

Cada clase, método, typedef, variable de miembro, argumento de función y valor de retorno debe
documentarse en todos los archivos de código fuente que forman la API formal y la implementación para
ns-3, Tales como origen/ /modelo/*, origen/ /ayudante/* y origen/ /utiles/*.
Documentación para artículos en origen/ /prueba/* y origen/ /ejemplos/* se prefiere,
pero no requerido.

Conveniente Caracteristicas
· Los miembros heredados heredarán automáticamente los documentos del padre (pero se pueden reemplazar
por documentación local).

1. Documente la clase base.

2. En la subclase, marque las funciones heredadas con un comentario ordinario:

// métodos heredados
vacío virtual FooBar (vacío);
virtual int BarFoo (doble baz);

Tenga en cuenta que las firmas deben coincidir exactamente, así que incluya el argumento formal (vacío)

Esto no funciona para funciones estáticas; ver ObtenerIdTipo, a continuación, para ver un ejemplo.

Contruyendo Doxygen Docs
Construir la documentación de Doxygen es bastante simple:

$ ./waf doxígeno

Esto se construye utilizando la configuración predeterminada, que genera secciones de documentación para
all elementos, incluso si no tienen bloques de documentación de comentarios explícitos. esto tiene la
efecto de suprimir las advertencias de artículos no documentados, pero se asegura de que todo aparezca
en la salida generada.

Al escribir documentación, a menudo es más útil ver qué elementos están generando
advertencias, generalmente sobre documentación faltante. Para ver la lista completa de advertencias, utilice el
doc/doxygen.advertencias.informe.sh script:

$ doc/doxygen.advertencias.informe.sh
Waf: Entrando en el directorio `build'
...
Waf: Dejando el directorio 'construir'
'compilación' finalizada correctamente (3m24.094s)

Reconstruyendo documentos de doxygen con errores completos... Listo.

Informe de advertencias de Doxygen
----------------------------------------

(Todos los recuentos son límites inferiores).

Advertencias por módulo/directorio:

Directorio de conteo
----- ----------------------------------
3844 fuente/lte/modelo
1718 origen/wimax/modelo
1423 origen/núcleo/modelo
....
138 parámetros adicionales no documentados.
----------------------------------------
15765 advertencias totales
126 directorios con advertencias

Avisos por archivo (alfabético)

Archivo de conteo
----- ----------------------------------
17 doc/introspected-doxygen.h
15 ejemplos/enrutamiento/manet-routing-compare.cc
26 ejemplos/estadísticas/wifi-example-apps.h
....
----------------------------------------
967 archivos con advertencias

Advertencias por archivo (numérico)

Archivo de conteo
----- ----------------------------------
374 src/lte/modelo/lte-asn1-header.h
280 src/lte/modelo/lte-rrc-sap.h
262 src/lte/modelo/lte-rrc-header.h
....
----------------------------------------
967 archivos con advertencias

Resumen de advertencias de Doxygen
----------------------------------------
126 directorios
Archivos 967
15765 advertencias

El script modifica la configuración para mostrar todas las advertencias y acortar el tiempo de ejecución.
Como puede ver, en este escrito tenemos a montón de elementos no documentados. El informe
resume las advertencias por módulo origen/*/*, y por archivo, en orden alfabético y numérico.

El script tiene algunas opciones para reducir las cosas y hacerlas más manejables. para ayuda,
utilice el -h opción. Habiéndolo ejecutado una vez para hacer la compilación de Doxygen y generar el
registro de advertencias, puede volver a procesar el archivo de registro con varios "filtros", sin tener que hacer
la compilación completa de Doxygen, nuevamente usando el -s opción. Puede excluir advertencias de
*/ejemplos/* archivos (-e opción) y/o */prueba/* archivos (-t).

Quizás la opción más útil al escribir comentarios de documentación es -m , cual
limitará el informe a solo archivos que coincidan origen/ /*y siga el informe con
las líneas de advertencia reales. Combinar con eth y puede concentrarse en las advertencias que están
más urgente en un solo módulo:

$ doc/doxygen.warnings.report.sh -m malla/ayudante
...
Resumen de advertencias de Doxygen
----------------------------------------
1 directorios
Archivos 3
149 advertencias

Advertencias filtradas
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: advertencia: el miembro m_root (variable) de la clase ns3::Dot11sStack no está documentado.
src/mesh/helper/dot11s/dot11s-installer.h:35: advertencia: el tipo de retorno del miembro ns3::Dot11sStack::GetTypeId no está documentado
src/mesh/helper/dot11s/dot11s-installer.h:56: advertencia: el tipo de retorno del miembro ns3::Dot11sStack::InstallStack no está documentado
src/mesh/helper/flame/lfame-installer.h:40: advertencia: el miembro GetTypeId() (función) de la clase ns3::FlameStack no está documentado.
src/mesh/helper/flame/flame-installer.h:60: advertencia: el tipo de retorno del miembro ns3::FlameStack::InstallStack no está documentado
src/mesh/helper/mesh-helper.h:213: advertencia: el miembro m_nInterfaces (variable) de la clase ns3::MeshHelper no está documentado.
src/mesh/helper/mesh-helper.h:214: advertencia: el miembro m_spreadChannelPolicy (variable) de la clase ns3::MeshHelper no está documentado.
src/mesh/helper/mesh-helper.h:215: advertencia: el miembro m_stack (variable) de la clase ns3::MeshHelper no está documentado.
src/mesh/helper/mesh-helper.h:216: advertencia: el miembro m_stackFactory (variable) de la clase ns3::MeshHelper no está documentado.
src/mesh/helper/mesh-helper.h:209: advertencia: los parámetros del miembro ns3::MeshHelper::CreateInterface no están (todos) documentados
src/mesh/helper/mesh-helper.h:119: advertencia: los parámetros del miembro ns3::MeshHelper::SetStandard no están (todos) documentados

¡Ahora solo es cuestión de entender el código y escribir algunos documentos!

ns-3 Detalles específicos
En cuanto a Sphinx, el Doxygen documentos y referencia son bastante buenos No duplicaremos el
conceptos básicos aquí, en lugar de centrarse en el uso preferido para ns-3.

· Usar Doxygen Módulos para agrupar elementos relacionados.

En el encabezado principal de un módulo, cree un grupo Doxgyen:

/ **
* \defgroup foo Protocolo Foo.
*/

Marque cada clase asociada como perteneciente al grupo:

/ **
* \ingroup foo
*
* Tipo de paquete Foo.
*/
clase foo

· Sabías definiciones de tipos puede tener argumentos formales? Esto permite la documentación de la función.
firmas de puntero:

/ **
* Barra de firma de la función de devolución de llamada.
*
* \param ale El tamaño de una pinta de cerveza, en onzas imperiales.
*/
typedef void (* BarCallback)(const int ale);

· Copia el Atributo cadenas de ayuda de la ObtenerIdTipo método a utilizar como resumen
descripciones de los miembros asociados.

· \bugid{298} creará un enlace al error 298 en nuestro Bugzilla.

· \pnombre{foo} en una descripción formateará foo como herramienta de edición del \param foo parámetro, dejando claro
que te estás refiriendo a un argumento real.

· \RFC{301} creará un enlace a RFC 301.

· \interno debe usarse solo para iniciar una discusión sobre los detalles de implementación, no para
marca privada funciones (ya vienen marcadas, como privada!)

· No cree clases con nombres triviales, como clase A, incluso en suites de prueba. Estas
hacer que todas las instancias del literal de nombre de clase 'A' se representen como enlaces.

Como se señaló anteriormente, las funciones estáticas no heredan la documentación de las mismas funciones en
la clase padre. ns-3 utiliza algunas funciones estáticas de forma ubicua; el sugerido
bloque de documentación para estos casos es:

· Constructor/destructor por defecto:

Mi clase (); //!< Constructor por defecto
~MiClase (); //!< Destructor

· Dummy destructor y DoDispose:

/** Destructor ficticio, consulte DoDispose. */
~MiClase ();

/** Implementación del destructor */
vacío virtual DoDispose ();

· ObtenerIdTipo:

/ **
* Registre este tipo.
* \return El objeto TypeId.
*/
TypeId estático GetTypeId (vacío);

Habilitación Subconjuntos of ns-3 Módulos
Como con la mayoría de los proyectos de software, ns-3 es cada vez más grande en términos de número de módulos,
líneas de código y huella de memoria. Sin embargo, los usuarios solo pueden usar algunos de esos módulos.
a la vez Por este motivo, es posible que los usuarios deseen habilitar explícitamente solo el subconjunto de la
posible ns-3 módulos que realmente necesitan para su investigación.

En este capítulo se explica cómo habilitar solo el ns-3 Módulos que te interesan
utilizando.

Cómo a habilitar a subconjunto of ns-3's módulos
Si se están creando bibliotecas compartidas, habilitar un módulo provocará al menos una
biblioteca a construir:

libns3-nombredelmódulo.so

Si el módulo tiene una biblioteca de prueba y se están construyendo bibliotecas de prueba, entonces

libns3-nombre-módulo-test.so

también se construirá. Otros módulos de los que depende el módulo y sus bibliotecas de prueba
también se construirá.

Por defecto, todos los módulos están integrados ns-3. Hay dos formas de habilitar un subconjunto de estos
módulos:

1. Usando la opción --enable-modules de waf

2. Uso de la ns-3 archivo de configuración

permitir módulos usando waf --habilitar-módulos opción
Para habilitar solo el módulo central con ejemplos y pruebas, por ejemplo, pruebe estos comandos:

$ ./waf limpio
$ ./waf configurar --enable-examples --enable-tests --enable-modules=core
$ ./waf compilación
$ cd construir/depurar/
$ls

y las siguientes bibliotecas deben estar presentes:

enlaces libns3-core.so ns3 scratch utils
ejemplos libns3-core-test.so muestras src

Nota la ./waf limpia El paso se realiza aquí solo para que sea más obvio qué bibliotecas de módulos
se construyeron. no tienes que hacer ./waf limpia para habilitar subconjuntos de módulos.

Ejecutar test.py hará que solo se ejecuten aquellas pruebas que dependen del núcleo del módulo:

24 de 24 pruebas aprobadas (24 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Repita los pasos anteriores para el módulo "red" en lugar del módulo "núcleo", y el
Se construirá lo siguiente, ya que la red depende del núcleo:

enlaces libns3-core.so libns3-network.so ns3 scratch utils
ejemplos libns3-core-test.so libns3-network-test.so muestras src

La ejecución de test.py hará que las pruebas que dependen solo de los módulos central y de red se ejecuten.
ser ejecutado:

31 de 31 pruebas aprobadas (31 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

permitir módulos usando los ns-3 configuración presentar
Se ha agregado un archivo de configuración, .ns3rc, a ns-3 que permite a los usuarios especificar qué
Los módulos deben incluirse en la compilación.

Al habilitar un subconjunto de ns-3 módulos, las reglas de precedencia son las siguientes:

1. La cadena --enable-modules configure anula cualquier archivo .ns3rc

2. el archivo .ns3rc en el nivel superior ns-3 el directorio se consulta a continuación, si está presente

3. el sistema busca ~/.ns3rc si los dos anteriores no están especificados

Si nada de lo anterior limita los módulos a construir, todos los módulos que waf conozca
ser construido.

La versión mantenida del archivo .ns3rc en el ns-3 El repositorio de código fuente reside en
los utils directorio. La razón de esto es si estuviera en el directorio de nivel superior del
repositorio, sería propenso a verificaciones accidentales de los mantenedores que habilitan el
módulos que quieren usar. Por lo tanto, los usuarios deben copiar manualmente el archivo .ns3rc del
utils directorio a su lugar preferido (directorio de nivel superior o su directorio de inicio) para
habilite la configuración de compilación modular persistente.

Asumiendo que estás en el nivel superior ns-3 directorio, puede obtener una copia del .ns3rc
archivo que está en el utils directorio de la siguiente manera:

$cputils/.ns3rc.

El archivo .ns3rc ahora debería estar en su nivel superior ns-3 directorio, y contiene el
siguientes:

#! / usr / bin / env pitón

# Una lista de los módulos que se habilitarán cuando se ejecute ns-3.
# Los módulos que dependen de los módulos enumerados también se habilitarán.
#
# Todos los módulos se pueden habilitar eligiendo 'all_modules'.
módulos_habilitados = ['todos_los_módulos']

# Establezca esto igual a verdadero si desea que se ejecuten ejemplos.
ejemplos_habilitado = Falso

# Establezca esto igual a verdadero si desea que se ejecuten las pruebas.
tests_enabled = Falso

Use su editor favorito para modificar el archivo .ns3rc para habilitar solo el módulo central con
ejemplos y pruebas como esta:

#! / usr / bin / env pitón

# Una lista de los módulos que se habilitarán cuando se ejecute ns-3.
# Los módulos que dependen de los módulos enumerados también se habilitarán.
#
# Todos los módulos se pueden habilitar eligiendo 'all_modules'.
módulos_habilitados = ['núcleo']

# Establezca esto igual a verdadero si desea que se ejecuten ejemplos.
ejemplos_habilitado = Verdadero

# Establezca esto igual a verdadero si desea que se ejecuten las pruebas.
tests_enabled = Verdadero

Solo el módulo central estará habilitado ahora si prueba estos comandos:

$ ./waf limpio
$ ./waf configurar
$ ./waf compilación
$ cd construir/depurar/
$ls

y las siguientes bibliotecas deben estar presentes:

enlaces libns3-core.so ns3 scratch utils
ejemplos libns3-core-test.so muestras src

Nota la ./waf limpia El paso se realiza aquí solo para que sea más obvio qué bibliotecas de módulos
se construyeron. no tienes que hacer ./waf limpia para habilitar subconjuntos de módulos.

Ejecutar test.py hará que solo se ejecuten aquellas pruebas que dependen del núcleo del módulo:

24 de 24 pruebas aprobadas (24 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Repita los pasos anteriores para el módulo "red" en lugar del módulo "núcleo", y el
Se construirá lo siguiente, ya que la red depende del núcleo:

enlaces libns3-core.so libns3-network.so ns3 scratch utils
ejemplos libns3-core-test.so libns3-network-test.so muestras src

La ejecución de test.py hará que las pruebas que dependen solo de los módulos central y de red se ejecuten.
ser ejecutado:

31 de 31 pruebas aprobadas (31 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Habilitar/deshabilitar ns-3 Examenes y Ejemplos
El ns-3 distribución incluye muchos ejemplos y pruebas que se utilizan para validar la ns-3
sistema. Sin embargo, es posible que los usuarios no siempre deseen que estos ejemplos y pruebas se ejecuten para sus
Instalación de ns-3.

En este capítulo se explica cómo construir ns-3 con o sin sus ejemplos y pruebas.

Cómo a habilitar deshabilitar ejemplos y pruebas in ns-3
Hay 3 formas de habilitar/deshabilitar ejemplos y pruebas en ns-3:

1. Usar build.py cuando ns-3 se construye por primera vez

2. Usar waf una vez ns-3 ha sido construido

3. Uso de la ns-3 archivo de configuración una vez ns-3 ha sido construido

Habilitar deshabilitar ejemplos y pruebas usando construir.py
Puede usar build.py para habilitar/deshabilitar ejemplos y pruebas cuando ns-3 se construye por primera
en las transacciones.

De forma predeterminada, los ejemplos y las pruebas no están integrados. ns-3.

Desde el directorio ns-3-allinone, puede crear ns-3 sin ejemplos ni pruebas simplemente
haciendo:

$ ./construir.py

Ejecutando test.py en el nivel superior ns-3 directorio ahora no hará que se realicen ejemplos o pruebas.
correr:

0 de 0 pruebas aprobadas (0 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Si desea construir ns-3 con ejemplos y pruebas, luego haga lo siguiente desde el
directorio ns-3-allinone:

$ ./build.py --habilitar-ejemplos --habilitar-pruebas

Ejecutando test.py en el nivel superior ns-3 directorio causará todos los ejemplos y pruebas
para ejecutarse:

170 de 170 pruebas aprobadas (170 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Habilitar deshabilitar ejemplos y pruebas usando waf
Puede usar waf para habilitar/deshabilitar ejemplos y pruebas una vez ns-3 ha sido construido.

De forma predeterminada, los ejemplos y las pruebas no están integrados. ns-3.

Desde el nivel superior ns-3 directorio, puede construir ns-3 sin ejemplos ni pruebas simplemente
haciendo:

$ ./waf configurar
$ ./waf compilación

Ejecutar test.py ahora no hará que se ejecuten ejemplos o pruebas:

0 de 0 pruebas aprobadas (0 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Si desea construir ns-3 con ejemplos y pruebas, luego haga lo siguiente desde arriba
nivel ns-3 directorio:

$ ./waf configurar --enable-examples --enable-tests
$ ./waf compilación

Ejecutar test.py hará que se ejecuten todos los ejemplos y pruebas:

170 de 170 pruebas aprobadas (170 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Habilitar deshabilitar ejemplos y pruebas usando los ns-3 configuración presentar
Se ha agregado un archivo de configuración, .ns3rc, a ns-3 que permite a los usuarios especificar si
los ejemplos y las pruebas deben construirse o no. Puede usar este archivo para habilitar/deshabilitar
ejemplos y pruebas una vez ns-3 ha sido construido.

Al habilitar la desactivación de ejemplos y pruebas, las reglas de precedencia son las siguientes:

1. Las cadenas de configuración --enable-examples/--disable-examples anulan cualquier archivo .ns3rc

2. Las cadenas de configuración --enable-tests/--disable-tests anulan cualquier archivo .ns3rc

3. el archivo .ns3rc en el nivel superior ns-3 el directorio se consulta a continuación, si está presente

4. el sistema busca ~/.ns3rc si el archivo .ns3rc no se encontró en el paso anterior

Si no existe ninguno de los anteriores, no se crearán ejemplos ni pruebas.

La versión mantenida del archivo .ns3rc en el ns-3 El repositorio de código fuente reside en
los utils directorio. La razón de esto es si estuviera en el directorio de nivel superior del
repositorio, sería propenso a verificaciones accidentales de los mantenedores que habilitan el
módulos que quieren usar. Por lo tanto, los usuarios deben copiar manualmente el archivo .ns3rc del
utils directorio a su lugar preferido (directorio de nivel superior o su directorio de inicio) para
habilitar la habilitación persistente de ejemplos y pruebas.

Asumiendo que estás en el nivel superior ns-3 directorio, puede obtener una copia del .ns3rc
archivo que está en el utils directorio de la siguiente manera:

$cputils/.ns3rc.

El archivo .ns3rc ahora debería estar en su nivel superior ns-3 directorio, y contiene el
siguientes:

#! / usr / bin / env pitón

# Una lista de los módulos que se habilitarán cuando se ejecute ns-3.
# Los módulos que dependen de los módulos enumerados también se habilitarán.
#
# Todos los módulos se pueden habilitar eligiendo 'all_modules'.
módulos_habilitados = ['todos_los_módulos']

# Establezca esto igual a verdadero si desea que se ejecuten ejemplos.
ejemplos_habilitado = Falso

# Establezca esto igual a verdadero si desea que se ejecuten las pruebas.
tests_enabled = Falso

Desde el nivel superior ns-3 directorio, puede construir ns-3 sin ejemplos ni pruebas simplemente
haciendo:

$ ./waf configurar
$ ./waf compilación

Ejecutar test.py ahora no hará que se ejecuten ejemplos o pruebas:

0 de 0 pruebas aprobadas (0 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Si desea construir ns-3 con ejemplos y pruebas, usa tu editor favorito para cambiar
los valores en el archivo .ns3rc para ejemplos_enabled y tests_enabled para que sean verdaderos:

#! / usr / bin / env pitón

# Una lista de los módulos que se habilitarán cuando se ejecute ns-3.
# Los módulos que dependen de los módulos enumerados también se habilitarán.
#
# Todos los módulos se pueden habilitar eligiendo 'all_modules'.
módulos_habilitados = ['todos_los_módulos']

# Establezca esto igual a verdadero si desea que se ejecuten ejemplos.
ejemplos_habilitado = Verdadero

# Establezca esto igual a verdadero si desea que se ejecuten las pruebas.
tests_enabled = Verdadero

Desde el nivel superior ns-3 directorio, puede construir ns-3 con ejemplos y pruebas simplemente por
haciendo:

$ ./waf configurar
$ ./waf compilación

Ejecutar test.py hará que se ejecuten todos los ejemplos y pruebas:

170 de 170 pruebas aprobadas (170 aprobada, 0 omitida, 0 fallida, 0 bloqueada, 0 errores de valgrind)

Diagnóstico
Este capítulo publica información sobre posibles errores comunes al construir o ejecutar
ns-3 .

Tenga en cuenta que la wiki (http://www.nsnam.org/wiki/Troubleshooting) puede haber contribuido
los productos.

CONSTRUIR errores
Tiempo de ejecución errores
A veces, pueden ocurrir errores con un programa después de una compilación exitosa. Estos son en tiempo de ejecución
errores, y pueden ocurrir comúnmente cuando la memoria está dañada o los valores del puntero son inesperados.
nulo.

He aquí un ejemplo de lo que podría ocurrir:

$ ./waf --ejecutar tcp punto a punto
Ingresando al directorio '/home/tomh/ns-3-nsc/build'
Compilación finalizada con éxito
El comando ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] salió con el código -11

El mensaje de error dice que el programa finalizó sin éxito, pero no está claro
a partir de esta información lo que podría estar mal. Para examinar más de cerca, intente ejecutarlo bajo
los gdb depurador:

$ ./waf --ejecutar tcp-punto-a-punto --command-template="gdb %s"
Ingresando al directorio '/home/tomh/ns-3-nsc/build'
Compilación finalizada con éxito
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Copyright 2004 Fundación de Software Libre, Inc.
GDB es un software gratuito, cubierto por la Licencia Pública General GNU, y usted
Bienvenido a cambiarlo y / o distribuir copias del mismo bajo ciertas condiciones.
Escriba "mostrar copia" para ver las condiciones.
No hay absolutamente ninguna garantía para GDB. Escriba "mostrar garantía" para obtener más detalles.
Este GDB se configuró como "i386-redhat-linux-gnu"...Usando host libthread_db
biblioteca "/lib/libthread_db.so.1".

(gdb) ejecutar
Programa de inicio: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Lectura de símbolos del objeto compartido leído de la memoria de destino... hecho.
Sistema cargado suministrado DSO en 0xf5c000

Programa recibido señal SIGSEGV, Fallo de segmentación.
0x0804aa12 en principal (argc=1, argv=0xbfdfefa4)
en ../examples/tcp-punto-a-punto.cc:136
136 puntos localSocket = socketFactory->CreateSocket ();
(gdb) p conector local
$1 = {m_ptr = 0x3c5d65}
(gdb) p fábrica de sockets
$2 = {m_pto = 0x0}
(gdb) salir
El programa se está ejecutando. ¿Salir de todos modos? (t o n) y

Tenga en cuenta primero la forma en que se invocó el programa: pase el comando para ejecutar como un argumento para el
plantilla de comando "gdb %s".

Esto nos dice que hubo un intento de desreferenciar un socketFactory de puntero nulo.

Miremos alrededor de la línea 136 de tcp-punto a punto, como sugiere gdb:

ptr socketFactory = n2->GetObject (Tcp::iid);
ptr localSocket = socketFactory->CreateSocket ();
localSocket->Enlazar ();

El culpable aquí es que el valor de retorno de GetObject no está siendo verificado y puede ser
nulo.

A veces, es posible que necesite usar el Valgrind memoria inspector para errores más sutiles. Otra vez,
invocas el uso de valgrind de manera similar:

$ ./waf --ejecutar tcp-punto-a-punto --command-template="valgrind %s"

FUENTE


Este documento está escrito en reStructuredText for Esfinge y se mantiene en el
documento/manual directorio del código fuente de ns-3.

Use ns-3-manual en línea usando los servicios de onworks.net


Servidores y estaciones de trabajo gratuitos

Descargar aplicaciones de Windows y Linux

  • 1
    fre: ac - convertidor de audio gratuito
    fre: ac - convertidor de audio gratuito
    fre:ac es un conversor de audio y CD gratuito
    Destripador para varios formatos y codificadores.
    Cuenta con MP3, MP4/M4A, WMA, Ogg
    Formato Vorbis, FLAC, AAC y Bonk
    apoyo, ...
    Descargar fre:ac - convertidor de audio gratuito
  • 2
    matplotlib
    matplotlib
    Matplotlib es una biblioteca completa
    para crear estática, animada y
    visualizaciones interactivas en Python.
    Matplotlib facilita las cosas fáciles y
    cosa dura ...
    Descargar Matplotlib
  • 3
    hombrebot
    hombrebot
    Escribe la lógica de tu chatbot una vez y
    conectarlo a uno de los disponibles
    servicios de mensajería, incluido Amazon
    Alexa, Mensajero de Facebook, Slack,
    Telegram o incluso yo...
    Descargar BotMan
  • 4
    Joplin
    Joplin
    Joplin es un código abierto y gratuito
    aplicación para tomar notas y tareas
    puede manejar una gran cantidad de notas en
    Formato de rebajas, organízalas en
    cuadernos y ...
    Descargar Joplin
  • 5
    gerbv: un visor Gerber (RS-274X)
    gerbv: un visor Gerber (RS-274X)
    Gerbv es un archivo Gerber de código abierto
    (solo RS-274X) visor. Gerbv te permite
    cargar varios archivos uno encima del otro,
    hacer mediciones en la imagen mostrada,
    etc ...
    Descargar gerbv, un visor Gerber (RS-274X)
  • 6
    Iómetro
    Iómetro
    Herramienta de análisis de rendimiento de E/S.
    Público: Desarrolladores, Información
    Tecnología, Ciencia / Investigación, Sistema
    Administradores. Interfaz de usuario: Win32
    (MS Windows). Progr ...
    Descargar Iómetro
  • Más "

Comandos de Linux

Ad