Dit is de opdracht ns-3-manual die kan worden uitgevoerd in de gratis hostingprovider van OnWorks met behulp van een van onze meerdere gratis online werkstations zoals Ubuntu Online, Fedora Online, Windows online emulator of MAC OS online emulator
PROGRAMMA:
NAAM
ns-3-handleiding - ns-3 handleiding
Dit is de ns-3 Handgeschakeld. Primaire documentatie voor het ns-3-project is beschikbaar in vijf
vormen:
· ns-3 zuurstofoxy: Documentatie van de openbare API's van de simulator
· Zelfstudie, Handleiding (deze document), en modelbibliotheek voor de laatste los en
ontwikkeling boom
· ns-3 wiki
INHOUD
Organisatie
Dit hoofdstuk beschrijft het geheel ns-3 software organisatie en de bijbehorende
organisatie van deze handleiding.
ns-3 is een discrete-event netwerksimulator waarin de simulatiekern en modellen zijn
geïmplementeerd in C++. ns-3 is gebouwd als een bibliotheek die statisch of dynamisch kan zijn
gekoppeld aan een C++ hoofdprogramma dat de simulatietopologie definieert en het
simulator. ns-3 exporteert ook bijna al zijn API naar Python, waardoor Python-programma's dat kunnen
importeer een "ns3"-module op ongeveer dezelfde manier als de ns-3 bibliotheek is gekoppeld door uitvoerbare bestanden
in C++.
[afbeelding] Software organisatie van ns-3.ONINDENT
De broncode voor ns-3 wordt grotendeels georganiseerd in de src directory en kan worden beschreven
door het schema erin Software organisatie of ns-3. We werken ons van onderaf af
omhoog; over het algemeen hebben modules alleen afhankelijkheden van modules eronder in de afbeelding.
We beschrijven eerst de kern van de simulator; die componenten die overal gemeenschappelijk zijn
protocol, hardware en omgevingsmodellen. De simulatiekern is geïmplementeerd in
src/kern. Pakketten zijn fundamentele objecten in een netwerksimulator en worden geïmplementeerd in
src/netwerk. Deze twee simulatiemodules zijn op zichzelf bedoeld om een
generieke simulatiekern die door verschillende soorten netwerken kan worden gebruikt, niet alleen
Op internet gebaseerde netwerken. De bovenstaande modules van ns-3 zijn onafhankelijk van een specifiek netwerk
en apparaatmodellen, die in de volgende delen van deze handleiding worden behandeld.
In aanvulling op het bovenstaande ns-3 kern introduceren we, ook in het eerste deel van de
manual, twee andere modules die een aanvulling vormen op de op C++ gebaseerde API. ns-3 programma's mogen
rechtstreeks toegang krijgen tot de hele API of gebruik kunnen maken van een zogenaamde helper API dat biedt
handige wrappers of inkapseling van low-level API-aanroepen. Het feit dat ns-3 programma's
kan worden geschreven naar twee API's (of een combinatie daarvan) is een fundamenteel aspect van de
simulator. We beschrijven ook hoe Python wordt ondersteund in ns-3 voordat we verder gaan met specifiek
modellen die relevant zijn voor netwerksimulatie.
De rest van de handleiding is gericht op het documenteren en ondersteunen van de modellen
mogelijkheden. Het volgende deel concentreert zich op twee fundamentele objecten in ns-3: de Knooppunt en
Netapparaat. Er zijn twee speciale NetDevice-typen ontworpen om het gebruik van netwerkemulatie te ondersteunen
gevallen, en emulatie wordt hierna beschreven. Het volgende hoofdstuk is eraan gewijd
Internetgerelateerde modellen, inclusief de sockets-API die wordt gebruikt door internettoepassingen. De
het volgende hoofdstuk behandelt toepassingen en het volgende hoofdstuk beschrijft aanvullende ondersteuning
voor simulatie, zoals animators en statistieken.
Het project onderhoudt een aparte handleiding gewijd aan het testen en valideren van ns-3 code
(Zie de ns-3 Testen en Validatie handboek).
Random Variabelen
ns-3 bevat een ingebouwde pseudo-random number generator (PRNG). Het is belangrijk voor
serieuze gebruikers van de simulator om de functionaliteit, configuratie en gebruik te begrijpen
van deze PRNG, en om te beslissen of het voldoende is voor zijn of haar onderzoeksgebruik.
Quick Overzicht
ns-3 willekeurige getallen worden verstrekt via instanties van ns3::WillekeurigeVariabeleStream.
· standaard, ns-3 simulaties gebruiken een vaste seed; als er willekeur in de
simulatie, zal elke uitvoering van het programma identieke resultaten opleveren, tenzij de seed en/of
het nummer van de run wordt gewijzigd.
· in ns-3.3 en eerder, ns-3 simulaties gebruikten standaard een willekeurige seed; dit markeert een
wijziging van het beleid te beginnen met ns-3.4.
· in ns-3.14 en eerder, ns-3 simulaties gebruikten een andere wrapper-klasse genaamd
ns3::willekeurige variabele. Vanaf ns-3.15, deze klasse is vervangen door
ns3::WillekeurigeVariabeleStream; de onderliggende pseudo-random number generator niet
gewijzigd.
· om willekeur te verkrijgen over meerdere simulatieruns, moet u ofwel de seed instellen
anders of stel het runnummer anders in. Bel om een seed te zetten
ns3::RngSeedManager::SetSeed() aan het begin van het programma; om een runnummer mee in te stellen
hetzelfde zaad, bel ns3::RngSeedManager::SetRun() aan het begin van het programma; zien
Wij creëren willekeurige variabelen.
· elke RandomVariableStream die wordt gebruikt in ns-3 heeft een virtuele generator voor willekeurige getallen
ermee; alle willekeurige variabelen gebruiken een vast of willekeurig startpunt op basis van het gebruik van de
global seed (vorige bullet);
· als u van plan bent om meerdere runs van hetzelfde scenario uit te voeren, met verschillende random
nummers, lees dan zeker het gedeelte over het uitvoeren van onafhankelijke replicaties:
Wij creëren willekeurige variabelen.
Lees verder voor meer uitleg over de toevalsgetallenvoorziening voor ns-3.
Achtergrond
Simulaties gebruiken veel willekeurige getallen; een studie wees uit dat de meeste netwerksimulaties
besteed maar liefst 50% van de CPU aan het genereren van willekeurige getallen. Simulatie gebruikers moeten zijn
bezig met de kwaliteit van de (pseudo)toevalsgetallen en de onafhankelijkheid ertussen
verschillende stromen van willekeurige getallen.
Gebruikers moeten zich zorgen maken over een aantal zaken, zoals:
· de seeding van de random number generator en of er een simulatie uitkomst is
deterministisch of niet,
· hoe u verschillende stromen willekeurige getallen kunt verkrijgen die onafhankelijk zijn van één
een ander, en
· hoe lang het duurt voordat streams fietsen
We zullen hier een paar termen introduceren: een RNG levert een lange reeks van (pseudo) random
nummers. De lengte van deze reeks wordt de genoemd cyclus lengte or periode, na welke
de RNG zal zichzelf herhalen. Deze reeks kan worden opgedeeld in disjuncte streams. Een
stroom van een RNG is een aaneengesloten subset of blok van de RNG-reeks. Als bijvoorbeeld de
De RNG-periode heeft een lengte N en er worden twee streams geleverd door deze RNG, en dan de eerste
stream kan de eerste N/2-waarden gebruiken en de tweede stream kan de tweede N/2 produceren
waarden. Een belangrijke eigenschap hierbij is dat de twee stromen niet gecorreleerd zijn. Insgelijks,
elke stroom kan onsamenhangend worden verdeeld in een aantal niet-gecorreleerde substromen. De
onderliggende RNG produceert hopelijk een pseudo-willekeurige reeks getallen met een zeer lange
cycluslengte, en verdeelt deze op een efficiënte manier in stromen en deelstromen.
ns-3 gebruikt dezelfde onderliggende random number generator als doet ns-2: de MRG32k3a
generator van Pierre L'Ecuyer. Een uitgebreide beschrijving is te vinden in
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. De MRG32k3a-generator
biedt 1.8x10^{19} onafhankelijke streams van willekeurige getallen, die elk bestaan uit
2.3x10^{15} substreams. Elke deelstroom heeft een punt (dat wil zeggen, het aantal willekeurige getallen
voor overlapping) van 7.6x10^{22}. De periode van de gehele generator is 3.1x10^{57}.
Klasse ns3::WillekeurigeVariabeleStream is de openbare interface naar dit onderliggende willekeurige getal
generator. Wanneer gebruikers nieuwe willekeurige variabelen maken (zoals ns3::UniformWillekeurigeVariabele,
ns3::ExponentieelWillekeurigeVariabele, enz.), creëren ze een object dat een van de
afzonderlijke, onafhankelijke streams van de random number generator. Daarom is elk object van
type dan: ns3::WillekeurigeVariabeleStream heeft, conceptueel, zijn eigen "virtuele" RNG. Verder,
elk ns3::WillekeurigeVariabeleStream kan worden geconfigureerd om een van de getekende substreams te gebruiken
uit de hoofdstroom.
Een alternatieve implementatie zou zijn om elke RandomVariable zijn eigen variabele te geven
(anders geplaatst) RNG. We kunnen echter niet zo sterk garanderen dat de verschillende
sequenties zouden in zo'n geval niet gecorreleerd zijn; daarom geven we er de voorkeur aan om een enkele RNG en te gebruiken
stromen en substromen ervan.
Wij creëren willekeurige variabelen
ns-3 ondersteunt een aantal willekeurige variabele objecten uit de basisklasse
WillekeurigeVariabeleStream. Deze objecten zijn afgeleid van ns3::Object en worden afgehandeld door smart
aanwijzers.
De juiste manier om deze objecten te maken, is door gebruik te maken van het templated CreëerObject<> methode,
zoals:
Ptr x = CreëerObject ();
dan heb je toegang tot waarden door methoden op het object aan te roepen, zoals:
myRandomNo = x-> GetInteger ();
Als je in plaats daarvan iets als dit probeert te doen:
myRandomNo = UniformRandomVariable().GetInteger ();
uw programma zal een segmentatiefout tegenkomen, omdat de implementatie afhankelijk is van
een attribuutconstructie die alleen voorkomt wanneer Object maken wordt genoemd.
Een groot deel van de rest van dit hoofdstuk bespreekt nu de eigenschappen van de stroom van
pseudo-willekeurige getallen die uit dergelijke objecten worden gegenereerd, en hoe het zaaien ervan kan worden gecontroleerd
voorwerpen.
Seeding en onafhankelijk replicaties
ns-3 simulaties kunnen worden geconfigureerd om deterministische of willekeurige resultaten te produceren. Als de
ns-3 simulatie is geconfigureerd om een vaste, deterministische seed te gebruiken met hetzelfde runnummer,
het zou elke keer dat het wordt uitgevoerd dezelfde uitvoer moeten geven.
Standaard ns-3 simulaties gebruiken een vast seed- en runnummer. Deze waarden worden opgeslagen in
twee ns3::Globale waarde gevallen: g_rngSeed en g_rngRun.
Een typische use case is om een simulatie uit te voeren als een opeenvolging van onafhankelijke proeven
statistieken berekenen over een groot aantal onafhankelijke runs. De gebruiker kan de
global seed en voer de simulatie opnieuw uit, of kan de substreamstatus van de RNG verbeteren, wat
wordt het ophogen van het runnummer genoemd.
Een klas ns3::RngSeedManager biedt een API om het seeding- en runnummer te regelen
gedrag. Deze seeding- en substream-statusinstelling moet worden aangeroepen voordat willekeurig wordt gestart
variabelen worden gemaakt; bijv.:
RngSeedManager::SetSeed (3); // Verandert seed van standaard 1 naar 3
RngSeedManager::SetRun (7); // Verandert runnummer van standaard 1 naar 7
// Maak nu willekeurige variabelen
Ptr x = CreëerObject ();
Ptr y = CreëerObject ();
...
Wat is beter, een nieuw zaadje plaatsen of de status van de substream bevorderen? Er is geen
garanderen dat de stromen geproduceerd door twee willekeurige zaden elkaar niet zullen overlappen. De enige manier om
garanderen dat twee streams elkaar niet overlappen, is door gebruik te maken van de substream-mogelijkheden van
de RNG-implementatie. daarom . the onderstroom bekwaamheid naar produceren meervoudig
onafhankelijk loopt of the dezelfde simulatie. Met andere woorden, hoe statistisch rigoureuzer
manier om meerdere onafhankelijke replicaties te configureren, is door een vaste seed te gebruiken en door te gaan
het runnummer. Deze implementatie maakt een maximum van 2.3x10^{15} onafhankelijk mogelijk
replicaties met behulp van de substreams.
Voor gebruiksgemak is het niet nodig om het zaad- en runnummer te regelen vanuit de
programma; de gebruiker kan de NS_GLOBAL_VALUE omgevingsvariabele als volgt:
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run programmanaam
Een andere manier om dit te regelen is door een opdrachtregelargument door te geven; aangezien dit een ns-3
GlobalValue-instantie, wordt het equivalent als volgt gedaan:
$ ./waf --command-template="%s --RngRun=3" --run programmanaam
of, als u programma's direct buiten waf uitvoert:
$ ./build/optimized/scratch/programmanaam --RngRun=3
De bovenstaande opdrachtregelvarianten maken het gemakkelijk om veel verschillende runs vanuit een shell uit te voeren
script door gewoon een andere RngRun-index door te geven.
Klasse WillekeurigeVariabeleStream
Alle willekeurige variabelen moeten uit klasse worden afgeleid Willekeurige variabele. Deze basisklasse biedt een
enkele methoden om het gedrag van de generator voor willekeurige getallen globaal te configureren. Afgeleid van
klassen bieden API voor het tekenen van willekeurige variaties van het specifieke distributiewezen
ondersteund.
Elke RandomVariableStream die in de simulatie wordt gemaakt, krijgt een nieuwe generator
RNGStream van de onderliggende PRNG. Op deze manier gebruikt, de implementatie van L'Ecuyer
staat maximaal 1.8x10^19 willekeurige variabelen toe. Elke willekeurige variabele in één
replicatie kan tot 7.6x10^22 willekeurige getallen produceren voordat ze elkaar overlappen.
Base klasse publiek API
Hieronder vindt u een uittreksel van enkele openbare methoden van klasse WillekeurigeVariabeleStream die toegang hebben tot de
volgende waarde in de substream.
/ **
* \brief Geeft een willekeurige verdubbeling van de onderliggende verdeling
* \return Een willekeurige waarde met drijvende komma
*/
dubbele GetValue (nietig) const;
/ **
* \brief Geeft een willekeurig geheel getal uit de onderliggende verdeling
* \return Integer cast van ::GetValue()
*/
uint32_t GetInteger (ongeldig) const;
We hebben de seeding-configuratie hierboven al beschreven. Verschillende WillekeurigeVariabele
subklassen kunnen extra API hebben.
Types of Willekeurige variabelen
De volgende soorten willekeurige variabelen worden geleverd en zijn gedocumenteerd in de ns-3
Doxygen of door te lezen src/core/model/willekeurige-variabele-stream.h. Gebruikers kunnen ook creëren
hun eigen aangepaste willekeurige variabelen door ze af te leiden uit klasse WillekeurigeVariabeleStream.
· klas UniformRandomVariabele
· klas ConstantWillekeurigeVariabele
· klas Sequentiële Willekeurige Variabele
· klas Exponentiële Willekeurige Variabele
· klas Paretowillekeurige variabele
· klas Weibull Willekeurige Variabele
· klas NormaalRandomVariabele
· klas LogNormaalWillekeurigeVariabele
· klas Gamma Willekeurige Variabele
· klas Erlang Willekeurige Variabele
· klas DriehoekigRandomVariabel
· klas ZipfRandomVariabele
· klas Zeta Willekeurige Variabele
· klas DeterministischWillekeurigeVariabele
· klas Empirische Willekeurige Variabele
Semantiek of WillekeurigeVariabeleStream objecten
RandomVariableStream-objecten zijn afgeleid van ns3::Object en worden afgehandeld door slimme aanwijzers.
RandomVariableStream-instanties kunnen ook worden gebruikt in ns-3 attributen, wat betekent dat
waarden kunnen voor hen worden ingesteld via de ns-3 attributen systeem. Een voorbeeld staat in de
verspreidingsmodellen voor WifiNetDevice:
TypeId
RandomPropagationDelayModel::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetOuder ()
.Constructor toevoegen ()
.AddAttribute ("Variabele",
"De willekeurige variabele die willekeurige vertragingen (s) genereert",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MaakPointerChecker ())
;
tijd teruggeven;
}
Hier de ns-3 gebruiker kan de standaard willekeurige variabele voor dit vertragingsmodel wijzigen (dat is
een UniformRandomVariable variërend van 0 tot 1) via het attribuutsysteem.
gebruik Overige PRNG
Er is momenteel geen ondersteuning voor het vervangen van een ander onderliggend willekeurig getal
generator (bijv. de GNU Scientific Library of het Akaroa-pakket). Pleisters zijn welkom.
omgeving the stream aantal
De onderliggende MRG32k3a-generator levert 2^64 onafhankelijke streams. In ns-3 zijn dit
opeenvolgend toegewezen vanaf de eerste stream als nieuwe RandomVariableStream-instanties
maken hun eerste aanroep naar GetValue().
Als resultaat van hoe deze RandomVariableStream-objecten worden toegewezen aan onderliggende streams,
de toewijzing is gevoelig voor verstoringen van de simulatieconfiguratie. De
consequentie is dat als enig aspect van de simulatieconfiguratie wordt gewijzigd, de mapping
van willekeurige variabelen voor streams kan (of niet) veranderen.
Als een concreet voorbeeld kan een gebruiker die een vergelijkend onderzoek uitvoert tussen routeringsprotocollen
ontdek dat de handeling van het veranderen van het ene routeringsprotocol voor het andere zal opmerken dat de
ook het onderliggende mobiliteitspatroon veranderde.
Vanaf ns-3.15 is er enige controle aan gebruikers gegeven om gebruikers toe te staan
repareer optioneel de toewijzing van geselecteerde RandomVariableStream-objecten aan onderliggende objecten
stromen. Dit is de Stroom attribuut, onderdeel van de basisklasse RandomVariableStream.
Door de bestaande reeks streams van vroeger te partitioneren:
<---------------------------------------------------------------- ------------------------>
stroom 0 stroom (2^64 - 1)
in twee sets van gelijke grootte:
<---------------------------------------------------------------- ------------------------>
^ ^^ ^
| || |
stroom 0 stroom (2^63 - 1) stroom 2^63 stroom (2^64 - 1)
<- automatisch toegewezen -----------><- toegewezen door gebruiker --->
De eerste 2^63 streams worden nog steeds automatisch toegewezen, terwijl de laatste 2^63 dat wel zijn
gegeven stroomindexen beginnend met nul tot 2^63-1.
Het toewijzen van streams aan een vast streamnummer is optioneel; Instanties van
RandomVariableStream waaraan geen streamwaarde is toegewezen, wordt de volgende toegewezen
een uit de pool van automatische streams.
Om een RandomVariableStream aan een bepaalde onderliggende stream te koppelen, wijst u zijn Stroom
attribuut toe aan een niet-negatief geheel getal (de standaardwaarde van -1 betekent dat een waarde
automatisch toegewezen).
Reclame jouw resultaat
Wanneer u simulatieresultaten publiceert, wordt een belangrijk stuk configuratie-informatie dat u
moet altijd aangeven hoe u de generator voor willekeurige getallen hebt gebruikt.
· welke zaden je hebt gebruikt,
· welke RNG je hebt gebruikt, zo niet de standaard,
· hoe werden onafhankelijke runs uitgevoerd,
· hoe heb je bij grote simulaties gecontroleerd of je niet hebt gefietst?
Het is de taak van de onderzoeker die resultaten publiceert om voldoende informatie op te nemen
anderen in staat te stellen zijn of haar resultaten te reproduceren. Dat is ook de taak van de onderzoeker
zichzelf ervan overtuigen dat de gebruikte willekeurige getallen statistisch geldig waren, en om in te geven
het papier waarom een dergelijk vertrouwen wordt aangenomen.
Samenvatting
Laten we eens kijken wat u moet doen bij het maken van een simulatie.
· Bepaal of je met een vaste seed of willekeurige seed loopt; een vast zaadje is de
standaard,
· Bepaal hoe u onafhankelijke replicaties gaat beheren, indien van toepassing,
· Overtuig uzelf ervan dat u niet meer willekeurige waarden tekent dan de cycluslengte, als
je voert een zeer lange simulatie uit, en
· Volg bij het publiceren de bovenstaande richtlijnen voor het documenteren van uw gebruik van willekeurige gegevens
nummer generator.
Hachee Functies
ns-3 biedt een generieke interface voor hashfuncties voor algemeen gebruik. In de eenvoudigste
gebruik, retourneert de hashfunctie de 32-bits of 64-bits hash van een gegevensbuffer of string.
De standaard onderliggende hashfunctie is gemompel3, gekozen omdat het een goede hash-functie heeft
eigenschappen en biedt een 64-bits versie. De eerbiedwaardige FNV1a hasj is ook beschikbaar.
Er is een ongecompliceerd mechanisme om alternatieve hash toe te voegen (of tijdens runtime aan te bieden).
functie implementaties.
Basic Gebruik
De eenvoudigste manier om een hashwaarde van een databuffer of string te krijgen is gewoon:
#include "ns3/hash.h"
naamruimte ns3 gebruiken;
char * buffer = ...
size_t buffer_size = ...
uint32_t buffer_hash = Hash32 (buffer, buffer_grootte);
std::tekenreeks s;
uint32_t string_hash = Hash32 (s);
Equivalente functies zijn gedefinieerd voor 64-bit hash-waarden.
Incrementeel Hashing
In sommige situaties is het handig om de hash van meerdere buffers te berekenen, alsof ze die hadden
zijn samengevoegd. (U wilt bijvoorbeeld de hash van een pakketstroom, maar niet
wil een enkele buffer samenstellen met de gecombineerde inhoud van alle pakketten.)
Dit is bijna net zo eenvoudig als het eerste voorbeeld:
#include "ns3/hash.h"
naamruimte ns3 gebruiken;
char * buffer;
size_t buffer_size;
Hasher hasher; // Gebruik standaard hash-functie
voor ( )
{
buffer = krijg_volgende_buffer ();
hasher (buffer, buffergrootte);
}
uint32_t gecombineerde_hash = hasher.GetHash32 ();
Standaard Hasjer behoudt de interne status om incrementele hashing mogelijk te maken. Als je wilt
hergebruiken een Hasjer object (bijvoorbeeld omdat het is geconfigureerd met een niet-standaard hash
functie), maar niet wilt toevoegen aan de eerder berekende hash, moet u Doorzichtig()
eerste:
hasher.clear ().GetHash32 (buffer, buffergrootte);
Hiermee wordt de interne status opnieuw geïnitialiseerd voordat de buffer wordt gehasht.
gebruik an Alternatief Hachee Functie
De standaard hash-functie is gemompel3. FNV1a is ook beschikbaar. Om de hash te specificeren
gebruik expliciet deze constructor:
Hasher hasher = Hasher (Create ());
Het toevoegen van New Hachee Functie implementaties
Om de hash-functie toe te voegen foo, Volg de hash-murmur3.h/. Cc patroon:
· Maak een klassendeclaratie (.h) en definitie (. Cc) erven van
Hash::Implementatie.
· omvatten de aangifte erin hasj.h (op het punt waar hash-murmur3.h inbegrepen.
· In uw eigen code instantiëren a Hasjer object via de constructor Hasjer
(Ptr ())
Als uw hash-functie een enkele functie is, bijv hashf, je hoeft niet eens een
nieuwe klasse afgeleid van HashImplementation:
Hasjer hasher =
Hasher (Maak (&hashf) );
Om dit te compileren, uw hashf moet overeenkomen met een van de handtekeningen van de functieaanwijzer:
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
Bronnen besteld, Hachee Functies
Bronnen voor andere hashfunctie-implementaties zijn onder meer:
· Peter Kankowski: http://www.strchr.com
· Arash Partow: http://www.partow.net/programming/hashfunctions/index.html
· SMHasher: http://code.google.com/p/smhasher/
· Sanmayce: http://www.sanmayce.com/Fastest_Hash/index.html
Events en Simulator
ns-3 is een discrete-event netwerksimulator. Conceptueel houdt de simulator a
het aantal gebeurtenissen dat is gepland om te worden uitgevoerd op een opgegeven simulatietijd. De taak van
de simulator moet de gebeurtenissen in sequentiële tijdsvolgorde uitvoeren. Zodra de voltooiing van
een gebeurtenis plaatsvindt, gaat de simulator naar de volgende gebeurtenis (of sluit af als er geen
meer gebeurtenissen in de gebeurteniswachtrij). Als er bijvoorbeeld een evenement is gepland voor simulatietijd
"100 seconden" wordt uitgevoerd en de volgende gebeurtenis wordt pas na "200 seconden" gepland
simulator springt onmiddellijk van 100 seconden naar 200 seconden (simulatietijd) naar
voer het volgende evenement uit. Dit is wat wordt bedoeld met de "discrete-event"-simulator.
Om dit allemaal mogelijk te maken, heeft de simulator een paar dingen nodig:
1. een simulatorobject dat toegang heeft tot een gebeurteniswachtrij waar gebeurtenissen worden opgeslagen en dat kan
aansturen van de uitvoering van evenementen
2. een planner die verantwoordelijk is voor het invoegen en verwijderen van gebeurtenissen uit de wachtrij
3. een manier om de simulatietijd weer te geven
4. de evenementen zelf
Dit hoofdstuk van de handleiding beschrijft deze fundamentele objecten (simulator, planner,
tijd, evenement) en hoe ze worden gebruikt.
Gebeurtenis
Naar be voltooid
Simulator
De klasse Simulator is het openbare toegangspunt voor toegang tot faciliteiten voor het plannen van evenementen. Eenmaal
er zijn een aantal evenementen gepland om de simulatie te starten, de gebruiker kan beginnen
voer ze uit door de hoofdlus van de simulator binnen te gaan (call Simulator::Uitvoeren). Zodra de belangrijkste lus
begint te lopen, zal het achtereenvolgens alle geplande gebeurtenissen uitvoeren in volgorde van oudste tot
meest recent totdat er ofwel geen gebeurtenissen meer in de gebeurteniswachtrij staan of
Simulator::Stop is opgeroepen.
Om gebeurtenissen te plannen voor uitvoering door de hoofdlus van de simulator, biedt de Simulator-klasse
de Simulator::Schedule* familie van functies.
1. Omgaan met gebeurtenishandlers met verschillende handtekeningen
Deze functies worden gedeclareerd en geïmplementeerd als C++-sjablonen om automatisch de
grote verscheidenheid aan handtekeningen voor C++-gebeurtenishandlers die in het wild worden gebruikt. Bijvoorbeeld om een afspraak te maken
gebeurtenis om 10 seconden in de toekomst uit te voeren en een C++-methode of -functie aan te roepen
specifieke argumenten, zou je dit kunnen schrijven:
ongeldige handler (int arg0, int arg1)
{
std::cout << "handler aangeroepen met argument arg0=" << arg0 << " en
arg1=" << arg1 << std::endl;
}
Simulator::Schema(seconden(10), &handler, 10, 5);
Welke uitvoer:
handler aangeroepen met argument arg0=10 en arg1=5
Natuurlijk kunnen deze C++-sjablonen ook transparante lidmethoden in C++ aan
voorwerpen:
Naar be voltooid: lid methode voorbeeld
Opmerkingen:
· de ns-3 Schedule-methoden herkennen automatisch alleen functies en methoden als ze
neem minder dan 5 argumenten. Als u ze nodig heeft om meer argumenten te ondersteunen, dient u a.u.b. een
bug report.
· Lezers die bekend zijn met de term 'volledig gebonden functoren' zullen de
Simulator::Schedule-methoden als een manier om dergelijke objecten automatisch te construeren.
2. Algemene planningsbewerkingen
De Simulator API is ontworpen om het heel eenvoudig te maken om de meeste evenementen te plannen. Het
biedt hiervoor drie varianten (gerangschikt van meest gebruikt naar minst gebruikt):
· Plan methoden waarmee u een evenement in de toekomst kunt plannen door de
vertraging tussen de huidige simulatietijd en de vervaldatum van de doelgebeurtenis.
· ScheduleNow-methoden waarmee u een evenement kunt plannen voor de huidige simulatie
tijd: ze zullen worden uitgevoerd _nadat_ de huidige gebeurtenis is voltooid, maar _voor_ de
simulatietijd wordt gewijzigd voor de volgende gebeurtenis.
· ScheduleDestroy-methoden waarmee u het afsluitproces van de Simulator kunt inhaken
om simulatieresources op te schonen: elke 'destroy'-gebeurtenis wordt uitgevoerd wanneer de gebruiker aanroept
de Simulator::Destroy-methode.
3. Behoud van de simulatiecontext
Er zijn twee basismanieren om evenementen te plannen, met en zonder verband. Wat doet dit
gemiddelde?
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
vs
Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
Lezers die tijd en moeite investeren in het ontwikkelen of gebruiken van een niet-triviaal simulatiemodel
zal de waarde kennen van het ns-3 logging framework om eenvoudige en complexe simulaties te debuggen
gelijk. Een van de belangrijke functies die door dit logging-framework wordt geboden, is de
automatische weergave van de netwerkknooppunt-ID die is gekoppeld aan de 'momenteel' lopende gebeurtenis.
Het knooppunt-ID van het netwerkknooppunt dat momenteel wordt uitgevoerd, wordt in feite gevolgd door de simulator
klas. Het is toegankelijk met de Simulator::GetContext-methode die het
'context' (een 32-bits geheel getal) gekoppeld aan en opgeslagen in de gebeurtenis die momenteel wordt uitgevoerd. In
sommige zeldzame gevallen, wanneer een gebeurtenis niet is gekoppeld aan een specifiek netwerkknooppunt, is het
'context' is ingesteld op 0xffffffff.
Om een context aan elke gebeurtenis te koppelen, worden de methoden Schedule en ScheduleNow automatisch gebruikt
hergebruik de context van de momenteel uitgevoerde gebeurtenis als de context van de geplande gebeurtenis
voor latere uitvoering.
In sommige gevallen, met name bij het simuleren van de verzending van een pakket van een knooppunt naar
een ander, dit gedrag is ongewenst aangezien de verwachte context van de ontvangstgebeurtenis dat is
die van het ontvangende knooppunt, niet het verzendende knooppunt. Om dit probleem te voorkomen, is de Simulator
class biedt een specifieke planningsmethode: ScheduleWithContext waarmee men kan voorzien
expliciet de knooppunt-ID van het ontvangende knooppunt dat is gekoppeld aan de ontvangstgebeurtenis.
XXX: code voorbeeld
In enkele zeer zeldzame gevallen moeten ontwikkelaars mogelijk de context wijzigen of begrijpen
(node id) van de eerste gebeurtenis wordt ingesteld op die van de bijbehorende node. Dit is gelukt
door de klasse NodeList: telkens wanneer een nieuw knooppunt wordt gemaakt, gebruikt de klasse NodeList
ScheduleWithContext om een 'initialize'-gebeurtenis voor dit knooppunt te plannen. De gebeurtenis 'initialiseren'
wordt dus uitgevoerd met een context die is ingesteld op die van de node-id en kan de normale variant van gebruiken
Plan methoden. Het roept de methode Node::Initialize aan die de 'initialize' propageert
gebeurtenis door de methode DoInitialize aan te roepen voor elk object dat aan het knooppunt is gekoppeld. De
DoInitialize-methode overschreven in sommige van deze objecten (met name in Application
basisklasse) zal enkele gebeurtenissen plannen (met name Application::StartApplication) welke
zal op zijn beurt gebeurtenissen voor het genereren van verkeer plannen, die op hun beurt zullen plannen
evenementen op netwerkniveau.
Opmerkingen:
· Gebruikers moeten voorzichtig zijn bij het verspreiden van DoInitialize-methoden over objecten door ze aan te roepen
Initialiseer expliciet op hun lidobjecten
· De context-ID die aan elke ScheduleWithContext-methode is gekoppeld, heeft nog andere toepassingen
logging: het wordt gebruikt door een experimentele tak van ns-3 om parallelle simulatie op uit te voeren
multicore-systemen die gebruikmaken van multithreading.
De Simulator::*-functies weten niet wat de context is: ze zorgen er alleen maar voor
welke context u ook opgeeft met ScheduleWithContext is beschikbaar wanneer de corresponderende
gebeurtenis wordt uitgevoerd met ::GetContext.
Het is aan de modellen die bovenop Simulator::* zijn geïmplementeerd om de contextwaarde te interpreteren.
In ns-3 interpreteren de netwerkmodellen de context als de knooppunt-ID van het knooppunt dat
een evenement gegenereerd. Daarom is het belangrijk om ScheduleWithContext aan te roepen
ns3::Kanaalsubklassen omdat we een gebeurtenis genereren van knooppunt i naar knooppunt j en we
wil er zeker van zijn dat de gebeurtenis die op knooppunt j wordt uitgevoerd, de juiste context heeft.
Tijd
Naar be voltooid
Scheduler
Naar be voltooid
Terugbelverzoeken
Enkele nieuwe gebruikers ns-3 niet bekend zijn met een veel gebruikt programmeeridioom
in de code: de ns-3 Bel terug. Dit hoofdstuk geeft enige motivatie over de
callback, richtlijnen voor het gebruik ervan en details over de implementatie ervan.
Terugbelverzoeken Motivatie
Bedenk dat u twee simulatiemodellen A en B heeft en deze wilt laten passeren
informatie tussen hen tijdens de simulatie. Een manier waarop u dat kunt doen, is dat u
kan A en B elk expliciet op de hoogte brengen van de ander, zodat ze kunnen aanroepen
methodes op elkaar:
klasse A, eerste klasse {
publiek:
void ReceiveInput (//parameters);
...
}
(in een ander bronbestand:)
klasse B {
publiek:
leegte DoSomething (leegte);
...
private:
A* een_exemplaar; // pointer naar een A
}
komen te vervallen
B::DoeIets()
{
// Vertel een instantie dat er iets is gebeurd
a_instance->ReceiveInput (// parameters);
...
}
Dit werkt zeker, maar heeft als nadeel dat het een afhankelijkheid van A en B introduceert
om meer over de ander te weten tijdens het compileren (dit maakt het moeilijker om onafhankelijk
compilatie-eenheden in de simulator) en is niet gegeneraliseerd; als in een later gebruiksscenario,
B moet met een heel ander C-object praten, de broncode voor B moet dat zijn
gewijzigd om een toe te voegen c_exemplaar enzovoorts. Het is gemakkelijk in te zien dat dit een brute kracht is
communicatiemechanisme dat kan leiden tot programmeerfouten in de modellen.
Dit wil niet zeggen dat objecten elkaar niet zouden moeten kennen als er een moeilijkheid is
afhankelijkheid tussen hen, maar dat het model vaak flexibeler kan worden gemaakt als dat het geval is
interacties zijn minder beperkt tijdens het compileren.
Dit is geen abstract probleem voor onderzoek naar netwerksimulatie, maar eerder een
bron van problemen in eerdere simulatoren, wanneer onderzoekers het willen uitbreiden of wijzigen
systeem om verschillende dingen te doen (zoals ze geneigd zijn te doen in onderzoek). Denk bijvoorbeeld aan
een gebruiker die een sublaag van het IPsec-beveiligingsprotocol wil toevoegen tussen TCP en IP:
------------ -----------
| Tcp | | Tcp |
------------ -----------
| wordt -> |
----------- -----------
| IP | | Ipsec |
----------- -----------
|
-----------
| IP |
-----------
Als de simulator aannames heeft gedaan en hard in de code is gecodeerd, praat dat IP altijd
naar een transportprotocol hierboven, kan de gebruiker gedwongen worden het systeem te hacken om het
gewenste onderlinge verbindingen. Dit is duidelijk geen optimale manier om een generiek te ontwerpen
simulator.
Terugbelverzoeken Achtergrond
NOTITIE:
Lezers die bekend zijn met het programmeren van callbacks kunnen dit zelfstudiegedeelte overslaan.
Het basismechanisme dat het mogelijk maakt om het bovenstaande probleem aan te pakken, staat bekend als a Bel terug.
Het uiteindelijke doel is om één stuk code een functie (of methode in C++) te laten aanroepen
zonder enige specifieke afhankelijkheid tussen modules.
Dit betekent uiteindelijk dat je een soort van indirectheid nodig hebt - je behandelt het adres van de
functie genoemd als een variabele. Deze variabele wordt een pointer-naar-functievariabele genoemd.
De relatie tussen functie en pointer-to-function pointer is eigenlijk niet anders
die van object en pointer-to-object.
In C is het canonieke voorbeeld van een pointer-to-functie a
pointer-to-function-returning-integer (PFI). Voor een PFI die één int-parameter gebruikt, is dit
kan worden verklaard als:
int (*pfi)(int arg) = 0;
Wat u hieruit krijgt, is een variabele met de eenvoudige naam pfi die is geïnitialiseerd op de waarde 0.
Als u deze aanwijzer naar iets zinvols wilt initialiseren, moet u een
functie met een bijpassende signatuur. In dit geval:
int MijnFunctie (int arg) {}
Als u dit doel hebt, kunt u de variabele initialiseren om naar uw functie te verwijzen, zoals:
pfi = MijnFunctie;
U kunt MyFunction dan indirect aanroepen met behulp van de meer suggestieve vorm van de oproep:
int resultaat = (*pfi) (1234);
Dit is suggestief omdat het lijkt alsof u alleen de verwijzing naar de functieaanwijzer ongedaan maakt
alsof je elke aanwijzer zou derefereren. Meestal maken mensen echter misbruik van de
feit dat de compiler weet wat er aan de hand is en alleen een kortere vorm zal gebruiken:
int resultaat = pfi (1234);
Merk op dat de functie pointer gehoorzaamt aan de semantiek van waarden, dus je kunt hem doorgeven zoals elke andere
andere waarde. Wanneer u een asynchrone interface gebruikt, passeert u meestal een entiteit
like this naar een functie die een actie zal uitvoeren en Bellen terug om het je te laten weten
voltooid. Het roept terug door de indirectie te volgen en de opgegeven functie uit te voeren.
In C++ heb je de toegevoegde complexiteit van objecten. De analogie met de PFI hierboven betekent jou
een pointer hebben naar een lidfunctie die een int (PMI) retourneert in plaats van de pointer naar
functie retourneert een int (PFI).
De declaratie van de variabele die de richting geeft, ziet er slechts iets anders uit:
int (MijnKlasse::*pmi) (int arg) = 0;
Dit declareert een variabele met de naam pmi net zoals in het vorige voorbeeld een variabele met de naam gedeclareerd
pfi. Aangezien het de bedoeling is om een methode van een instantie van een bepaalde klasse aan te roepen, moet men dat doen
declareer die methode in een klasse:
klasse MijnKlasse {
publiek:
int MijnMethode (int arg);
};
Gegeven deze klassedeclaratie, zou men die variabele als volgt initialiseren:
pmi = &MijnKlasse::MijnMethode;
Dit wijst het adres toe van de code die de methode implementeert aan de variabele, compleet
de indirectie. Om een methode aan te roepen, heeft de code een dit wijzer. Dit op zijn beurt
betekent dat er een object van MyClass moet zijn om naar te verwijzen. Een simplistisch voorbeeld hiervan is rechtvaardig
een methode indirect aanroepen (denk aan virtuele functie):
int (MijnKlasse::*pmi) (int arg) = 0; // Declareer een PMI
pmi = &MijnKlasse::MijnMethode; // Wijs naar de implementatiecode
MijnKlasse mijnKlasse; // Heb een instantie van de klasse nodig
(mijnKlasse.*pmi) (1234); // Roep de methode aan met een object ptr
Net als in het C-voorbeeld kunt u dit gebruiken in een asynchrone aanroep naar een andere module
welke zal Bellen terug met behulp van een methode en een objectwijzer. De eenvoudige extensie
men zou kunnen overwegen om een pointer door te geven aan het object en de PMI-variabele. De module
zou gewoon doen:
(*objectPtr.*pmi) (1234);
om de callback op het gewenste object uit te voeren.
Men zou zich in deze tijd kunnen afvragen wat is the punt? De opgeroepen module zal het moeten begrijpen
het concrete type van het aanroepende object om correct terug te bellen. Waarom niet
accepteer dit gewoon, geef de correct getypte objectaanwijzer door en doe het voorwerp->Methode(1234) in
de code in plaats van de callback? Dit is precies het hierboven beschreven probleem. Wat is
nodig is een manier om de aanroepende functie volledig los te koppelen van de aangeroepen klasse. Dit
vereiste leidde tot de ontwikkeling van de functor.
Een functor is de uitgroei van iets dat in de jaren zestig is uitgevonden en een sluiting wordt genoemd. Het is
eigenlijk gewoon een verpakte functieaanroep, mogelijk met een bepaalde status.
Een functor heeft twee delen, een specifiek deel en een generiek deel, gerelateerd door overerving.
De aanroepende code (de code die de callback uitvoert) voert een generieke overbelasting uit
operator () van een generieke functor om ervoor te zorgen dat de callback wordt aangeroepen. De opgeroepen code (de
code die teruggebeld wil worden) zal een gespecialiseerde implementatie moeten verzorgen van
the operator () die het klassespecifieke werk uitvoert dat de nauwe koppeling veroorzaakte
probleem hierboven.
Met de specifieke functor en zijn overbelasting operator () aangemaakt, de aangeroepen code dan
geeft de gespecialiseerde code aan de module die de callback zal uitvoeren (het calling
code).
De aanroepende code neemt een generieke functor als parameter, dus er wordt een impliciete cast uitgevoerd
in de functieaanroep om de specifieke functor om te zetten in een generieke functor. Dit betekent
dat de aanroepende module alleen het generieke functortype hoeft te begrijpen. Het is ontkoppeld
van de aanroepende code volledig.
De informatie die men nodig heeft om een specifieke functor te maken, is de objectwijzer en de
pointer-naar-methode adres.
De essentie van wat er moet gebeuren is dat het systeem een generiek deel van de
functie:
sjabloon
klasse Functor
{
publiek:
virtuele int-operator() (T arg) = 0;
};
De aanroeper definieert een specifiek deel van de functor dat er eigenlijk alleen maar is om te implementeren
de specifieke exploitant() methode:
sjabloon
klasse SpecificFunctor : openbare functor
{
publiek:
SpecifiekeFunctor(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}
virtuele int-operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
private:
int (T::*m_pmi)(ARG arg);
T* m_p;
};
Hier is een voorbeeld van het gebruik:
klasse A, eerste klasse
{
publiek:
A (int a0) : a (a0) {}
int Hallo (int b0)
{
std::cout << "Hallo van A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};
int main ()
{
A a(10);
Specifieke functie sf(&a, &A::Hallo);
sf(5);
}
NOTITIE:
De vorige code is geen echte ns-3-code. Het is simplistische voorbeeldcode die alleen wordt gebruikt
illustreren de betrokken concepten en helpen u het systeem beter te begrijpen. Niet doen
verwacht deze code ergens in de ns-3-boom te vinden.
Merk op dat er twee variabelen zijn gedefinieerd in de klasse hierboven. De variabele m_p is de
object pointer en m_pmi is de variabele die het adres bevat van de functie to
uitvoeren.
Merk op dat wanneer exploitant() wordt aangeroepen, roept het op zijn beurt de methode aan die bij de
objectaanwijzer met behulp van de C++ PMI-syntaxis.
Om dit te gebruiken, zou men dan een modelcode kunnen declareren die een generieke functor als a neemt
parameter:
leegte LibraryFunction (Functor-functie);
De code die met het model zal praten, bouwt een specifieke functor en geeft deze door
Bibliotheekfunctie:
MijnKlasse mijnKlasse;
Specifieke functie functor (&mijnklas, MijnKlasse::MijnMethode);
. Bibliotheekfunctie is gedaan, voert het de callback uit met behulp van de exploitant() op de generieke
functor is doorgegeven, en levert in dit specifieke geval het integer-argument:
komen te vervallen
BibliotheekFunctie (Functor-functie)
{
// Voer de bibliotheekfunctie uit
functie(1234);
}
Let erop dat Bibliotheekfunctie is volledig losgekoppeld van het specifieke type klant.
De verbinding wordt gemaakt door het Functor-polymorfisme.
De Callback-API in ns-3 implementeert objectgeoriënteerde callbacks met behulp van het functiemechanisme.
Deze callback-API, gebaseerd op C++-sjablonen, is typeveilig; dat wil zeggen, het presteert statisch
typecontroles om de juiste handtekeningcompatibiliteit tussen bellers en callees af te dwingen. Het is
daarom meer typeveilig in gebruik dan traditionele functieaanwijzers, maar de syntaxis wel
ziet er op het eerste gezicht imposant uit. Dit gedeelte is bedoeld om u door het terugbelsysteem te leiden
zodat u het comfortabel kunt gebruiken ns-3.
gebruik the Terugbellen API
De Callback API is vrij minimaal en biedt slechts twee services:
1. callback type declaratie: een manier om een type callback aan te geven met een bepaalde handtekening,
en,
2. callback-instantiëring: een manier om een door een sjabloon gegenereerde doorschakelcallback te instantiëren
die alle oproepen kan doorsturen naar een andere C++ class member-methode of C++-functie.
Dit kan het beste worden waargenomen door een voorbeeld te doorlopen op basis van samples/main-callback.cc.
gebruik the Terugbellen API with statisch functies
Overweeg een functie:
statisch dubbel
CbOne (dubbel a, dubbel b)
{
std::cout << "roep cbOne aan a=" << a << ", b=" << b << std::endl;
retourneer een;
}
Beschouw ook het volgende hoofdprogrammafragment:
int hoofd (int argc, char *argv[])
{
// retourtype: dubbel
// eerste arg-type: dubbel
// tweede arg-type: dubbel
Bel terug een;
}
Dit is een voorbeeld van een callback in C-stijl -- een die geen a bevat of nodig heeft dit
wijzer. Het functiesjabloon Terugbellen is in wezen de declaratie van de variabele
met daarin de pointer-to-functie. In het bovenstaande voorbeeld hebben we expliciet een aanwijzer getoond
naar een functie die een geheel getal retourneerde en een enkel geheel getal als parameter nam, The
Terugbellen sjabloonfunctie is daar een generieke versie van - het wordt gebruikt om het type te declareren
van een terugroepactie.
NOTITIE:
Lezers die niet bekend zijn met C++-sjablonen kunnen dit raadplegen
http://www.cplusplus.com/doc/tutorial/templates/.
De Terugbellen template vereist één verplicht argument (het retourtype van de functie to
worden toegewezen aan deze callback) en maximaal vijf optionele argumenten, die elk de
type argumenten (als uw specifieke callback-functie meer dan vijf argumenten heeft,
dan kan dit worden afgehandeld door de callback-implementatie uit te breiden).
Dus in het bovenstaande voorbeeld hebben we een callback aangekondigd met de naam "one" die uiteindelijk zal gebeuren
houd een functiewijzer vast. De handtekening van de functie die het zal bevatten, moet terugkeren
double en moet twee dubbele argumenten ondersteunen. Als men probeert een functie door te geven waarvan
handtekening niet overeenkomt met de gedeclareerde callback, zal er een compilatiefout optreden. Ook als
men probeert een incompatibele callback aan een callback toe te wijzen, de compilatie zal slagen, maar a
runtime NS_FATAL_ERROR wordt verhoogd. Het voorbeeldprogramma
src/core/examples/main-callback.cc demonstreert beide foutgevallen aan het einde van
the hoofd() programma.
Nu moeten we deze callback-instantie en de daadwerkelijke doelfunctie aan elkaar koppelen
(CbOne). Merk hierboven op dat CbOne dezelfde typen functiehandtekeningen heeft als de callback--
dit is belangrijk. We kunnen elke correct getypte functie doorgeven aan deze callback.
Laten we dit nader bekijken:
statisch dubbel CbOne (dubbel a, dubbel b) {}
^ ^ ^
| | |
| | |
Bel terug een;
U kunt een functie alleen aan een callback binden als deze de overeenkomende handtekening heeft. De eerste
sjabloonargument is het retourtype en de aanvullende sjabloonargumenten zijn de typen
van de argumenten van de functiehandtekening.
Laten we nu onze callback "one" binden aan de functie die overeenkomt met de handtekening:
// bouw een callback-instantie die verwijst naar de cbOne-functie
één = MakeCallback (&CbOne);
Deze oproep aan MaakTerugbellen is in wezen het creëren van een van de gespecialiseerde functors
hierboven genoemd. De variabele gedeclareerd met behulp van de Terugbellen sjabloonfunctie gaat
de rol van de generieke functor spelen. De opdracht een = MaakTerugbellen (&CbOne) is
de cast die de gespecialiseerde functor die bekend is bij de callee omzet in een generieke functor
bekend bij de beller.
Later in het programma, als terugbellen nodig is, kan dit als volgt worden gebruikt:
NS_ASSERT (!één.IsNull ());
// Roep de cbOne-functie aan via een callback-instantie
dubbele retEen;
retOne = één (10.0, 20.0);
De cheque voor Is niets() zorgt ervoor dat de callback niet null is - dat er een functie is
om achter deze callback te bellen. Dan, een() voert het generieke uit exploitant() dat is echt
overladen met een specifieke implementatie van exploitant() en geeft hetzelfde resultaat als if
CbEen() was direct gebeld.
gebruik the Terugbellen API with lid functies
Over het algemeen roept u geen statische functies aan, maar in plaats daarvan openbare lidfuncties van
een voorwerp. In dit geval is er een extra argument nodig voor de MakeCallback-functie, to
vertel het systeem op welk object de functie moet worden aangeroepen. Beschouw dit voorbeeld,
ook van main-callback.cc:
klasse MyCb {
publiek:
int CbTwo (dubbel a) {
std::cout << "roep cbTwo a=" << a << std::endl;
retour -5;
}
};
int hoofd ()
{
...
// retourtype: int
// eerste arg-type: dubbel
Bel terug twee;
MijnCb cb;
// bouw een callback-instantie die verwijst naar MyCb::cbTwo
twee = MakeCallback (&MyCb::CbTwo, &cb);
...
}
Hier geven we een extra objectwijzer door aan de MaakTerugbellen<> functie. Terugroepen van
het achtergrondgedeelte daarboven operator() zal de syntaxis van de aanwijzer naar lid gebruiken wanneer het
wordt uitgevoerd op een object:
virtuele int-operator() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
En dus moesten we de twee variabelen (m_p en m_pmi) toen we het specifieke maakten
functie. De lijn:
twee = MakeCallback (&MyCb::CbTwo, &cb);
doet precies dat. In dit geval wanneer twee () wordt aangeroepen:
int resultaat = twee (1.0);
zal resulteren in een oproep aan de CbTwee lidfunctie (methode) op het object waarnaar wordt verwezen
&cb.
Gebouw Null Terugbelverzoeken
Het is mogelijk dat callbacks null zijn; daarom kan het verstandig zijn om te controleren voordat u ze gebruikt.
Er is een speciale constructie voor een null-callback, die de voorkeur heeft boven gewoon passen
"0" als argument; het is de MakeNullCallback<> construct:
twee = MakeNullCallback ();
NS_ASSERT (twee.IsNull ());
Het aanroepen van een null-callback is net als het aanroepen van een null-functieaanwijzer: het zal crashen op
looptijd.
Gebonden Terugbelverzoeken
Een zeer nuttige uitbreiding van het functorconcept is dat van een Bound Callback. Voorheen het
werd vermeld dat sluitingen oorspronkelijk functieoproepen waren die voor later waren verpakt
executie. Merk op dat er in alle bovenstaande Callback-beschrijvingen geen manier is om dit te doen
verpak alle parameters voor later gebruik -- wanneer de Terugbellen wordt via gebeld exploitant().
Alle parameters worden geleverd door de aanroepende functie.
Wat als het gewenst is om de clientfunctie (degene die de callback verzorgt) toe te staan
enkele parameters geven? Alexandrescu noemt het proces van het toelaten van een cliënt
geef een van de parameters op "verbindend". Een van de parameters van exploitant() is
gebonden (gefixeerd) door de klant.
Een deel van onze pcap-traceercode is hier een mooi voorbeeld van. Er is een functie die
moet worden gebeld wanneer een pakket wordt ontvangen. Deze functie roept een object aan dat
schrijft het pakket daadwerkelijk naar schijf in het pcap-bestandsformaat. De handtekening van een van deze
functies zullen zijn:
statische leegte DefaultSink (Ptr bestand, ptr P);
Het statische sleutelwoord betekent dat dit een statische functie is die geen a nodig heeft dit aanwijzer, dus
het zal callbacks in C-stijl gebruiken. We willen niet dat de aanroepcode iets te weten komt
alles behalve het pakket. Wat we in de aanroepcode willen, is slechts een oproep die eruitziet als:
m_promiscSnifferTrace (m_currentPkt);
Wat we willen doen is binden the Ptr filet naar de specifieke callback
implementatie wanneer het is gemaakt en regelen voor de exploitant() van het terugbellen naar
verstrek die parameter gratis.
Wij bieden de MaakBoundCallback sjabloonfunctie voor dat doel. Het kost hetzelfde
parameters als de MaakTerugbellen sjabloonfunctie maar neemt ook de parameters aan
gebonden. In het geval van bovenstaand voorbeeld:
MakeBoundCallback (&DefaultSink, bestand);
zal een specifieke callback-implementatie creëren die de extra grens weet toe te voegen
argumenten. Conceptueel breidt het de hierboven beschreven specifieke functor uit met een of meer
gebonden argumenten:
sjabloon
klasse SpecificFunctor : openbare functor
{
publiek:
SpecifiekeFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg = gebondenArg;
}
virtuele int-operator() (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
private:
leegte (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};
U kunt zien dat wanneer de specifieke functor wordt gemaakt, het gebonden argument wordt opgeslagen in het
functor / callback-object zelf. Wanneer de exploitant() wordt aangeroepen met de single
parameter, zoals in:
m_promiscSnifferTrace (m_currentPkt);
de uitvoering van de exploitant() voegt de gebonden parameter toe aan de daadwerkelijke functieaanroep:
(*m_p.*m_pmi)(m_boundArg, arg);
Het is ook mogelijk om twee of drie argumenten te binden. Stel dat we een functie hebben met
handtekening:
statische leegte NotifyEvent (Ptr a, Ptr b, MyEventType e);
Men kan een gebonden callback-binding maken met de eerste twee argumenten, zoals:
MakeBoundCallback (&NotifyEvent, a1, b1);
ervan uitgaand a1 en b1 zijn typeobjecten A en B respectievelijk. Zo ook voor drie
argumenten die men zou hebben functie met een handtekening:
statische leegte NotifyEvent (Ptr a, Ptr b, MyEventType e);
Bindende drie argumenten klaar met:
MakeBoundCallback (&NotifyEvent, a1, b1, c1);
weer veronderstellen a1, b1 en c1 zijn typeobjecten A, B en C respectievelijk.
Dit soort binding kan worden gebruikt voor het uitwisselen van informatie tussen objecten in simulatie;
in het bijzonder kunnen gebonden callbacks worden gebruikt als getraceerde callbacks, die zullen worden beschreven in
het volgende gedeelte.
Getraceerd Terugbelverzoeken
Placeholder onderafdeling
Terugbellen locaties in ns-3
Waar worden callbacks vaak gebruikt ns-3? Hier zijn enkele van de meer zichtbare
typische gebruikers:
· Socket-API
· Laag-2/Laag-3-API
· Traceringssubsysteem
· API tussen IP- en routeringssubsystemen
Implementatie gegevens
De bovenstaande codefragmenten zijn simplistisch en alleen bedoeld om het mechanisme te illustreren
zelf. De daadwerkelijke callback-code is vrij gecompliceerd en zeer sjabloonintensief en a
diep begrip van de code is niet vereist. Indien geïnteresseerd, kunnen ervaren gebruikers de
volgende nuttig.
De code is oorspronkelijk geschreven op basis van de technieken beschreven in
http://www.codeproject.com/cpp/TTLFunction.asp. Het werd vervolgens herschreven om te volgen
de architectuur beschreven in een modern C + + ontwerp Algemeen Programming en Design Patronen
Toegepast, Alexandrescu, hoofdstuk 5, Gegeneraliseerd Functies.
Deze code gebruikt:
· standaard sjabloonparameters om te voorkomen dat gebruikers lege parameters moeten opgeven wanneer
het aantal parameters is kleiner dan het maximaal ondersteunde aantal
· het pimpl-idioom: de Callback-klasse wordt op waarde doorgegeven en delegeert de kern van
het werk naar zijn pimpl-wijzer.
· twee pimpl-implementaties die zijn afgeleid van CallbackImpl FunctorCallbackImpl kunnen worden gebruikt
met elk functortype, terwijl MemPtrCallbackImpl kan worden gebruikt met verwijzingen naar leden
functies.
· een implementatie van een referentielijst om de waardesemantiek van de callback te implementeren.
Deze code wijkt met name af van de Alexandrescu-implementatie doordat dit niet het geval is
gebruik typelijsten om de typen van de callback-argumenten te specificeren en door te geven. Natuurlijk,
het maakt ook geen gebruik van kopieervernietigingssemantiek en vertrouwt eerder op een referentielijst dan op een referentielijst
autoPtr om de aanwijzer vast te houden.
Object model
ns-3 is in wezen een C++ objectsysteem. Objecten kunnen worden gedeclareerd en geïnstantieerd als
gebruikelijk, volgens C++ regels. ns-3 voegt ook enkele functies toe aan traditionele C++-objecten, zoals
hieronder beschreven, om meer functionaliteit en functies te bieden. Dit hoofdstuk in de handleiding is
bedoeld om de lezer kennis te laten maken met de ns-3 objectmodel.
In dit gedeelte wordt het ontwerp van de C++-klasse beschreven voor ns-3 voorwerpen. Kortom, meerdere ontwerpen
gebruikte patronen omvatten klassiek objectgeoriënteerd ontwerp (polymorfe interfaces en
implementaties), scheiding van interface en implementatie, het niet-virtuele publiek
interface-ontwerppatroon, een objectaggregatiefaciliteit en referentietelling voor
geheugen management. Degenen die bekend zijn met componentmodellen zoals COM of Bonobo zullen dat wel doen
elementen van het ontwerp herkennen in de ns-3 objectaggregatiemodel, hoewel het ns-3
ontwerp is ook niet strikt in overeenstemming met beide.
Object-georiënteerde gedrag
C++-objecten bieden over het algemeen algemene objectgeoriënteerde mogelijkheden (abstractie,
inkapseling, overerving en polymorfisme) die deel uitmaken van klassiek objectgeoriënteerd
ontwerp. ns-3 objecten maken gebruik van deze eigenschappen; bijvoorbeeld:
klasse Adres
{
publiek:
Adres ();
Adres (uint8_t type, const uint8_t *buffer, uint8_t len);
Adres (const Adres & adres);
Adres &operator = (const Adres &adres);
...
private:
uint8_t m_type;
uint8_t m_len;
...
};
Object baseren klassen
Er worden drie speciale basisklassen gebruikt ns-3. Klassen die erven van deze basis
klassen kunnen objecten met speciale eigenschappen instantiëren. Deze basisklassen zijn:
· klas Object
· klas ObjectBase
· klas EenvoudigeRefCount
Het is niet vereist dat ns-3 objecten erven van deze klasse, maar degenen die wel krijgen
bijzondere eigenschappen. Klassen die voortkomen uit klasse Object krijg de volgende eigenschappen.
· de ns-3 type en attribuutsysteem (zie Attributen)
· een objectaggregatiesysteem
· een smart-pointer referentietelsysteem (klasse Ptr)
Klassen die voortkomen uit klasse ObjectBase krijg de eerste twee eigenschappen hierboven, maar doe dat niet
krijg slimme aanwijzingen. Klassen die voortkomen uit klasse EenvoudigeRefCount: krijg alleen de
smart-pointer referentietelsysteem.
In de praktijk klasse Object is de variant van de drie hierboven die de ns-3 ontwikkelaar zal
het meest tegenkomen.
Geheugen management en klasse Ptr
Geheugenbeheer in een C++-programma is een complex proces en wordt vaak verkeerd of foutief uitgevoerd
inconsistent. We hebben gekozen voor een ontwerp voor het tellen van referenties dat als volgt wordt beschreven.
Alle objecten die referentietelling gebruiken, houden een interne referentietelling bij om te bepalen
wanneer een object zichzelf veilig kan verwijderen. Elke keer dat een pointer wordt verkregen naar een
interface, wordt het aantal referenties van het object verhoogd door aan te roepen Ref(). Het is
verplichting van de gebruiker van de aanwijzer om expliciet Unref() de aanwijzer als u klaar bent. Wanneer
het aantal referenties daalt naar nul, het object wordt verwijderd.
· Wanneer de clientcode een aanwijzer verkrijgt van het object zelf door het maken van objecten,
of via GetObject, hoeft het aantal referenties niet te worden verhoogd.
· Wanneer clientcode een pointer van een andere bron verkrijgt (bijvoorbeeld door een pointer te kopiëren), moet dat wel
Bellen Ref() om het aantal referenties te verhogen.
· Alle gebruikers van de objectpointer moeten bellen Unref() om de referentie vrij te geven.
De last om te bellen Unref() wordt enigszins verlicht door het gebruik van de referentietelling
slimme aanwijzerklasse die hieronder wordt beschreven.
Gebruikers die een low-level API gebruiken die expliciet niet-referentie-getelde objecten willen toewijzen
op de heap, met behulp van operator new, zijn verantwoordelijk voor het verwijderen van dergelijke objecten.
Referentie telling slim wijzer (Ptr)
het roepen Ref() en Unref() de hele tijd zou omslachtig zijn, dus ns-3 biedt een slimme
aanwijzer klasse Ptr Soortgelijke Boost::intrusive_ptr. Deze smart-pointer-klasse gaat daar vanuit
het onderliggende type biedt een paar Ref en Niet gerefereerd methoden die worden verwacht
verhoog en verlaag de interne refcount van de objectinstantie.
Met deze implementatie kunt u de slimme aanwijzer manipuleren alsof deze normaal is
aanwijzer: je kunt het vergelijken met nul, vergelijken met andere aanwijzers, nul toewijzen aan
het, enz.
Het is mogelijk om de onbewerkte aanwijzer uit deze slimme aanwijzer te extraheren met de GetPointer()
en PeekPointer() werkwijzen.
Als u een nieuw object in een slimme aanwijzer wilt opslaan, raden we u aan de
CreateObject-sjabloonfuncties om het object te maken en op te slaan in een slimme aanwijzer
geheugenlekken voorkomen. Deze functies zijn echt kleine gemaksfuncties en hun doel
is alleen om u een klein beetje typewerk te besparen.
Object maken en creëren
Objecten in C++ kunnen statisch, dynamisch of automatisch worden gemaakt. Dit klopt
besteld, ns-3 ook, maar voor sommige objecten in het systeem zijn enkele aanvullende frameworks beschikbaar.
In het bijzonder worden referentie-getelde objecten meestal toegewezen met behulp van een sjabloon Maak of
CreateObject-methode, als volgt.
Voor objecten die zijn afgeleid van class Object:
Ptr apparaat = CreateObject ();
Maak dergelijke objecten niet met behulp van operator nieuwe; maak ze met behulp van CreëerObject()
gebruiken.
Voor objecten die zijn afgeleid van class EenvoudigeRefCount, of andere objecten die het gebruik van de
slimme pointer-klasse, een sjabloonhulpfunctie is beschikbaar en wordt aanbevolen om te gebruiken:
Ptr b = Maken ();
Dit is gewoon een wikkel rond de nieuwe operator die de referentietelling correct afhandelt
systeem.
Samengevat, gebruik Creëren als B geen object is maar alleen referentietelling gebruikt (bijv
Pakket), en gebruiken CreateObject als B is afgeleid van ns3::Object.
Aggregatie
De ns-3 objectaggregatiesysteem wordt voor een groot deel gemotiveerd door de erkenning dat a
gemeenschappelijk gebruik voor ns-2 is het gebruik van overerving en polymorfisme om uit te breiden
protocol modellen. Gespecialiseerde versies van TCP zoals RenoTcpAgent worden bijvoorbeeld afgeleid
van (en functies overschrijven van) klasse TcpAgent.
Echter, twee problemen die zich hebben voorgedaan in de ns-2 model zijn downcasts en "zwakke basis
class." Downcasting verwijst naar de procedure van het gebruik van een basisklasse-pointer naar een object en
het tijdens runtime opvragen om type-informatie te achterhalen, gebruikt om de aanwijzer expliciet te casten
naar een subklasse-pointer zodat de subklasse-API kan worden gebruikt. Zwakke basisklasse verwijst naar de
problemen die zich voordoen wanneer een klasse niet effectief kan worden hergebruikt (afgeleid van) omdat het
mist de nodige functionaliteit, waardoor de ontwikkelaar de basisklasse moet wijzigen en
veroorzaakt een wildgroei aan API-aanroepen van basisklassen, waarvan sommige mogelijk niet semantisch zijn
correct voor alle subklassen.
ns-3 gebruikt een versie van het ontwerppatroon van de query-interface om deze problemen te voorkomen.
Dit ontwerp is gebaseerd op elementen van de Bestanddeel Object Model en GNOME Bonobo hoewel
volledige compatibiliteit op binair niveau van vervangbare componenten wordt niet ondersteund en dat hebben we wel gedaan
geprobeerd de syntaxis en impact op modelontwikkelaars te vereenvoudigen.
Voorbeelden
Aggregatie voorbeeld
Knooppunt is een goed voorbeeld van het gebruik van aggregatie in ns-3. Merk op dat er geen afgeleide zijn
klassen van knooppunten in ns-3 zoals klasse InternetNode. In plaats daarvan zijn componenten (protocollen) dat wel
samengevoegd tot een knooppunt. Laten we eens kijken hoe sommige Ipv4-protocollen aan een node worden toegevoegd:
statische leegte
Ipv4Stack toevoegen(Ptr knooppunt)
{
Ptr ipv4 = CreateObject ();
ipv4->SetNode (knooppunt);
knooppunt->AggregateObject (ipv4);
Ptr ipv4Impl = CreateObject ();
ipv4Impl->SetIpv4 (ipv4);
knooppunt->AggregateObject (ipv4Impl);
}
Merk op dat de Ipv4-protocollen zijn gemaakt met behulp van CreëerObject(). Vervolgens worden ze geaggregeerd
naar het knooppunt. Op deze manier hoeft de Node-basisklasse niet te worden bewerkt om gebruikers toe te staan
met een basisklasse Node-aanwijzer om toegang te krijgen tot de Ipv4-interface; gebruikers kunnen het knooppunt om een vragen
pointer naar zijn Ipv4-interface tijdens runtime. Hoe de gebruiker het knooppunt vraagt, wordt beschreven in de
volgende onderafdeling.
Merk op dat het een programmeerfout is om meer dan één object van hetzelfde type samen te voegen
an ns3::Object. Aggregatie is bijvoorbeeld geen optie voor het opslaan van alle
actieve sockets van een knooppunt.
GetObject voorbeeld
GetObject is een typeveilige manier om een veilige downcasting te bereiken en interfaces mogelijk te maken
gevonden op een object.
Overweeg een knooppuntaanwijzer m_knooppunt dat verwijst naar een Node-object dat een implementatie heeft van
IPv4 is er eerder naar geaggregeerd. De clientcode wil een standaardroute configureren. Naar
om dit te doen, moet het toegang hebben tot een object binnen het knooppunt dat een interface heeft naar de IP-forwarding
configuratie. Het voert het volgende uit:
Ptr ipv4 = m_node->GetObject ();
Als het knooppunt in feite geen Ipv4-object heeft, dan zal de methode dat wel doen
retourneert nul. Daarom is het een goede gewoonte om de geretourneerde waarde van een dergelijke functie te controleren
telefoongesprek. Als dit lukt, kan de gebruiker nu de Ptr gebruiken voor het Ipv4-object dat eerder was
geaggregeerd naar het knooppunt.
Een ander voorbeeld van hoe men aggregatie zou kunnen gebruiken, is het toevoegen van optionele modellen aan objecten. Voor
bijvoorbeeld, een bestaand Node-object kan een "Energy Model"-object hebben dat eraan is toegevoegd
looptijd (zonder de knooppuntklasse te wijzigen en opnieuw te compileren). Een bestaand model (zoals een
draadloos netwerkapparaat) kan dan later "GetObject" voor het energiemodel en adequaat handelen
als de interface is ingebouwd in het onderliggende Node-object of is samengevoegd
het tijdens runtime. Andere knooppunten hoeven echter niets te weten over energiemodellen.
We hopen dat deze manier van programmeren ontwikkelaars veel minder hoeft aan te passen
de basisklassen.
Object fabrieken
Een veelgebruikte use-case is om veel op dezelfde manier geconfigureerde objecten te maken. Men kan herhaaldelijk
Bellen CreëerObject() maar er is ook een fabrieksontwerppatroon in gebruik in de ns-3 systeem.
Het wordt veel gebruikt in de "helper" API.
Klasse ObjectFabriek kan worden gebruikt om objecten te instantiëren en om de attributen aan te configureren
die voorwerpen:
ongeldig SetTypeId (TypeId tid);
void Set (std::tekenreeksnaam, const AttributeValue &waarde);
Ptr Creëer (ongeldige) const;
De eerste methode maakt het mogelijk om de ns-3 TypeId-systeem om het type objecten op te geven
gemaakt. Met de tweede kan men attributen instellen op de objecten die moeten worden gemaakt, en de
derde maakt het mogelijk om de objecten zelf te creëren.
Bijvoorbeeld:
ObjectFactory-fabriek;
// Laat deze fabriek objecten maken van het type FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Laat dit fabrieksobject een standaardwaarde van een attribuut wijzigen, voor
// vervolgens gemaakte objecten
fabriek.Set ("Systeemverlies", DoubleValue (2.0));
// Maak een dergelijk object
Ptr object = fabriek. Maken ();
fabriek.Set ("Systeemverlies", DoubleValue (3.0));
// Maak een ander object met een ander systeemverlies
Ptr object = fabriek. Maken ();
neerbuigend
Een vraag die meerdere keren is gerezen is: "Als ik een basisklassewijzer (Ptr) heb naar een
object en ik wil de afgeleide klasse-aanwijzer, moet ik downcasten (via C ++ dynamische cast) naar
krijg de afgeleide aanwijzer, of moet ik het objectaggregatiesysteem gebruiken GetObject<> ()
om een Ptr te vinden naar de interface naar de subklasse API?"
Het antwoord hierop is dat in veel situaties beide technieken zullen werken. ns-3 biedt
templated-functie om de syntaxis van object dynamisch casten veel gebruiksvriendelijker te maken
vriendelijk:
sjabloon
Ptr
DynamicCast (Ptr const&p)
{
terug Ptr (dynamische_cast (PeekPointer (p)));
}
DynamicCast werkt wanneer de programmeur een aanwijzer van het basistype heeft en test tegen een
subklasse aanwijzer. GetObject werkt bij het zoeken naar verschillende geaggregeerde objecten, maar ook
werkt met subklassen, op dezelfde manier als DynamicCast. Als het niet zeker is, moet de programmeur dat doen
gebruik GetObject, omdat het in alle gevallen werkt. Als de programmeur de klassenhiërarchie kent van
het object in kwestie, is het directer om gewoon DynamicCast te gebruiken.
Configuratie en Attributen
In ns-3 simulaties zijn er twee hoofdaspecten bij de configuratie:
· De simulatietopologie en hoe objecten zijn verbonden.
· De waarden die worden gebruikt door de modellen die in de topologie zijn geïnstantieerd.
Dit hoofdstuk richt zich op het tweede item hierboven: hoeveel waarden in gebruik zijn ns-3 zijn
georganiseerd, gedocumenteerd en aanpasbaar door ns-3 gebruikers. De ns-3 attribuutsysteem is ook het
onderbouwing van hoe sporen en statistieken worden verzameld in de simulator.
In de loop van dit hoofdstuk bespreken we de verschillende manieren om de waarden in te stellen of te wijzigen
door ns-3 objecten modelleren. In toenemende volgorde van specificiteit zijn dit:
┌─────────────────────────────────┬─────────────── ───────────────────┐
│Methode │ Reikwijdte │
├─────────────────────────────────┼─────────────── ───────────────────┤
│Standaard attribuutwaarden ingesteld │ Beïnvloeden alle instanties van de │
│wanneer Attributen zijn gedefinieerd in │ klasse. │
│GetTypeId (). │ │
└─────────────────────────────────┴─────────────── ───────────────────┘
│Opdrachtregel │ Invloed op alle toekomstige instanties. │
│Configuratie::SetDefault() │
│ConfiguratieStore │
├─────────────────────────────────┼─────────────── ───────────────────┤
│ObjectFabriek │ Beïnvloedt alle aangemaakte instanties │
│ │ met de fabriek. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│XHelperSetAttribuut () │ Beïnvloedt alle instanties gemaakt door │
│ │ de helper. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│MijnKlasse::SetX () │ Wijzigt deze specifieke instantie. │
│Object::SetAttribuut () │ Over het algemeen is dit de enige vorm │
│Configuratie::Instellen() │ die kan worden gepland om te veranderen │
│ │ een instantie zodra de simulatie │
│ │ loopt. │
└─────────────────────────────────┴─────────────── ───────────────────┘
Met "specificiteit" bedoelen we dat methoden in latere rijen in de tabel de ingestelde waarden overschrijven
door, en beïnvloeden doorgaans minder instanties dan, eerdere methoden.
Voordat we dieper ingaan op de details van het attribuutwaardesysteem, is het nuttig om er enkele te bekijken
basiseigenschappen van klasse Object.
Object Overzicht
ns-3 is in wezen een C++ objectgebaseerd systeem. Hiermee bedoelen we nieuwe C++-klassen
(typen) kunnen zoals gewoonlijk worden gedeclareerd, gedefinieerd en subklassen.
Veel ns-3 objecten erven van de Object basis klasse. Deze objecten hebben wat extra's
eigenschappen die we benutten voor het organiseren van het systeem en het verbeteren van het geheugenbeheer
van onze objecten:
· "Metadata"-systeem dat de klassenaam koppelt aan veel meta-informatie over de
voorwerp, waaronder:
· De basisklasse van de subklasse,
· De set toegankelijke constructors in de subklasse,
· De verzameling "attributen" van de subklasse,
· Of elk attribuut kan worden ingesteld of alleen-lezen is,
· Het toegestane bereik van waarden voor elk attribuut.
· Implementatie van slimme aanwijzers voor het tellen van referenties, voor geheugenbeheer.
ns-3 objecten die het attribuutsysteem gebruiken, zijn van een van beide afgeleid Object or ObjectBase. Meest
ns-3 objecten die we zullen bespreken, zijn afgeleid Object, maar een paar die buiten de smart vallen
pointer memory management framework afgeleid van ObjectBase.
Laten we een paar eigenschappen van deze objecten bekijken.
Slimme Pointers
Zoals geïntroduceerd in de ns-3 zelfstudie, ns-3 objecten zijn geheugen beheerd door een referentie
telling slim wijzer uitvoering, klasse Ptr.
Slimme aanwijzers worden veel gebruikt in de ns-3 API's, om te voorkomen dat verwijzingen worden doorgegeven naar
heap-toegewezen objecten die geheugenlekken kunnen veroorzaken. Voor het meest basale gebruik (syntaxis) behandelt u
een slimme aanwijzer zoals een gewone aanwijzer:
Ptr zd = ...;
nd->CallSomeFunction ();
// enz.
Dus hoe krijg je een slimme aanwijzer naar een object, zoals in de eerste regel van dit voorbeeld?
Object maken
Zoals we hierboven hebben besproken in Memory-management-and-class-Ptr, op het laagste niveau API, objecten
Van type Object worden niet geïnstantieerd met behulp van operator nieuwe zoals gewoonlijk, maar in plaats daarvan door een templated
functie aangeroepen Object maken ().
Een typische manier om zo'n object te maken is als volgt:
Ptr nd = CreëerObject ();
U kunt dit beschouwen als functioneel equivalent aan:
WifiNetDevice* en = nieuw WifiNetDevice ();
Objecten die voortkomen uit Object moet worden toegewezen aan de heap met behulp van Object maken (). Die
afgeleid van ObjectBase, zoals ns-3 helperfuncties en pakketkoppen en aanhangwagens,
kan worden toegewezen op de stapel.
In sommige scripts zie je misschien niet veel Object maken () roept de code in; dit is
omdat er in feite enkele helperobjecten zijn die het doen Object maken () gesprekken
voor jou
TypeId
ns-3 klassen die uit klasse voortkomen Object kan een metadataklasse bevatten genaamd TypeId uit die
registreert meta-informatie over de klasse, voor gebruik in de objectaggregatie en component
beheerder systemen:
· Een unieke tekenreeks die de klasse identificeert.
· De basisklasse van de subklasse, binnen het metadatasysteem.
· De verzameling toegankelijke constructors in de subklasse.
· Een lijst met openbaar toegankelijke eigenschappen ("attributen") van de klasse.
Object Samenvatting
Laten we al deze concepten samenvoegen en naar een specifiek voorbeeld kijken: klasse Knooppunt.
Het openbare headerbestand knooppunt.h heeft een declaratie die een static bevat GetTypeId ()
functie-oproep:
klasse Knooppunt: openbaar object
{
publiek:
statisch TypeId GetTypeId (ongeldig);
...
Dit is vastgelegd in de knooppunt.cc bestand als volgt:
TypeId
Node::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::Node")
.SetOuder ()
.Constructor toevoegen ()
.AddAttribute ("Apparaatlijst",
"De lijst met apparaten die aan deze Node zijn gekoppeld.",
ObjectVectorWaarde (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker ())
.AddAttribute ("Toepassingslijst",
"De lijst met applicaties die aan deze Node zijn gekoppeld.",
ObjectVectorWaarde (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker ())
.AddAttribute ("Id",
"De id (uniek geheel getal) van deze Node.",
TypeId::ATTR_GET, // staat alleen toe om het te krijgen.
UintegerWaarde (0),
MakeUintegerAccessor (&Node::m_id),
MaakUintegerChecker ())
;
tijd teruggeven;
}
Houd rekening met de TypeId van de ns-3 Object class als een uitgebreide vorm van runtime-type
informatie (RTTI). De C++-taal bevat een eenvoudig soort RTTI om te ondersteunen
dynamische_cast en typeid exploitanten.
De Ouder instellen () call in de bovenstaande definitie wordt gebruikt in combinatie met our
objectaggregatiemechanismen om veilige up- en downcasting in overervingsbomen mogelijk te maken
gedurende GetObject (). Het stelt subklassen ook in staat om de attributen van hun ouder te erven
klasse.
De Constructeur toevoegen () call wordt gebruikt in combinatie met onze abstracte objectfabriek
mechanismen waarmee we C++-objecten kunnen bouwen zonder een gebruiker te dwingen de
concrete klasse van het object dat ze aan het bouwen is.
De drie bellen naar Attribuut toevoegen () associeer een gegeven string met een sterk getypte waarde in
de klas. Merk op dat u een helptekenreeks moet opgeven die bijvoorbeeld kan worden weergegeven
via opdrachtregelprocessors. Elk Kenmerk wordt geassocieerd met mechanismen voor toegang
de onderliggende lidvariabele in het object (bijvoorbeeld MakeUintegerAccessor () vertelt
de generieke Kenmerk code hoe u bij de node-ID hierboven kunt komen). Er zijn ook "Checker"
methoden die worden gebruikt om waarden te valideren tegen bereikbeperkingen, zoals maximum en
minimaal toegestane waarden.
Wanneer gebruikers knooppunten willen maken, zullen ze meestal een vorm van Object maken (),:
Ptr n = CreëerObject ();
of meer abstract, met behulp van een objectfabriek kun je een Knooppunt object zonder zelfs
het concrete C ++ -type kennen:
ObjectFactory-fabriek;
const std::string typeId = "ns3::Node'';
fabriek.SetTypeId (typeId);
Ptr knooppunt = fabriek. Maken ();
Beide methoden resulteren erin dat volledig geïnitialiseerde attributen beschikbaar zijn in het
verkregen Object instanties.
Vervolgens bespreken we hoe attributen (waarden geassocieerd met lidvariabelen of functies van
de klas) zijn aangesloten op het bovenstaande TypeId.
Attributen
Het doel van het attribuutsysteem is het organiseren van de toegang tot interne lidobjecten van een
simulatie. Dit doel ontstaat omdat, typisch in simulatie, gebruikers zullen knippen en
bestaande simulatiescripts plakken/wijzigen, of simulatieconstructies van een hoger niveau gebruiken,
maar zullen vaak geïnteresseerd zijn in het bestuderen of traceren van bepaalde interne variabelen. Voor
gebruik bijvoorbeeld gevallen zoals:
· "I willen naar opsporen the pakketten on the draadloze interface Slechts on the eerste toegang punt."
· "I willen naar opsporen the waarde of the TCP congestie venster (elk Time to it veranderingen) on a
bijzonder TCP stopcontact."
· "I willen a storten of allen waarden uit die waren gebruikt in my simulatie."
Evenzo willen gebruikers mogelijk fijnmazige toegang tot interne variabelen in de simulatie, of
wil misschien de initiële waarde die voor een bepaalde parameter wordt gebruikt in grote lijnen wijzigen
vervolgens gemaakte objecten. Ten slotte willen gebruikers misschien weten welke variabelen kunnen worden ingesteld
en opvraagbaar in een simulatieconfiguratie. Dit is niet alleen voor directe simulatie
interactie op de opdrachtregel; denk ook aan een (toekomstige) grafische gebruikersinterface die
zou graag een functie willen bieden waarbij een gebruiker met de rechtermuisknop op een knooppunt kan klikken op
het canvas en zie een hiërarchische, georganiseerde lijst met parameters die kunnen worden ingesteld op de
node en zijn samenstellende lidobjecten, en helptekst en standaardwaarden voor elk
parameter.
Het definiëren Attributen
We bieden gebruikers een manier om toegang te krijgen tot waarden diep in het systeem, zonder te hoeven peilen
accessors (pointers) door het systeem en loop pointerketens om bij hen te komen. Overweeg a
klasse DropTailQueue die een lidvariabele heeft die een geheel getal zonder teken is m_maxPakketten;
deze lidvariabele bepaalt de diepte van de wachtrij.
Als we kijken naar de aangifte van DropTailQueue, zien we het volgende:
klasse DropTailQueue: openbare wachtrij {
publiek:
statisch TypeId GetTypeId (ongeldig);
...
private:
standaard::wachtrij > m_pakketten;
uint32_t m_maxPackets;
};
Laten we eens kijken naar dingen die een gebruiker zou willen doen met de waarde van m_maxPakketten:
· Stel een standaardwaarde in voor het systeem, zodat wanneer een nieuw DropTailQueue is gecreëerd,
dit lid wordt geïnitialiseerd naar die standaardwaarde.
· Stel de waarde in op een reeds aangemaakte wachtrij of haal deze op.
De bovenstaande dingen vereisen meestal het verstrekken Set () en Krijgen () functies, en een soort van
globale standaardwaarde.
In de ns-3 attribuutsysteem, deze waardedefinities en registraties van accessorfuncties
worden verplaatst naar de TypeId klasse; bv.:
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
TypeId
DropTailQueue::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::DropTailQueue")
.SetOuder ()
.Constructor toevoegen ()
.AddAttribute ("MaxPackets",
"Het maximale aantal pakketten dat door deze DropTailQueue wordt geaccepteerd.",
UintegerWaarde (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MaakUintegerChecker ())
;
tijd teruggeven;
}
De Attribuut toevoegen () methode is het uitvoeren van een aantal dingen voor de m_maxPakketten waarde:
· Binding van de (meestal private) lidvariabele m_maxPakketten naar een openbare string
"Max Pakketten".
· Een standaardwaarde opgeven (100 pakketten).
· Verschaffen van wat hulptekst die de betekenis van de waarde definieert.
· Biedt een "Checker" (niet gebruikt in dit voorbeeld) die kan worden gebruikt om grenzen te stellen aan de
toelaatbaar bereik van waarden.
Het belangrijkste punt is dat nu de waarde van deze variabele en de standaardwaarde ervan toegankelijk zijn
in de attribuutnaamruimte, die is gebaseerd op tekenreeksen zoals "Max Pakketten" en TypeId naam
snaren. In de volgende sectie zullen we een voorbeeldscript geven dat laat zien hoe gebruikers kunnen
manipuleren van deze waarden.
Merk op dat de initialisatie van het attribuut afhankelijk is van de macro NS_OBJECT_ENSURE_REGISTERED
(DropTailQueue) gebeld worden; als u dit weglaat uit uw nieuwe klasse-implementatie, uw
attributen worden niet correct geïnitialiseerd.
Hoewel we hebben beschreven hoe attributen te maken, hebben we nog niet beschreven hoe toegang te krijgen
en beheer deze waarden. Er is bijvoorbeeld geen globals.h header-bestand waar deze zijn
opgeslagen; attributen worden opgeslagen met hun klassen. Vragen die natuurlijk opkomen zijn hoe
leren gebruikers gemakkelijk over alle attributen van hun modellen, en hoe leert een gebruiker
toegang krijgen tot deze attributen, of hun waarden documenteren als onderdeel van het record van hun
simulatie?
Gedetailleerde documentatie van de feitelijke attributen die voor een type zijn gedefinieerd, en een globale lijst van
alle gedefinieerde attributen zijn beschikbaar in de API-documentatie. Voor de rest hiervan
document gaan we de verschillende manieren demonstreren om attribuut te verkrijgen en in te stellen
waarden.
omgeving Standaard Waarden
Configuratie::SetDefault en Opdrachtregel
Laten we eens kijken hoe een gebruikersscript toegang kan krijgen tot een specifieke attribuutwaarde. Zouden gaan
Gebruik de src/point-to-point/examples/main-attribuut-waarde.cc script ter illustratie, met
sommige details eruit gehaald. De hoofd- functie begint:
// Dit is een basisvoorbeeld van het gebruik van het attribuutsysteem
// een waarde instellen en ophalen in het onderliggende systeem; namelijk een niet-ondertekende
// geheel getal van het maximale aantal pakketten in een wachtrij
//
int
hoofd (int argc, char *argv[])
{
// Het kenmerk MaxPackets heeft standaard een waarde van 100 pakketten
// (deze standaard kan worden waargenomen in de functie DropTailQueue::GetTypeId)
//
// Hier stellen we het in op 80 pakketten. We kunnen een van de volgende twee waardetypen gebruiken:
// een op tekenreeksen gebaseerde waarde of een Uinteger-waarde
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// De onderstaande functieaanroep is overbodig
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
// Sta de gebruiker toe om een van de standaardinstellingen en het bovenstaande te overschrijven
// SetDefaults () tijdens runtime, via opdrachtregelargumenten
// Bijvoorbeeld via "--ns3::DropTailQueue::MaxPackets=80"
Opdrachtregel cmd;
// Dit biedt nog een andere manier om de waarde in te stellen vanaf de opdrachtregel:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);
Het belangrijkste om op te merken in het bovenstaande zijn de twee equivalente oproepen naar Configuratie::SetDefault
(). Dit is hoe we de standaardwaarde instellen voor alle vervolgens geïnstantieerde
DropTailQueueS. We illustreren dat twee soorten Waarde lessen, een Tekenreekswaarde en
UintegerWaarde class, kan worden gebruikt om de waarde toe te wijzen aan het attribuut genoemd door
"ns3::DropTailQueue::MaxPackets".
Het is ook mogelijk om attributen te manipuleren met behulp van de Opdrachtregel; we zagen enkele voorbeelden
aan het begin van de zelfstudie. Het is met name eenvoudig om een kort argument toe te voegen
naam, zoals --maxPakketten, voor een Attribuut dat bijzonder relevant is voor uw model,
in dit geval "ns3::DropTailQueue::MaxPackets". Dit heeft als extra eigenschap dat de
helpreeks voor het kenmerk wordt afgedrukt als onderdeel van het gebruiksbericht voor het script.
Voor meer informatie zie de Opdrachtregel API-documentatie.
Nu gaan we een paar objecten maken met behulp van de low-level API. Onze nieuw gecreëerde wachtrijen wel
niet m_maxPakketten geïnitialiseerd op 100 pakketten, zoals gedefinieerd in de
DropTailQueue::GetTypeId () functie, maar tot 80 pakketten, vanwege wat we hierboven hebben gedaan
standaard waarden.:
Ptr n0 = CreëerObject ();
Ptr net0 = CreëerObject ();
n0->Apparaat toevoegen (net0);
Ptr q = CreëerObject ();
net0->ToevoegenQueue(q);
Op dit moment hebben we er een gemaakt Knooppunt (n0) en een enkele PointToPointNetDevice
(net0), en voegde een DropTailQueue (q) Om net0.
Constructeurs, helpers en ObjectFabriek
Willekeurige combinaties van attributen kunnen worden ingesteld en opgehaald van de helper en op laag niveau
API's; ofwel van de constructeurs zelf:
Ptr p=
CreateObjectWithAttributes
("MinX", dubbele waarde (-100.0),
"MinY", dubbele waarde (-100.0),
"DeltaX", dubbele waarde (5.0),
"DeltaY", DoubleValue (20.0),
"Rasterbreedte", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
of van de helper-API's op een hoger niveau, zoals:
mobiliteit.SetPositionAllocator
("ns3::GridPositionAllocator",
"MinX", dubbele waarde (-100.0),
"MinY", dubbele waarde (-100.0),
"DeltaX", dubbele waarde (5.0),
"DeltaY", DoubleValue (20.0),
"Rasterbreedte", UintegerValue (20),
"LayoutType", StringValue ("RowFirst"));
We illustreren het hier niet, maar u kunt ook een ObjectFabriek met nieuwe waarden
voor specifieke attributen. Instanties gemaakt door de ObjectFabriek zal die hebben
attributen ingesteld tijdens de bouw. Dit lijkt sterk op het gebruik van een van de helper-API's
voor de klas.
Ter beoordeling: er zijn verschillende manieren om waarden in te stellen voor attributen voor klasseninstanties naar be
aangemaakt in the toekomst:
· Configuratie::SetDefault ()
· CommandLine::Waarde toevoegen ()
· CreateObjectWithAttributes<> ()
· Verschillende hulp-API's
Maar wat als u al een instantie heeft gemaakt en u de waarde van de
attribuut? Hoe kunnen we in dit voorbeeld de m_maxPakketten waarde van het al
geïnstantieerd DropTailQueue? Hier zijn verschillende manieren om dat te doen.
De wereld Waarden
SmartPointer
Stel dat een slimme aanwijzer (Ptr) naar een relevant netwerkapparaat in de hand; in de stroming
het is bijvoorbeeld de net0 wijzer.
Een manier om de waarde te wijzigen, is door toegang te krijgen tot een aanwijzer naar de onderliggende wachtrij en deze te wijzigen
attribuut.
Eerst zien we dat we een verwijzing kunnen krijgen naar de (basisklasse) Queue via the
PointToPointNetDevice attributen, waar het wordt genoemd "TxQueue":
PointerValue tmp;
net0->GetAttribute ("TxQueue", tmp);
Ptr txQueue = tmp.GetObject ();
De GetObject () functie, kunnen we een veilige downcast uitvoeren naar a DropTailQueue, Waar
"Max Pakketten" is een attribuut:
Ptr dtq = txQueue->GetObject ();
NS_ASSERT (dtq!= 0);
Vervolgens kunnen we de waarde van een attribuut in deze wachtrij krijgen. We hebben de wikkel geïntroduceerd
Waarde klassen voor de onderliggende gegevenstypen, vergelijkbaar met Java-wrappers rond deze typen,
aangezien het attribuutsysteem waarden opslaat die zijn geserialiseerd naar strings, en niet naar ongelijksoortige typen.
Hier wordt de attribuutwaarde toegewezen aan a UintegerWaardeEn Krijgen () methode hierop
waarde produceert de (onverpakte) uint32_t.:
UintegerValue-limiet;
dtq->GetAttribute ("MaxPackets", limiet);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << "pakketten");
Merk op dat de bovenstaande neerslachtigheid niet echt nodig is; we hadden het attribuut kunnen krijgen
waarde rechtstreeks van txWachtrij, dat is een Object:
txQueue->GetAttribute ("MaxPackets", limiet);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << "pakketten");
Laten we het nu op een andere waarde instellen (60 pakketten):
txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", limiet);
NS_LOG_INFO ("3. txQueue-limiet gewijzigd: " << limit.Get () << "pakketten");
Config namespace Pad
Een alternatieve manier om bij het kenmerk te komen, is door de configuratienaamruimte te gebruiken. Hier,
dit attribuut bevindt zich op een bekend pad in deze naamruimte; deze aanpak is nuttig als er een is
heeft geen toegang tot de onderliggende pointers en wil een specifiek configureren
attribuut met een enkele verklaring.:
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
UintegerWaarde (25));
txQueue->GetAttribute ("MaxPackets", limiet);
NS_LOG_INFO ("4. txQueue-limiet gewijzigd via naamruimte: "
<< limit.Get () << "pakketten");
Het configuratiepad heeft vaak de vorm van ".../
naam>/ /.../ / " verwijzen naar een specifieke instantie door index van een
voorwerp in de houder. In dit geval is de eerste container de lijst van alle Knooppunts; de
tweede container is de lijst van allemaal Netapparaats op de gekozen Knooppunt. eindelijk, de
configuratiepad eindigt meestal met een opeenvolging van lidattributen, in dit geval de
"Max Pakketten" attribuut van de "TxQueue" van de uitverkorenen Netapparaat.
We hadden ook jokertekens kunnen gebruiken om deze waarde in te stellen voor alle knooppunten en alle internetapparaten
(wat in dit eenvoudige voorbeeld hetzelfde effect heeft als het vorige Configuratie::Instellen ()):
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
UintegerWaarde (15));
txQueue->GetAttribute ("MaxPackets", limiet);
NS_LOG_INFO ("5. txQueue-limiet gewijzigd door wildcard-naamruimte: "
<< limit.Get () << "pakketten");
Object Naam Diensten
Een andere manier om bij het attribuut te komen, is door gebruik te maken van de objectnaamservicefaciliteit. De
objectnaamservice stelt ons in staat items toe te voegen aan de configuratienaamruimte onder de
"/Namen/" pad met een door de gebruiker gedefinieerde naamstring. Deze benadering is nuttig als men dat niet doet
toegang hebben tot de onderliggende pointers en het is moeilijk om de vereiste te bepalen
concreet configuratienaamruimtepad.
Namen::Toevoegen ("server", n0);
Namen::Toevoegen ("server/eth0", net0);
...
Config::Set ("/Namen/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
Hier hebben we de padelementen toegevoegd "server" en "eth0" onder de "/Namen/" naamruimte dan
gebruikte het resulterende configuratiepad om het attribuut in te stellen.
Zie Objectnamen voor een uitgebreidere behandeling van de ns-3 configuratie naamruimte.
Implementatie Details
Waarde Klassen
Lezers zullen de Typ Waarde klassen die subklassen zijn van de Attribuutwaarde baseren
klas. Deze kunnen worden gezien als tussenliggende klassen die worden gebruikt om van raw te converteren
typen naar de Attribuutwaardes die worden gebruikt door het attribuutsysteem. Bedenk dat dit
database bevat vele soorten objecten die zijn geserialiseerd naar strings. Ombouw naar dit type
kan worden gedaan met behulp van een tussenliggende klasse (zoals Integere waardeof dubbele waarde besteld,
getallen met drijvende komma) of via snaren. Directe impliciete conversie van typen naar
Attribuutwaarde is niet echt praktisch. Dus in het bovenstaande hebben gebruikers de keuze om te gebruiken
tekenreeksen of waarden:
p->Set ("cwnd", StringValue ("100")); // string-gebaseerde setter
p->Set ("cwnd", IntegerValue (100)); // op gehele getallen gebaseerde setter
Het systeem biedt enkele macro's die gebruikers helpen bij het declareren en definiëren van nieuwe AttributeValue
subklassen voor nieuwe typen die ze willen introduceren in het attribuutsysteem:
· ATTRIBUTE_HELPER_HEADER
· ATTRIBUTE_HELPER_CPP
Zie de API-documentatie voor deze constructies voor meer informatie.
initialisatie Bestel
Attributen in het systeem mogen niet afhankelijk zijn van de status van een ander Attribuut hierin
systeem. Dit komt omdat een volgorde van attribuutinitialisatie niet is opgegeven, noch
afgedwongen, door het systeem. Een specifiek voorbeeld hiervan is te zien in geautomatiseerde configuratie
programma's zoals ConfiguratieStore. Hoewel een bepaald model het zo kan rangschikken dat Attributen
in een bepaalde volgorde worden geïnitialiseerd, kan een andere automatische configurator beslissen
zelfstandig Attributen wijzigen in bijvoorbeeld alfabetische volgorde.
Vanwege deze niet-specifieke ordening mag geen enkel attribuut in het systeem enige afhankelijkheid hebben
op een ander kenmerk. Als gevolg hiervan mogen kenmerksetters nooit falen vanwege de status
van een ander kenmerk. Geen enkele attribuutsetter mag een andere attribuutwaarde wijzigen (instellen) als een
resultaat van het veranderen van de waarde.
Dit is een zeer sterke beperking en er zijn gevallen waarin attributen moeten worden ingesteld
consistent om een correcte werking mogelijk te maken. Hiertoe staan we consistentiecontrole toe
wanneer the attribuut is gebruikt (cf. NS_ASSERT_MSG or NS_ABORT_MSG).
Over het algemeen is de attribuutcode om waarden toe te wijzen aan de onderliggende klasselidvariabelen
wordt uitgevoerd nadat een object is geconstrueerd. Maar wat als u de toegewezen waarden nodig heeft
voordat de constructor-body wordt uitgevoerd, omdat je ze nodig hebt in de logica van de
constructeur? Er is een manier om dit te doen, bijvoorbeeld gebruikt in de klas ConfiguratieStore: telefoongesprek
ObjectBase::ConstructSelf () als volgt:
ConfigStore::ConfigStore ()
{
ObjectBase::ConstructSelf (AttribuutConstructionList ());
// ga verder met constructor.
}
Pas op dat het object en al zijn afgeleide klassen ook een GetInstanceTypeId
() methode. Anders de ObjectBase::ConstructSelf () zal niet in staat zijn om het te lezen
attributen.
Het toevoegen van Attributen
De ns-3 systeem zal een aantal interne waarden onder het attribuutsysteem plaatsen, maar
Ongetwijfeld zullen gebruikers dit willen uitbreiden om degene op te halen die we hebben gemist, of om hun toe te voegen
eigen klassen aan het systeem.
Er zijn drie typische use-cases:
· Een bestaand klassegegevenslid toegankelijk maken als een kenmerk, wanneer dit nog niet het geval is.
· Een nieuwe klasse in staat stellen om sommige gegevensleden als attributen weer te geven door deze een TypeId te geven.
· Het maken van een Attribuutwaarde subklasse voor een nieuwe klasse zodat deze toegankelijk is als een
Attribuut.
Bestaand Lid Veranderlijk
Beschouw deze variabele in TCPSocket:
uint32_t m_cWnd; // Congestievenster
Stel dat iemand die met TCP werkt de waarde van die variabele wil ophalen of instellen
met behulp van het metadatasysteem. Als het niet al was verstrekt door ns-3, kan de gebruiker aangeven
de volgende toevoeging in het runtime-metadatasysteem (aan de GetTypeId() definitie voor
TCPSocket):
.AddAttribute ("Congestievenster",
"Tcp-congestievenster (bytes)",
UintegerWaarde (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MaakUintegerChecker ())
Nu, de gebruiker met een pointer naar een TCPSocket instantie kan bewerkingen uitvoeren zoals
instellen en de waarde ophalen, zonder deze functies expliciet toe te voegen.
Verder kunnen toegangscontroles worden toegepast, zoals het laten uitlezen van de parameter en
niet geschreven, of grenscontrole op de toegestane waarden kan worden toegepast.
New Klasse TypeId
Hier bespreken we de impact op een gebruiker die een nieuwe klasse wil toevoegen ns-3. Wat
extra dingen moeten worden gedaan om het in staat te stellen attributen vast te houden?
Laten we uitgaan van onze nieuwe klas, genaamd ns3::Mijn Mobiliteit, is een soort mobiliteitsmodel. Eerst,
de klasse moet erven van de bovenliggende klasse, ns3::Mobiliteitsmodel. In de mijn-mobiliteit.h
header-bestand:
naamruimte ns3 {
klasse MyClass : openbaar MobilityModel
{
Dit vereist dat we de GetTypeId () functie. Dit is een openbare functie met één regel
verklaring:
publiek:
/ **
* Registreer dit type.
* \return Het object TypeId.
*/
statisch TypeId GetTypeId (ongeldig);
We hebben al geïntroduceerd wat een TypeId definitie eruit zal zien in de mijn-mobiliteit.cc
implementatie bestand:
NS_OBJECT_ENSURE_REGISTERED (Mijn Mobiliteit);
TypeId
MyMobility::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::MyMobility")
.SetOuder ()
.SetGroupName ("Mobiliteit")
.Constructor toevoegen ()
.AddAttribute ("Grenzen",
"Grenzen van het gebied om te cruisen.",
Rechthoekwaarde (rechthoek (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
MaakRechthoekChecker ())
.AddAttribute ("Tijd",
"Wijzig huidige richting en snelheid na verplaatsen voor deze vertraging.",
Tijdwaarde (seconden (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// etc (meer parameters).
;
tijd teruggeven;
}
Als we geen subklasse willen maken van een bestaande klasse, erven we gewoon in het headerbestand
vanaf ns3::Object, en in het objectbestand stellen we de bovenliggende klasse in op ns3::Object with
.SetOuder ().
Typische fouten hier zijn:
· Niet bellen NS_OBJECT_ENSURE_REGISTERED ()
· Niet bellen met de Ouder instellen () methode, of het aanroepen met het verkeerde type.
· Niet bellen met de Constructeur toevoegen () methode, of het aanroepen met het verkeerde type.
· Invoering van een typefout in de naam van de TypeId in zijn constructeur.
· Het niet gebruiken van de volledig gekwalificeerde C++-typenaam van de omringende C++-klasse als de naam van de
TypeId. Merk op dat "ns3::" Is benodigd.
Geen van deze fouten kan worden gedetecteerd door de ns-3 codebase, dus gebruikers wordt geadviseerd om te controleren
zorgvuldig meerdere keren dat ze deze goed hadden.
New Attribuutwaarde Type
Vanuit het perspectief van de gebruiker die een nieuwe klasse in het systeem schrijft en dat wil
toegankelijk als attribuut, is het vooral de kwestie van het schrijven van de conversies naar/van
tekenreeksen en attribuutwaarden. Het meeste hiervan kan worden gekopieerd/geplakt met gemacro-iseerde code. Voor
overweeg bijvoorbeeld een klassendeclaratie voor Rechthoek in de src/mobiliteit/model directory:
Voorvoegsel Dien in
/ **
* \brief een 2d rechthoek
*/
klasse Rechthoek
{
...
dubbele xMin;
dubbel xMax;
dubbele yMin;
dubbele yMax;
};
Er moeten één macro-aanroep en twee operators onder de klassendeclaratie worden toegevoegd om dit te kunnen doen
verander een rechthoek in een waarde die bruikbaar is voor de Kenmerk systeem:
std::ostream &operator << (std::ostream &os, const Rechthoek &rechthoek);
std::istream &operator >> (std::istream &is, Rechthoek &rechthoek);
ATTRIBUTE_HELPER_HEADER (rechthoek);
Implementatie Dien in
In de klassendefinitie (. Cc bestand), ziet de code er als volgt uit:
ATTRIBUTE_HELPER_CPP (rechthoek);
standaard::ostream &
operator << (std::ostream &os, const Rechthoek &rechthoek)
{
os << rechthoek.xMin << "|" << rechthoek.xMax << "|" << rechthoek.yMin << "|"
<< rechthoek.yMax;
retourneer os;
}
standaard::istream &
operator >> (std::istream &is, Rechthoek &rechthoek)
{
teken c1, c2, c3;
is >> rechthoek.xMin >> c1 >> rechthoek.xMax >> c2 >> rechthoek.yMin >> c3
>> rechthoek.yMax;
als (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
retour is;
}
Deze streamoperators converteren eenvoudig vanuit een tekenreeksrepresentatie van de rechthoek
("xMin|xMax|yMin|yMax") naar de onderliggende rechthoek. De modelleur moet deze specificeren
operatoren en de string syntactische representatie van een instantie van de nieuwe klasse.
ConfiguratieStore
Waarden voor ns-3 attributen kunnen worden opgeslagen in een ASCII- of XML-tekstbestand en worden geladen in een
toekomstige simulatierun. Deze functie staat bekend als de ns-3 ConfigStore. De ConfiguratieStore is
een gespecialiseerde database voor attribuutwaarden en standaardwaarden.
Hoewel het een afzonderlijk onderhouden module is in de src/config-store/ map, wij
documenteer het hier vanwege zijn enige afhankelijkheid van ns-3 kernmodule en attributen.
We kunnen dit systeem verkennen aan de hand van een voorbeeld uit
src/config-store/examples/config-store-save.cc.
Allereerst alle gebruikers van de ConfiguratieStore moet de volgende verklaring bevatten:
#include "ns3/config-store-module.h"
Vervolgens voegt dit programma een voorbeeldobject toe ConfiguratieVoorbeeld om te laten zien hoe het systeem is uitgebreid:
klasse ConfigExample: openbaar object
{
publiek:
statisch TypeId GetTypeId (ongeldig) {
statisch TypeId tid = TypeId ("ns3::A")
.SetOuder ()
.AddAttribute ("TestInt16", "helptekst",
IntegerWaarde (-2),
MakeIntegerAccessor (&A::m_int16),
Maak IntegerChecker ())
;
tijd teruggeven;
}
int16_t m_int16;
};
NS_OBJECT_ENSURE_REGISTERED (ConfigExample);
Vervolgens gebruiken we het Config-subsysteem om de standaardinstellingen op een aantal manieren te overschrijven:
Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
Ptr a_obj = CreateObject ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
"Kan ConfigExample's integer attribuut niet instellen via Config::SetDefault");
Ptr a2_obj = CreëerObject ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
IntegerWaarde iv;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Get () == -3,
"Kan ConfigExample's integer attribuut niet instellen via SetAttribute");
De volgende instructie is nodig om ervoor te zorgen dat (een van) de gemaakte objecten geroot is
in de configuratienaamruimte als een objectinstantie. Dit gebeurt normaal gesproken wanneer u
objecten samenvoegen tot a ns3::Knooppunt or ns3::Kanaal bijvoorbeeld, maar hier, omdat we aan het werk zijn
op het kernniveau moeten we een nieuw root-naamruimteobject maken:
Config::RegisterRootNamespaceObject (a2_obj);
schrijf-
Vervolgens willen we het configuratiearchief uitvoeren. De voorbeelden laten zien hoe je het in tweeën doet
formaten, XML en onbewerkte tekst. In de praktijk zou men deze stap vlak voor het bellen moeten uitvoeren
Simulator::Uitvoeren () om de uiteindelijke configuratie op te slaan vlak voordat de simulatie wordt uitgevoerd.
Er zijn drie kenmerken die het gedrag van de ConfigStore bepalen: "Modus",
"Bestandsnaam"en "Bestandsformaat". De modus (standaard "Geen") configureert of ns-3 moet
configuratie laden vanuit een eerder opgeslagen bestand (specificeer "Modus=Laden") of sla het op in een bestand
(specificeer "Modus=Opslaan"). De bestandsnaam (standaard "") is waar de ConfigStore or zou moeten lezen
schrijf zijn gegevens. Het bestandsformaat (standaard "Rauwe tekst") bepaalt of de ConfigStore-indeling
is platte tekst of XML ("Bestandsformaat=Xml")
Het voorbeeld laat zien:
Config::SetDefault ("ns3::ConfigStore::Bestandsnaam", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Opslaan"));
ConfigStore uitvoerConfig;
outputConfig.ConfigureDefaults ();
outputConfig.ConfigureAttributes ();
// Output config store naar txt-formaat
Config::SetDefault ("ns3::ConfigStore::Bestandsnaam", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Opslaan"));
ConfigStore-uitvoerConfig2;
outputConfig2.ConfigureDefaults ();
outputConfig2.ConfigureAttributen ();
Simulator::Uitvoeren ();
Simulator::Vernietigen ();
Let op de plaatsing van deze verklaringen vlak voor de Simulator::Uitvoeren () verklaring.
Deze uitvoer registreert alle waarden die net voor het starten van de simulatie aanwezig waren (d.w.z.
nadat alle configuratie heeft plaatsgevonden).
Na het hardlopen kun je het output-attributen.txt bestand en zie:
standaard ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
standaard ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
standaard ns3::PcapFileWrapper::CaptureSize "65535"
standaard ns3::PacketSocket::RcvBufSize "131072"
standaard ns3::ErrorModel::IsEnabled "true"
standaard ns3::RateErrorModel::ErrorUnit "EU_BYTE"
standaard ns3::RateErrorModel::ErrorRate "0"
standaard ns3::RateErrorModel::RanVar "Uniform:0:1"
standaard ns3::DropTailQueue::Mode "Pakketten"
standaard ns3::DropTailQueue::MaxPackets "100"
standaard ns3::DropTailQueue::MaxBytes "6553500"
standaard ns3::Toepassing::Starttijd "+0.0ns"
standaard ns3::Toepassing::StopTime "+0.0ns"
standaard ns3::ConfigStore::Modus "Opslaan"
standaard ns3::ConfigStore::Bestandsnaam "output-attributes.txt"
standaard ns3::ConfigStore::FileFormat "RawText"
standaard ns3::ConfigExample::TestInt16 "-5"
global RngSeed "1"
globale RngRun "1"
global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
global SchedulerType "ns3::MapScheduler"
globale ChecksumEnabled "false"
waarde /$ns3::ConfigExample/TestInt16 "-3"
In het bovenstaande worden alle standaardwaarden voor attributen voor de kernmodule weergegeven.
Vervolgens worden alle waarden voor de ns-3 globale waarden worden geregistreerd. Tot slot de waarde van de
instantie van ConfiguratieVoorbeeld dat was geworteld in de configuratienaamruimte wordt weergegeven. In een
vast ns-3 programma, zouden veel meer modellen, attributen en standaardinstellingen worden getoond.
Er bestaat ook een XML-versie in output-attributen.xml:
Dit bestand kan worden gearchiveerd met uw simulatiescript en uitvoergegevens.
lezing
Vervolgens bespreken we het configureren van simulaties via een opgeslagen invoerconfiguratiebestand. Er zijn
een paar belangrijke verschillen vergeleken met het schrijven van de uiteindelijke simulatieconfiguratie.
Ten eerste moeten we uitspraken zoals deze aan het begin van het programma plaatsen, ervoor
configuratie-instructies voor simulatie worden geschreven (zodat de waarden worden geregistreerd voordat ze worden
gebruikt in objectconstructie).
Config::SetDefault ("ns3::ConfigStore::Bestandsnaam", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Laden"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore invoerConfig;
inputConfig.ConfigureDefaults ();
Merk vervolgens op dat het laden van invoerconfiguratiegegevens beperkt is tot kenmerkstandaard (d.w.z.
not instance) waarden en globale waarden. Attribuutinstantiewaarden worden niet ondersteund
omdat in dit stadium van de simulatie, voordat er objecten worden geconstrueerd, er geen zijn
dergelijke objectinstanties rond. (Let op, toekomstige verbeteringen aan het configuratiearchief kunnen veranderen
dit gedrag).
Ten tweede, terwijl de output van ConfiguratieStore state zal alles in de database vermelden, de
invoerbestand hoeft alleen de specifieke waarden te bevatten die moeten worden overschreven. Dus, een manier om te gebruiken
deze klasse voor configuratie van invoerbestanden is om een initiële configuratie te genereren met behulp van de
uitvoer ("Opslaan") "Modus" zoals hierboven beschreven, haalt u uit dat configuratiebestand alleen het
elementen die men wil wijzigen, en verplaats deze minimale elementen naar een nieuw configuratiebestand
die vervolgens veilig kan worden bewerkt en geladen in een volgende simulatierun.
Wanneer de ConfiguratieStore object is geïnstantieerd, zijn attributen "Bestandsnaam", "Modus"en
"Bestandsformaat" moet ook worden ingesteld via opdrachtregel of via programma verklaringen.
Lezen schrijven Voorbeeld
Laten we als ingewikkelder voorbeeld aannemen dat we willen lezen in een configuratie van
defaults uit een invoerbestand met de naam invoer-defaults.xmlen schrijf het resultaat op
attributen naar een apart bestand genaamd output-attributen.xml.:
#include "ns3/config-store-module.h"
...
int hoofd (...)
{
Config::SetDefault ("ns3::ConfigStore::Bestandsnaam", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Laden"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore invoerConfig;
inputConfig.ConfigureDefaults ();
//
// Sta de gebruiker toe om een van de standaardinstellingen en de bovenstaande Bind () at te overschrijven
// runtime, via opdrachtregelargumenten
//
Opdrachtregel cmd;
cmd.Parse (argc, argv);
// setup-topologie
...
// Invoke vlak voor het invoeren van Simulator::Run ()
Config::SetDefault ("ns3::ConfigStore::Bestandsnaam", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Opslaan"));
ConfigStore uitvoerConfig;
outputConfig.ConfigureAttributes ();
Simulator::Uitvoeren ();
}
ConfiguratieStore GUI
Er is een GTK-gebaseerde front-end voor de ConfigStore. Hierdoor kunnen gebruikers een GUI gebruiken om
variabelen openen en wijzigen. Screenshots van deze functie zijn beschikbaar in de |ns3|
Overzicht presentatie.
Om deze functie te gebruiken, moet men installeren libgtk en libgtk-dev; een voorbeeld ubuntu
installatie commando is:
$ sudo apt-get install libgtk2.0-0 libgtk2.0-dev
Controleer de uitvoer van de stap om te controleren of deze is geconfigureerd of niet:
$ ./waf configure --enable-examples --enable-tests
---- Samenvatting van optionele NS-3-functies:
Python-bindingen: ingeschakeld
Python API-scanondersteuning: ingeschakeld
NS-3 Click-integratie: ingeschakeld
GtkConfigStore: niet ingeschakeld (bibliotheek 'gtk+-2.0 >= 2.12' niet gevonden)
In het bovenstaande voorbeeld was het niet ingeschakeld, dus het kan pas worden gebruikt als er een geschikte versie is
geïnstalleerd en:
$ ./waf configure --enable-examples --enable-tests
$ ./waf
wordt herhaald.
Het gebruik is bijna hetzelfde als de niet-GTK-gebaseerde versie, maar die zijn er niet ConfiguratieStore
betrokken attributen:
// Invoke vlak voor het invoeren van Simulator::Run ()
GtkConfigStore-configuratie;
config.ConfigureDefaults ();
config.ConfigureAttributen ();
Wanneer u nu het script uitvoert, zou er een GUI moeten verschijnen, waarmee u menu's van kunt openen
attributen op verschillende knooppunten/objecten en start vervolgens de uitvoering van de simulatie wanneer u
zijn klaar.
toekomst zelfstandig
Er zijn een aantal mogelijke verbeteringen:
· Bewaar een uniek versienummer met datum en tijd aan het begin van het bestand.
· Bewaar rng initial seed ergens.
· Laat elke RandomVariable zijn eigen initiële seed serialiseren en lees het later opnieuw.
Object namen
Placeholder hoofdstuk
Logging
De ns-3 logging-faciliteit kan worden gebruikt om de voortgang van de simulatie te bewaken of te debuggen
programma's. Logboekuitvoer kan worden ingeschakeld door programma-instructies in uw hoofd() programma of
door het gebruik van de NS_LOG variabele omgeving.
Logging-instructies worden niet gecompileerd in geoptimaliseerde builds van ns-3. Om logboekregistratie te gebruiken, één
moet de (standaard) debug build van bouwen ns-3.
Het project geeft geen garantie of de logboekoutput hetzelfde zal blijven
tijd. Gebruikers worden gewaarschuwd tegen het bouwen van simulatie-uitvoerframeworks bovenop logboekregistratie
code, omdat de uitvoer en de manier waarop de uitvoer wordt ingeschakeld in de loop van de tijd kunnen veranderen.
Overzicht
ns-3 logging-instructies worden meestal gebruikt om verschillende programma-uitvoeringsgebeurtenissen te loggen, zoals
zoals het optreden van simulatiegebeurtenissen of het gebruik van een bepaalde functie.
Dit codefragment is bijvoorbeeld afkomstig uit Ipv4L3Protocol::IsBestemmingsadres():
als (adres == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("Voor mij (uitzendadres interface)");
return true;
}
Als logboekregistratie is ingeschakeld voor de Ipv4L3-protocol component met een ernst van LOGICA or
hierboven (zie hieronder over de ernst van logs), wordt de verklaring afgedrukt; anders zou het
zal worden onderdrukt.
inschakelen uitgang
Er zijn twee manieren waarop gebruikers doorgaans de logboekuitvoer regelen. De eerste is door het instellen van de
NS_LOG omgevingsvariabele; bijv.:
$ NS_LOG="*" ./waf --eerst uitvoeren
zal de runnen eerste zelfstudieprogramma met alle logboekuitvoer. (De bijzonderheden van de NS_LOG
formaat wordt hieronder besproken.)
Dit kan gedetailleerder worden gemaakt door afzonderlijke componenten te selecteren:
$ NS_LOG="Ipv4L3Protocol" ./waf --eerst uitvoeren
De uitvoer kan verder worden aangepast met voorvoegselopties.
De tweede manier om logboekregistratie in te schakelen, is door expliciete instructies in uw programma te gebruiken, zoals in
the eerste zelfstudieprogramma:
int
hoofd (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
...
(De betekenis van LOG_LEVEL_INFO, en andere mogelijke waarden, worden hieronder besproken.)
NS_LOG Syntaxis
De NS_LOG omgevingsvariabele bevat een lijst met logboekcomponenten en opties. Logboek
componenten worden gescheiden door `:'-tekens:
$ NS_LOG=" : ..."
Opties voor elke logcomponent worden gegeven als vlaggen na elke logcomponent:
$ NS_LOG=" = | ...: ..."
Opties bepalen de ernst en het niveau voor dat onderdeel, en of het optioneel is
informatie moet worden opgenomen, zoals de simulatietijd, simulatieknooppunt, functie
naam en de symbolische ernst.
Log Componenten
Over het algemeen verwijst een logcomponent naar een enkele broncode . Cc bestand, en omvat de
hele dossier.
Sommige helpers hebben speciale methoden om het loggen van alle componenten in een module mogelijk te maken,
verspreid over verschillende compilatie-eenheden, maar logisch gegroepeerd, zoals de ns-3
wifi-code:
WifiHelper wifiHelper;
wifiHelper.EnableLogComponents ();
De NS_LOG log component wildcard `*' zal alle componenten inschakelen.
Om te zien welke logboekcomponenten zijn gedefinieerd, werkt elk van deze:
$ NS_LOG="afdruklijst" ./waf --run ...
$ NS_LOG="foo" # een token dat niet overeenkomt met een log-component
Het eerste formulier zal de naam en ingeschakelde vlaggen afdrukken voor alle logboekcomponenten die dat wel zijn
gekoppeld; probeer het mee scratch-simulator. Het tweede formulier drukt alle geregistreerde logboeken af
componenten en sluit vervolgens af met een fout.
Strengheid en Niveau opties
Afzonderlijke berichten behoren tot een enkele "ernstklasse", ingesteld door de macro die de
bericht. In het bovenstaande voorbeeld NS_LOG_LOGIC(..) maakt het bericht in de LOG_LOGIC
ernst klasse.
De volgende ernstklassen zijn gedefinieerd als opsomming constanten:
┌───────────────┬─────────────────────── ────────── ─┐
│Severity Class │ Betekenis │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_NONE │ De standaard, geen logboekregistratie │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_ERROR │ Alleen ernstige foutmeldingen │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_WARN │ Waarschuwingsberichten │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_DEBUG │ Voor gebruik bij foutopsporing │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_INFO │ Informatief │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_FUNCTION │ Functietracering │
├───────────────┼─────────────────────── ────────── ─┤
│LOG_LOGIC │ Beheer stroomtracing binnen │
│ │ functies │
└───────────────┴─────────────────────── ────────── ─┘
Meestal wil men berichten zien met een bepaalde ernstklasse en hoger. Dit wordt gedaan door
definiëren van inclusieve logging "niveaus":
┌───────────────────┬─────────────────── ────────── ─────┐
│Niveau │ Betekenis │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_ERROR │ Alleen LOG_ERROR ernstklasse │
│ │ berichten. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_WARN │ LOG_WARN en hoger. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_DEBUG │ LOG_DEBUG en hoger. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_INFO │ LOG_INFO en hoger. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_FUNCTION │ LOG_FUNCTION en hoger. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_LOGIC │ LOG_LOGIC en hoger. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_LEVEL_ALL │ Alle ernstklassen. │
├───────────────────┼─────────────────── ────────── ─────┤
│LOG_ALL │ Synoniem voor LOG_LEVEL_ALL │
└───────────────────┴─────────────────── ────────── ─────┘
De ernstklasse en niveau-opties kunnen worden opgegeven in de NS_LOG omgevingsvariabele door
deze penningen:
┌─────────┬────────────────┐
│Klasse │ Niveau │
├─────────┼────────────────┤
│fout │ niveau_fout │
├─────────┼────────────────┤
│waarschuwen │ niveau_waarschuwen │
├─────────┼────────────────┤
│debug │ niveau_debug │
├─────────┼────────────────┤
│info │ niveau_info │
├─────────┼────────────────┤
│functie │ niveau_functie │
├─────────┼────────────────┤
│logica │ niveau_logica │
├─────────┼────────────────┤
│ niveau_alles │
│ allen │
│ * │
└─────────┴────────────────┘
Het gebruik van een token voor een ernstklasse maakt alleen logberichten met die ernst mogelijk. Bijvoorbeeld,
NS_LOG="*=waarschuwen" zal geen berichten met ernst uitvoeren fout. NS_LOG="*=niveau_debug" wil
uitvoer berichten op ernstniveaus debug en hoger.
Ernstklassen en -niveaus kunnen worden gecombineerd met de `|' telefoniste:
NS_LOG="*=level_warn|logica" zal berichten uitvoeren op ernstniveaus fout, waarschuwen en logica.
De NS_LOG wildcard voor ernstniveau `*' en allen zijn synoniemen voor niveau_alles.
Voor logcomponenten die alleen worden genoemd in NS_LOG
$ NS_LOG=" :..."
de standaard ernst is LOG_LEVEL_ALL.
Voorvoegsel opties
Een aantal voorvoegsels kan helpen identificeren waar en wanneer een bericht afkomstig is, en waarvandaan
ernst.
De beschikbare voorvoegselopties (zoals opsomming constanten) zijn
┌─────────────────┬───────────────────── ────────── ───┐
│Voorvoegselsymbool │ Betekenis │
├─────────────────┼───────────────────── ────────── ───┤
│LOG_PREFIX_FUNC │ Prefix de naam van de roeping │
│ │ functie. │
├─────────────────┼───────────────────── ────────── ───┤
│LOG_PREFIX_TIME │ Prefix de simulatietijd. │
├─────────────────┼───────────────────── ────────── ───┤
│LOG_PREFIX_NODE │ Prefix de knooppunt-ID. │
├─────────────────┼───────────────────── ────────── ───┤
│LOG_PREFIX_LEVEL │ Prefix het ernstniveau. │
├─────────────────┼───────────────────── ────────── ───┤
│LOG_PREFIX_ALL │ Schakel alle voorvoegsels in. │
└─────────────────┴───────────────────── ────────── ───┘
De prefix-opties worden hieronder kort beschreven.
De opties kunnen worden gegeven in de NS_LOG omgevingsvariabele door deze tokens:
┌─────────────┬───────────┐
│Token │ Alternatief │
├─────────────┼───────────┤
│voorvoegsel_func │ func │
├─────────────┼───────────┤
│voorvoegsel_tijd │ Time to │
└─────────────┴───────────┘
│voorvoegsel_knooppunt │ knooppunt │
├─────────────┼───────────┤
│voorvoegsel_niveau │ niveau │
├─────────────┼───────────┤
│voorvoegsel_alles │ allen │
│ * │
└─────────────┴───────────┘
Voor logcomponenten die alleen worden genoemd in NS_LOG
$ NS_LOG=" :..."
de standaard voorvoegselopties zijn LOG_PREFIX_ALL.
Strengheid Voorvoegsel
Bij de opties kan de ernstklasse van een bericht worden opgenomen voorvoegsel_niveau or niveau.
Deze waarde van bijvoorbeeld NS_LOG maakt logboekregistratie mogelijk voor alle logboekcomponenten (`*') en alle
ernstklassen (=alles), en geeft het bericht een voorvoegsel met de ernstklasse (|voorvoegsel_niveau).
$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
Krassimulator
[ERROR] foutmelding
[WARN] waarschuwingsbericht
[DEBUG] foutopsporingsbericht
[INFO] infobericht
[FUNCT] functiebericht
[LOGIC] logisch bericht
Tijd Voorvoegsel
De simulatietijd kan bij de opties worden meegenomen voorvoegsel_tijd or Time to. Hiermee drukt u de
simulatietijd in seconden.
Knooppunt Voorvoegsel
De simulatie node id kan worden opgenomen in de opties voorvoegsel_knooppunt or knooppunt.
Functie Voorvoegsel
De naam van de aanroepende functie kan bij de opties worden opgenomen voorvoegsel_func or func.
NS_LOG wildcards
Het jokerteken `*' van de logcomponent schakelt alle componenten in. Om alle componenten op a
gebruik van een specifiek ernstniveau *=.
Het jokerteken '*' voor de optie voor het ernstniveau is een synoniem voor allen. Dit moet vóór elk gebeuren
`|' karakters die opties scheiden. Gebruik om alle ernstklassen in te schakelen =*,
or =*|.
De optie wildcard `*' of token allen maakt alle voorvoegselopties mogelijk, maar moet voorkomen na a
`|' karakter. Gebruik om een specifieke ernstklasse of -niveau en alle voorvoegsels in te schakelen
= |*.
De gecombineerde optie wildcard ** maakt alle ernst en alle voorvoegsels mogelijk; Bijvoorbeeld,
=**.
De uber-wildcard *** maakt alle ernst en alle prefixen mogelijk voor alle logboekcomponenten.
Deze zijn allemaal gelijkwaardig:
$ NS_LOG="***" ... $ NS_LOG="*=alles|*" ... $ NS_LOG="*=*|alles" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$NS_LOG="*=*|*" ...
Let op: zelfs de triviale scratch-simulator produceert meer dan 46 uitvoerlijnen met
NS_LOG="***"!
Hoe naar toevoegen logging naar jouw code
Het toevoegen van logging aan uw code is heel eenvoudig:
1. Roep de NS_LOG_COMPONENT_DEFINE (...); macro binnenkant van namespace ns3.
Maak een unieke tekenreeks-ID (meestal gebaseerd op de naam van het bestand en/of class
gedefinieerd in het bestand) en registreer het met een macro-oproep zoals hieronder:
naamruimte ns3 {
NS_LOG_COMPONENT_DEFINE ("Ipv4L3-protocol");
...
Dit registreert Ipv4L3-protocol als logcomponent.
(De macro is zorgvuldig geschreven om opname binnen of buiten toe te staan
namespace ns3, en het gebruik zal variëren over de codebase, maar de oorspronkelijke bedoeling was om
registreer dit buiten van naamruimte ns3 bij bestand wereldwijd bereik.)
2. Voeg logboekverklaringen (macro-oproepen) toe aan uw functies en functie-organen.
Logging Macro's
De logboekmacro's en bijbehorende ernstniveaus zijn
┌───────────────┬─────────────────────── ─┐
│Ernstklasse │ Macro │
├───────────────┼─────────────────────── ─┤
│LOG_NONE │ (niet nodig) │
├───────────────┼─────────────────────── ─┤
│LOG_ERROR │ NS_LOG_ERROR (...); │
├───────────────┼─────────────────────── ─┤
│LOG_WARN │ NS_LOG_WARN (...); │
├───────────────┼─────────────────────── ─┤
│LOG_DEBUG │ NS_LOG_DEBUG (...); │
├───────────────┼─────────────────────── ─┤
│LOG_INFO │ NS_LOG_INFO (...); │
├───────────────┼─────────────────────── ─┤
│LOG_FUNCTION │ NS_LOG_FUNCTION (...); │
├───────────────┼─────────────────────── ─┤
│LOG_LOGIC │ NS_LOG_LOGIC (...); │
└───────────────┴─────────────────────── ─┘
De macro's functioneren als uitvoerstreamers, dus alles waarnaar u kunt verzenden standaard::couttoegetreden
by << exploitanten, is toegestaan:
void MyClass::Check (int waarde, char * item)
{
NS_LOG_FUNCTION (dit << arg << item);
als (arg > 10)
{
NS_LOG_ERROR ("Ongeldige waarde aangetroffen" << waarde <
" tijdens het controleren van " << naam << "!");
}
...
}
Merk op dat NS_LOG_FUNCTION voegt automatisch een ` in,' (komma-spatie) scheidingsteken tussen
elk van zijn argumenten. Dit vereenvoudigt het loggen van functieargumenten; gewoon samenvoegen
hen << zoals in het bovenstaande voorbeeld.
Onvoorwaardelijk Logging
Voor het gemak is de NS_LOG_UNCOND (...); macro zal altijd zijn argumenten loggen, zelfs als
de bijbehorende log-component is op geen enkele manier ingeschakeld. Deze macro gebruikt er geen
van de voorvoegselopties. Houd er rekening mee dat logboekregistratie alleen is ingeschakeld in builds voor foutopsporing; deze macro
produceert geen output in geoptimaliseerde builds.
Richtlijnen
· Begin elke lesmethode met NS_LOG_FUNCTION (deze << argumenten...); Dit maakt gemakkelijk
traceren van functieoproepen.
· Behalve: log geen operatoren of expliciete kopieerconstructors in, aangezien deze leiden tot
oneindige recursie en stapeloverloop.
· Gebruik voor methoden zonder argumenten hetzelfde formulier: NS_LOG_FUNCTION (dit);
· Voor statische functies:
· Met argumenten gebruiken NS_LOG_FUNCTION (...); zoals normaal.
· Gebruik zonder argumenten NS_LOG_FUNCTION_NOARGS ();
· Gebruik maken van NS_LOG_ERROR voor ernstige foutcondities die de simulatie waarschijnlijk ongeldig maken
uitvoering.
· Gebruik maken van NS_LOG_WARN voor ongebruikelijke omstandigheden die mogelijk corrigeerbaar zijn. Geef alsjeblieft wat tips
over de aard van het probleem en hoe het kan worden verholpen.
· NS_LOG_DEBUG wordt meestal gebruikt in een ad hoc manier om de uitvoering van een model te begrijpen.
· Gebruik maken van NS_LOG_INFO voor aanvullende informatie over de uitvoering, zoals de grootte van een
gegevensstructuur bij het toevoegen/verwijderen ervan.
· Gebruik maken van NS_LOG_LOGIC om belangrijke logische takken binnen een functie te traceren.
· Test of uw logging-wijzigingen de code niet breken. Voer enkele voorbeeldprogramma's uit met
alle logcomponenten ingeschakeld (bijv NS_LOG="***").
Tracing
Het traceringssubsysteem is een van de belangrijkste mechanismen om te begrijpen ns-3. in
meeste gevallen, ns-3 gebruikers zullen een briljant idee hebben voor een nieuw en verbeterd netwerk
functie. Om te controleren of dit idee werkt, zal de onderzoeker wijzigingen aanbrengen in een
bestaande systeem en voer vervolgens experimenten uit om te zien hoe de nieuwe functie zich gedraagt door te verzamelen
statistieken die het gedrag van de functie vastleggen.
Met andere woorden, het hele punt van het uitvoeren van een simulatie is om output te genereren voor verder gebruik
studie. In ns-3, het subsysteem dat een onderzoeker in staat stelt dit te doen, is de tracering
subsysteem.
Tracing Motivatie
Er zijn veel manieren om informatie uit een programma te halen. De meest eenvoudige manier is
om de informatie direct af te drukken naar de standaarduitvoer, zoals in,
#inclusief
...
int hoofd ()
{
...
std::cout << "De waarde van x is " << x << std::endl;
...
}
Dit is werkbaar in kleine omgevingen, maar naarmate uw simulaties steeds uitgebreider worden
gecompliceerd, krijg je steeds meer afdrukken en de taak van ontleden en uitvoeren
berekeningen op de uitvoer beginnen steeds moeilijker te worden.
Een ander ding om te overwegen is dat elke keer dat er een nieuw stukje nodig is, de softwarekern is
moet worden bewerkt en een andere afdruk moet worden geïntroduceerd. Er is geen gestandaardiseerde manier om alles te controleren
van deze output, dus de hoeveelheid output neigt grenzeloos te groeien. Uiteindelijk, de
bandbreedte die nodig is om deze informatie eenvoudig uit te voeren, begint de looptijd te beperken
van de simulatie. De uitvoerbestanden worden enorm groot en het ontleden ervan wordt een
probleem.
ns-3 biedt een eenvoudig mechanisme voor logboekregistratie en biedt enige controle over uitvoer via
Log Componenten , maar het controleniveau is helemaal niet erg fijnmazig. De logging
module is een relatief bot instrument.
Het is wenselijk om een faciliteit te hebben die het mogelijk maakt om alleen in het kernsysteem te reiken
verkrijg de vereiste informatie zonder het kernsysteem te wijzigen en opnieuw te compileren. Zelfs
beter zou een systeem zijn dat de gebruiker op de hoogte stelt wanneer een interessant item is gewijzigd of een
interessante gebeurtenis gebeurde.
De ns-3 traceringssysteem is ontworpen om langs die lijnen te werken en is goed geïntegreerd met
de substammen Attribute en Config maken relatief eenvoudige gebruiksscenario's mogelijk.
Overzicht
Het traceringssubsysteem is sterk afhankelijk van de ns-3 Terugbel- en attribuutmechanismen. Jij
moet de overeenkomstige delen van de handleiding lezen en begrijpen voordat u dit probeert
het traceersysteem begrijpen.
De ns-3 traceersysteem is gebaseerd op de concepten van onafhankelijke traceerbronnen en
het opsporen van putten; samen met een uniform mechanisme voor het aansluiten van bronnen op putten.
Traceerbronnen zijn entiteiten die gebeurtenissen die in een simulatie plaatsvinden kunnen signaleren en bieden
toegang tot interessante onderliggende data. Een traceerbron kan bijvoorbeeld aangeven wanneer een
pakket wordt ontvangen door een netapparaat en geeft toegang tot de inhoud van het pakket
geïnteresseerde sporen zinken. Een traceerbron kan ook aangeven wanneer een interessante status is
verandering vindt plaats in een model. Het congestievenster van een TCP-model is bijvoorbeeld een priemgetal
kandidaat voor een sporenbron.
Traceerbronnen zijn op zichzelf niet nuttig; ze moeten worden verbonden met andere stukjes code
die daadwerkelijk iets nuttigs doen met de informatie van de bron. De
entiteiten die traceergegevens verbruiken, worden trace-sinks genoemd. Spoorbronnen zijn
generatoren van gebeurtenissen en trace-sinks zijn consumenten.
Door deze expliciete indeling kunnen grote aantallen sporenbronnen worden verspreid
het systeem op plaatsen waarvan de auteurs van het model denken dat ze nuttig kunnen zijn. Tenzij een gebruiker een
trace sink naar een van deze bronnen, er wordt niets uitgevoerd. Deze opstelling maakt relatief
onervaren gebruikers om nieuwe soorten putten aan bestaande traceerbronnen te koppelen, zonder
waarvoor het bewerken en opnieuw compileren van de kern of modellen van de simulator vereist is.
Er kunnen nul of meer gebruikers zijn van traceergebeurtenissen die door een traceerbron worden gegenereerd. Men kan
denk aan een traceerbron als een soort punt-naar-multipunt-informatieverbinding.
Het "transportprotocol" voor deze conceptuele point-to-multipoint-verbinding is een ns-3 Terugbellen.
Bedenk uit de Callback-sectie dat callback-faciliteit een manier is om twee modules toe te laten
het systeem om te communiceren via functieaanroepen en tegelijkertijd de oproeping te ontkoppelen
volledig functioneren vanuit de aangeroepen klasse. Dit is dezelfde vereiste als hierboven beschreven
voor het traceersysteem.
Kortom, een traceerbron is een callback waarop meerdere functies kunnen worden geregistreerd.
Wanneer een trace-sink interesse toont in het ontvangen van traceergebeurtenissen, voegt het een callback toe aan een
lijst met callbacks die door de traceerbron worden vastgehouden. Wanneer er een interessante gebeurtenis plaatsvindt, is de trace
bron beroept zich op zijn exploitant() het verstrekken van nul of meer parameters. Dit vertelt de bron aan
doorloop de lijst met callbacks en roept ze beurtelings op. Op deze manier worden de parameter(s)
worden gecommuniceerd naar de trace-sinks, die slechts functies zijn.
De eenvoudigste Voorbeeld
Het zal nuttig zijn om een kort voorbeeld te gaan lopen om te versterken wat we hebben gezegd.:
#include "ns3/object.h"
#include "ns3/uinteger.h"
#include "ns3/traced-value.h""
#include "ns3/trace-source-accessor.h"
#inclusief
naamruimte ns3 gebruiken;
Het eerste dat u moet doen, is de vereiste bestanden toevoegen. Zoals hierboven vermeld, het traceersysteem
maakt veel gebruik van de Object- en Attribute-systemen. De eerste twee omvatten het binnenhalen van de
declaraties voor die systemen. Het bestand, getraceerde waarde.h levert het nodige op
declaraties voor het traceren van gegevens die voldoen aan de waardesemantiek.
Over het algemeen betekent waardesemantiek alleen dat u het object kunt doorgeven, niet een
adres. Om waardesemantiek überhaupt te gebruiken, moet je een object hebben met een
bijbehorende kopieerconstructor en toewijzingsoperator beschikbaar. We breiden de vereisten uit
om te praten over de set operators die vooraf zijn gedefinieerd voor typen gewone oude gegevens (POD).
Operator=, operator++, operator--, operator+, operator==, enz.
Wat dit allemaal betekent, is dat u wijzigingen aan een object kunt traceren die zijn gemaakt met behulp van
die operatoren.:
klasse MyObject : openbaar object
{
publiek:
statisch TypeId GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("MijnObject")
.SetParent (Object::GetTypeId ())
.Constructor toevoegen ()
.AddTraceSource ("MyInteger",
"Een geheel getal om te traceren.",
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
tijd teruggeven;
}
MijnObject () {}
Getraceerde waarde m_mijnInt;
};
Omdat het traceersysteem is geïntegreerd met Attributen en Attributen werken met Objecten,
er moet een ns-3 Object voor de sporenbron om in te leven. De twee belangrijke lijnen van
code zijn de .Voeg TraceSource toe en Getraceerde waarde verklaring.
De .Voeg TraceSource toe biedt de "hooks" die worden gebruikt voor het verbinden van de traceerbron met de
buitenwereld. De Getraceerde waarde aangifte zorgt voor de infrastructuur die de
hierboven genoemde operators en stuurt het terugbelproces aan.:
komen te vervallen
IntTrace (Int oudeWaarde, Int nieuweWaarde)
{
std::cout << "Traced " << oldValue << " to " << newValue << std::endl;
}
Dit is de definitie van de trace-sink. Het komt rechtstreeks overeen met een callback-functie.
Deze functie wordt aangeroepen wanneer een van de operators van de Getraceerde waarde is
uitgevoerd.:
int
hoofd (int argc, char *argv[])
{
Ptr mijnObject = CreëerObject ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
mijnObject->m_mijnInt = 1234;
}
In dit fragment is het eerste dat moet worden gedaan, het maken van het object waarin
de sporenbron leeft.
De volgende stap, de TraceConnectZonderContext, vormt de verbinding tussen het spoor
source en de trace-sink. Let op de MaakTerugbellen sjabloon functie. Terugroepen uit de
Callback-sectie dat dit de gespecialiseerde functor creëert die verantwoordelijk is voor het leveren van de
overbelast exploitant() gebruikt om de callback te "afvuren". De overbelaste operatoren (++, --, etc.)
zal dit gebruiken exploitant() om de callback daadwerkelijk aan te roepen. De TraceConnectZonderContext,
neemt een tekenreeksparameter die de naam geeft van het attribuut dat aan de tracering is toegewezen
bron. Laten we het stukje over context voorlopig negeren, aangezien het nog niet belangrijk is.
Eindelijk de regel:
mijnObject->m_mijnInt = 1234;
moet worden geïnterpreteerd als een aanroeping van exploitant= op de lidvariabele m_mijnInt with
het gehele getal 1234 doorgegeven als parameter. Het blijkt dat deze operator is gedefinieerd (door
Getraceerde waarde) om een callback uit te voeren die void retourneert en twee integer-waarden aanneemt als
parameters -- een oude waarde en een nieuwe waarde voor het gehele getal in kwestie. Dat is precies
de functiehandtekening voor de callback-functie die we hebben geleverd -- IntTrace.
Samenvattend is een traceerbron in wezen een variabele die een lijst met callbacks bevat. A
trace sink is een functie die wordt gebruikt als doel van een callback. Het kenmerk en objecttype
informatiesystemen worden gebruikt om een manier te bieden om traceerbronnen te verbinden met traceerputten. De
handeling van het "raken" van een traceerbron is het uitvoeren van een operator op de traceerbron die vuurt
terugbellen. Dit resulteert in de trace-sink-callbacks die interesse in de bron registreren
wordt aangeroepen met de parameters die door de bron worden verstrekt.
gebruik the Config Subsysteem naar Connect naar Opsporen Bronnen
De TraceConnectZonderContext oproep hierboven weergegeven in het eenvoudige voorbeeld is eigenlijk heel
zelden gebruikt in het systeem. Meer typisch, de Config subsysteem wordt gebruikt om te kunnen selecteren
een traceerbron in het systeem met behulp van wat een wordt genoemd config pad.
U kunt bijvoorbeeld iets vinden dat er als volgt uitziet in het systeem (genomen
vanaf voorbeelden/tcp-grote-overdracht.cc):
leegte CwndTracer (uint32_t oude waarde, uint32_t nieuwe waarde) {}
...
Config::VerbindenZonderContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
MakeCallback (&CwndTracer));
Dit zou er heel bekend uit moeten zien. Het is hetzelfde als het vorige voorbeeld, behalve dat
een statische lidfunctie van klasse Config wordt aangeroepen in plaats van een methode aan Object;
en in plaats van een Kenmerk name, wordt er een pad opgegeven.
Het eerste dat u moet doen, is het pad achteruit lezen. Het laatste segment van het pad moet zijn
an Kenmerk een Object. Sterker nog, als je een aanwijzing had naar de Object dat heeft de
"Congestievenster" Kenmerk handig (noem maar het object), zou je dit kunnen schrijven net als de
vorig voorbeeld:
leegte CwndTracer (uint32_t oude waarde, uint32_t nieuwe waarde) {}
...
theObject->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
Het blijkt dat de code voor Config::VerbindenZonderContext doet precies dat. Dit
functie neemt een pad dat een keten vertegenwoordigt van Object pointers en volgt ze totdat het
bereikt het einde van het pad en interpreteert het laatste segment als een Kenmerk op de laatste
voorwerp. Laten we eens kijken wat er gebeurt.
Het voorloopteken "/" in het pad verwijst naar een zogenaamde naamruimte. Een van de
vooraf gedefinieerde naamruimten in het configuratiesysteem is "NodeList", een lijst van alle
knopen in de simulatie. Items in de lijst worden door indexen in de lijst vermeld, dus
"/NodeList/0" verwijst naar het nulde knooppunt in de lijst met knooppunten die door de simulatie is gemaakt.
Dit knooppunt is eigenlijk een Ptr en zo is een subklasse van een ns3::Object.
Zoals beschreven in de sectie Objectmodel, ns-3 ondersteunt een objectaggregatiemodel. De
het volgende padsegment begint met het "$"-teken dat een aangeeft GetObject bellen moet zijn
op zoek gegaan naar het type dat volgt. Wanneer een knooppunt wordt geïnitialiseerd door een
InternetStackHelper een aantal interfaces zijn geaggregeerd naar het knooppunt. Een daarvan is de
TCP niveau vier protocol. Het runtime-type van dit protocolobject is ns3::TcpL4Protocol''.
. the ``GetObject wordt uitgevoerd, retourneert het een pointer naar het object van dit type.
De TcpL4 Protocol class definieert een attribuut genaamd "SocketList" dat een lijst is van
stopcontacten. Elke socket is eigenlijk een ns3::Object met eigen Attributen. De artikelen binnen
de lijst met sockets wordt aangeduid met een index, net als in de NodeList, dus "SocketList/0"
verwijst naar de nulde socket in de lijst met sockets op de nulde node in de NodeList --
het eerste knooppunt dat in de simulatie is gebouwd.
Dit stopcontact, waarvan het type een blijkt te zijn ns3::TcpSocketImpl definieert een attribuut
genaamd "CongestionWindow", wat een Getraceerde waarde. De
Config::VerbindenZonderContext doet nu een,:
object->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
met behulp van de objectwijzer van "SocketList/0" die de verbinding tussen de trace maakt
bron gedefinieerd in de socket naar de callback -- CwndTracer.
Nu, wanneer er een wijziging wordt aangebracht in de Getraceerde waarde vertegenwoordigen de congestie
venster in de TCP-socket, wordt de geregistreerde callback uitgevoerd en wordt de functie
CwndTracer wordt het afdrukken van de oude en nieuwe waarden van de TCP-congestie genoemd
venster.
gebruik the Tracing API
Er zijn drie niveaus van interactie met het traceersysteem:
· Beginnende gebruikers kunnen eenvoudig bepalen welke objecten deelnemen aan tracering;
· Gevorderde gebruikers kunnen het traceersysteem uitbreiden om het gegenereerde uitvoerformaat te wijzigen
of bestaande traceerbronnen op verschillende manieren gebruiken, zonder de kern van de
simulator;
· Gevorderde gebruikers kunnen de kern van de simulator aanpassen om nieuwe traceerbronnen en -sinks toe te voegen.
gebruik Opsporen helpers
De ns-3 trace-helpers bieden een rijke omgeving voor het configureren en selecteren van verschillende
traceer gebeurtenissen en schrijf ze naar bestanden. In voorgaande paragrafen, voornamelijk "Bouwen
Topologieën", hebben we verschillende varianten van de traceerhulpmethoden gezien die voor gebruik zijn ontworpen
binnen andere (apparaat)helpers.
Misschien herinnert u zich enkele van deze variaties:
pointToPoint.EnablePcapAll ("tweede");
pointToPoint.EnablePcap ("tweede", p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap ("derde", csmaDevices.Get (0), waar);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("mijneerste.tr"));
Wat misschien niet voor de hand ligt, is dat er een consistent model is voor alle
trace-gerelateerde methoden gevonden in het systeem. We zullen nu even de tijd nemen en een kijkje nemen
op het "grote plaatje".
Er zijn momenteel twee primaire use-cases van de traceringshelpers in ns-3: Hulpmiddelen voor apparaten
en protocolleraars. Apparaathelpers kijken naar het probleem om te specificeren welke sporen moeten
worden ingeschakeld via een knooppunt, apparaatpaar. U kunt bijvoorbeeld dat pcap
tracering moet zijn ingeschakeld op een bepaald apparaat op een specifiek knooppunt. Dit volgt uit de
ns-3 apparaat conceptueel model, en ook de conceptuele modellen van de verschillende apparaten
helpers. In navolging hiervan volgen de aangemaakte bestanden een
- - naamgeving.
Protocolhelpers kijken naar het probleem van het specificeren van welke traceringen moeten worden ingeschakeld
een protocol en interfacepaar. Dit volgt uit de ns-3 protocol stapel conceptueel model,
en ook de conceptuele modellen van internetstackhelpers. Natuurlijk, de traceerbestanden
moet volgen a - - naamgeving.
De sporenhelpers vallen daarom op natuurlijke wijze in een tweedimensionale taxonomie. Er zijn
subtiliteiten die voorkomen dat alle vier de klassen zich identiek gedragen, maar we streven er wel naar
laat ze allemaal zo gelijk mogelijk werken; en waar mogelijk zijn er analogen voor
alle methoden in alle klassen.
┌────────────────┬──────┬───────┐
│ │ pcap │ ascii │
├────────────────┼──────┼───────┤
│Apparaathulp │ │ │
├────────────────┼──────┼───────┤
│Protocolhelper │ │ │
└────────────────┴──────┴───────┘
We gebruiken een benadering genaamd a mixin om traceerfunctionaliteit toe te voegen aan onze helperklassen. A
mixin is een klasse die functionaliteit biedt die wordt geërfd door een subklasse.
Erven van een mixin wordt niet beschouwd als een vorm van specialisatie, maar is echt een manier om dat te doen
functionaliteit verzamelen.
Laten we eens kijken naar alle vier deze gevallen en hun respectieve mixen.
Pkap Tracing Apparaat helpers
Het doel van deze helpers is om het gemakkelijk te maken om een consistente pcap-traceervoorziening toe te voegen aan een
ns-3 apparaat. We willen dat alle verschillende smaken van pcap-tracing overal hetzelfde werken
alle apparaten, dus de methoden van deze helpers worden overgenomen door apparaathelpers. Kijk eens
at src/netwerk/helper/trace-helper.h als je de discussie wilt volgen terwijl je kijkt
echte codes.
De klas PcapHelperForDevice is een mixin biedt de functionaliteit op hoog niveau voor gebruik
pcap-tracering in een ns-3 apparaat. Elk apparaat moet een enkele virtuele methode implementeren
geërfd van deze klasse.:
virtuele leegte EnablePcapInternal (std::string prefix, Ptr nd, bool promiscue) = 0;
De handtekening van deze methode weerspiegelt de apparaatgerichte kijk op de situatie hierbij
niveau. Alle openbare methoden geërfd van klasse PcapUserHelperForDevice verminderen
deze enkele apparaatafhankelijke implementatiemethode noemen. Bijvoorbeeld het laagste niveau
pcap-methode,:
void EnablePcap (std::string prefix, Ptr nd, bool promiscue = false, bool expliciete bestandsnaam = false);
zal de implementatie van het apparaat noemen PcapInternal inschakelen direct. Alle andere openbare pcap
traceringsmethoden bouwen voort op deze implementatie om extra gebruikersniveau te bieden
functionaliteit. Wat dit voor de gebruiker betekent, is dat alle apparaathelpers in het systeem dat zullen doen
alle pcap-traceermethoden beschikbaar hebben; en deze methoden zullen allemaal op dezelfde manier werken
weg over apparaten als het apparaat implementeert PcapInternal inschakelen correct.
Pkap Tracing Apparaat Helper Methoden
void EnablePcap (std::string prefix, Ptr en,
bool promiscue = false, bool expliciete bestandsnaam = false);
void EnablePcap (std::stringvoorvoegsel, std::string ndName,
bool promiscue = false, bool expliciete bestandsnaam = false);
void EnablePcap (std::stringvoorvoegsel, NetDeviceContainer d,
bool promiscue = onwaar);
void EnablePcap (std::stringvoorvoegsel, NodeContainer n,
bool promiscue = onwaar);
void EnablePcap (std::stringvoorvoegsel, uint32_t knooppunt, uint32_t apparaatid,
bool promiscue = onwaar);
void EnablePcapAll (std::string-voorvoegsel, bool promiscuous = false);
Bij elk van de hierboven getoonde methoden is er een standaardparameter genaamd gemengd uit die
standaard ingesteld op onwaar. Deze parameter geeft aan dat de tracering niet moet worden verzameld
Promiscuous mode. Als u wilt dat uw traceringen al het verkeer bevatten dat door het apparaat wordt gezien
(en als het apparaat een promiscue modus ondersteunt) voegt u gewoon een echte parameter toe aan een van de
roept hierboven. Bijvoorbeeld,:
Ptr zd;
...
helper.EnablePcap ("prefix", nd, true);
zal promiscuous mode captures mogelijk maken op de Netapparaat gespecificeerd door nd.
De eerste twee methoden bevatten ook een standaardparameter genaamd expliciete bestandsnaam dat zal
hieronder worden besproken.
U wordt aangemoedigd om de Doxygen voor de les door te nemen PcapHelperForDevice om de details te vinden
van deze methoden; maar om samen te vatten...
U kunt pcap-tracering inschakelen op een bepaald node/net-device-paar door een
Ptr een Pcap inschakelen methode. De Ptr is impliciet sinds het net-apparaat
moet tot precies één behoren Knooppunt. Bijvoorbeeld,:
Ptr zd;
...
helper.EnablePcap ("prefix", nd);
U kunt pcap-tracering inschakelen op een bepaald node/net-device-paar door een
std::tekenreeks die een objectnaam-servicetekenreeks vertegenwoordigt voor een Pcap inschakelen methode. De
Ptr wordt opgezocht vanuit de naamreeks. Nogmaals, de is impliciet aangezien de
het genoemde net-apparaat moet tot precies één apparaat behoren Knooppunt. Bijvoorbeeld,:
Namen::Toevoegen ("server" ...);
Namen::Toevoegen ("server/eth0" ...);
...
helper.EnablePcap ("prefix", "server/ath0");
U kunt pcap-tracering inschakelen op een verzameling node/net-device-paren door een
NetDeviceContainer. Voor elk Netapparaat in de container wordt het type gecontroleerd. Voor elk
apparaat van het juiste type (hetzelfde type dat wordt beheerd door de apparaathelper), tracering is
ingeschakeld. Nogmaals, de is impliciet omdat het gevonden netapparaat precies moet behoren
een Knooppunt. Bijvoorbeeld,:
NetDeviceContainer d = ...;
...
helper.EnablePcap ("voorvoegsel", d);
U kunt pcap-tracering inschakelen op een verzameling node/net-device-paren door een
KnooppuntContainer. Voor elk Knooppunt in de KnooppuntContainer het is bijgevoegd NetApparaten worden herhaald.
Voor elk Netapparaat gekoppeld aan elk knooppunt in de container, het type van dat apparaat is
gecontroleerd. Voor elk apparaat van het juiste type (hetzelfde type als wordt beheerd door het apparaat
helper), tracering is ingeschakeld.:
KnooppuntContainer n;
...
helper.EnablePcap ("prefix", n);
U kunt pcap-tracering inschakelen op basis van node-ID en apparaat-ID, maar ook met expliciet
Ptr. Elk Knooppunt in het systeem heeft een knooppunt-ID met een geheel getal en elk apparaat is verbonden met een knooppunt
heeft een geheel getal apparaat-ID.:
helper.EnablePcap ("voorvoegsel", 21, 1);
Ten slotte kunt u pcap-tracing inschakelen voor alle apparaten in het systeem, met hetzelfde type als
die wordt beheerd door de apparaathelper.:
helper.EnablePcapAll ("voorvoegsel");
Pkap Tracing Apparaat Helper Bestandsnaam Selectie
Impliciet in de bovenstaande methodebeschrijvingen is de constructie van een volledige bestandsnaam door
de implementatiemethode. Volgens afspraak traceert pcap in de ns-3 systeem zijn van de vorm
- identiteitsbewijs>- id>.pcap
Zoals eerder vermeld, heeft elk knooppunt in het systeem een door het systeem toegewezen knooppunt-ID; En
elk apparaat heeft een interface-index (ook wel een apparaat-ID genoemd) ten opzichte van zijn knooppunt.
Standaard wordt dan een pcap-traceerbestand gemaakt als resultaat van het inschakelen van tracering op de eerste
apparaat van knooppunt 21 met behulp van het voorvoegsel "voorvoegsel" zou zijn voorvoegsel-21-1.pcap.
Je kunt altijd de ns-3 objectnaamservice om dit duidelijker te maken. Als bijvoorbeeld
u gebruikt de objectnaamservice om de naam "server" toe te wijzen aan knooppunt 21, de resulterende pcap
traceerbestandsnaam wordt automatisch, voorvoegsel-server-1.pcap en als u ook de toewijst
naam "eth0" naar het apparaat, uw pcap-bestandsnaam zal dit automatisch oppikken en zijn
genaamd voorvoegsel-server-eth0.pcap.
Tot slot twee van de hierboven getoonde methoden:
void EnablePcap (std::string prefix, Ptr nd, bool promiscue = false, bool expliciete bestandsnaam = false);
ongeldig EnablePcap (std::string prefix, std::string ndName, bool promiscue = false, bool explicietFilename = false);
hebben een standaardparameter genaamd expliciete bestandsnaam. Indien ingesteld op waar, wordt deze parameter
schakelt het automatische voltooiingsmechanisme voor bestandsnamen uit en stelt u in staat een expliciet
bestandsnaam. Deze optie is alleen beschikbaar bij de methoden die pcap-tracering op een
enkel apparaat.
Bijvoorbeeld om een apparaathelper een enkele promiscue pcap te laten maken
capture-bestand met een specifieke naam (mijn-pcap-bestand.pcap) op een bepaald apparaat kan men:
Ptr zd;
...
helper.EnablePcap ("mijn-pcap-bestand.pcap", en, waar, waar);
De eerste waar parameter maakt sporen in promiscue modus mogelijk en de tweede vertelt de helper
om het voorvoegsel parameter als een volledige bestandsnaam.
ascii Tracing Apparaat helpers
Het gedrag van de ascii-traceerhulp mixin is in wezen vergelijkbaar met de pcap-versie.
Neem een kijkje op src/netwerk/helper/trace-helper.h als je de discussie wilt volgen
terwijl je naar echte code kijkt.
De klas AsciiTraceHelperForDevice voegt de functionaliteit op hoog niveau toe voor het gebruik van ascii
traceren naar een apparaathelperklasse. Net als in het geval van pcap moet elk apparaat een
enkele virtuele methode geërfd van de ascii-tracering mixin.:
virtuele leegte EnableAsciiInternal (Ptr stream, std::tekenreeksvoorvoegsel, Ptr d) = 0;
De handtekening van deze methode weerspiegelt de apparaatgerichte kijk op de situatie hierbij
niveau; en ook het feit dat de helper mogelijk naar een gedeelde uitvoerstroom schrijft. Alles van
de openbare ascii-trace-gerelateerde methoden geërfd van klasse AsciiTraceHelperForDevice
reduceer tot het aanroepen van deze enkele apparaatafhankelijke implementatiemethode. Bijvoorbeeld de
ascii-traceermethoden op het laagste niveau:
void EnableAscii (std::string prefix, Ptr e);
leegte EnableAscii (Ptr stroom, ptr e);
zal de implementatie van het apparaat noemen AsciiInternal inschakelen rechtstreeks, waarbij ofwel a
geldig voorvoegsel of stream. Alle andere openbare ascii-opsporingsmethoden zullen hierop voortbouwen
functies op laag niveau om extra functionaliteit op gebruikersniveau te bieden. Wat betekent dit voor de
gebruiker is dat alle apparaathelpers in het systeem alle ascii-traceermethoden zullen hebben
beschikbaar; en deze methoden werken allemaal op dezelfde manier op verschillende apparaten als de apparaten
uitvoeren EnablAsciiIntern correct.
ascii Tracing Apparaat Helper Methoden
void EnableAscii (std::string prefix, Ptr e);
leegte EnableAscii (Ptr stroom, ptr e);
ongeldig EnableAscii (std::string prefix, std::string ndName);
leegte EnableAscii (Ptr stream, std::string ndName);
ongeldig EnableAscii (std::tekenreeksvoorvoegsel, NetDeviceContainer d);
leegte EnableAscii (Ptr stroom, NetDeviceContainer d);
ongeldig EnableAscii (std::tekenreeksvoorvoegsel, NodeContainer n);
leegte EnableAscii (Ptr stroom, NodeContainer n);
void EnableAscii (std::stringvoorvoegsel, uint32_t knooppunt, uint32_t apparaatid);
leegte EnableAscii (Ptr stream, uint32_t nodeid, uint32_t deviceid);
ongeldig EnableAsciiAll (std::tekenreeksvoorvoegsel);
leegte EnableAsciiAll (Ptr stroom);
U wordt aangemoedigd om de Doxygen voor de les door te nemen TraceHelperForDevice om de vondst
details van deze methoden; maar om samen te vatten...
Er zijn twee keer zoveel methoden beschikbaar voor ascii-tracering als voor pcap
traceren. Dit komt omdat, naast het pcap-achtige model, sporen van elk
unieke knooppunt/apparaat-paar worden naar een uniek bestand geschreven, we ondersteunen een model waarin trace
informatie voor veel node/device-paren wordt naar een gemeenschappelijk bestand geschreven. Dit betekent dat de
- - mechanisme voor het genereren van bestandsnamen is vervangen door een mechanisme om
verwijzen naar een gemeenschappelijk bestand; en het aantal API-methoden wordt verdubbeld om alles mogelijk te maken
combinaties.
Net als bij pcap-tracering kunt u ascii-tracering inschakelen op een bepaald node/net-device-paar
door het verstrekken van een Ptr een Ascii inschakelen methode. De Ptr is impliciet sinds
het internetapparaat moet tot precies één apparaat behoren Knooppunt. Bijvoorbeeld,:
Ptr zd;
...
helper.EnableAscii ("voorvoegsel", nd);
In dit geval worden er geen traceercontexten naar het ascii-traceerbestand geschreven, aangezien dat wel het geval zou zijn
overbodig. Het systeem kiest de aan te maken bestandsnaam volgens dezelfde regels als
beschreven in de pcap-sectie, behalve dat het bestand het achtervoegsel ".tr" zal hebben in plaats van
".pcap".
Als u ascii-tracering op meer dan één internetapparaat wilt inschakelen en alle traceringen wilt laten verzenden
naar een enkel bestand, kunt u dat ook doen door een object te gebruiken om naar een enkel bestand te verwijzen:
Ptr nd1;
Ptr nd2;
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAscii (stroom, nd1);
helper.EnableAscii (stroom, nd2);
In dit geval worden traceringscontexten naar het ascii-traceerbestand geschreven, omdat ze vereist zijn
om sporen van de twee apparaten ondubbelzinnig te maken. Merk op dat aangezien de gebruiker volledig is
als u de bestandsnaam opgeeft, moet de tekenreeks de ".tr" bevatten voor consistentie.
U kunt ascii-tracering inschakelen op een bepaald node/net-device-paar door een
std::tekenreeks die een objectnaam-servicetekenreeks vertegenwoordigt voor een Pcap inschakelen methode. De
Ptr wordt opgezocht vanuit de naamreeks. Nogmaals, de is impliciet aangezien de
het genoemde net-apparaat moet tot precies één apparaat behoren Knooppunt. Bijvoorbeeld,:
Namen::Toevoegen ("klant" ...);
Namen::Toevoegen ("klant/eth0" ...);
Namen::Toevoegen ("server" ...);
Namen::Toevoegen ("server/eth0" ...);
...
helper.EnableAscii ("voorvoegsel", "client/eth0");
helper.EnableAscii ("voorvoegsel", "server/eth0");
Dit zou resulteren in twee bestanden met de naam voorvoegsel-client-eth0.tr en voorvoegsel-server-eth0.tr with
traceringen voor elk apparaat in het respectieve traceerbestand. Aangezien alle EnableAscii
functies zijn overbelast om een stream-wrapper te nemen, je kunt dat formulier ook gebruiken:
Namen::Toevoegen ("klant" ...);
Namen::Toevoegen ("klant/eth0" ...);
Namen::Toevoegen ("server" ...);
Namen::Toevoegen ("server/eth0" ...);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAscii (stroom, "client/eth0");
helper.EnableAscii (stroom, "server/eth0");
Dit zou resulteren in een enkel traceerbestand met de naam trace-bestandsnaam.tr dat bevat alles
de traceergebeurtenissen voor beide apparaten. De gebeurtenissen zouden worden ondubbelzinnig gemaakt door de traceringscontext
snaren.
U kunt ascii-tracering inschakelen op een verzameling node/net-device-paren door een
NetDeviceContainer. Voor elk Netapparaat in de container wordt het type gecontroleerd. Voor elk
apparaat van het juiste type (hetzelfde type dat wordt beheerd door de apparaathelper), tracering is
ingeschakeld. Nogmaals, de is impliciet omdat het gevonden netapparaat precies moet behoren
een Knooppunt. Bijvoorbeeld,:
NetDeviceContainer d = ...;
...
helper.EnableAscii ("voorvoegsel", d);
Dit zou resulteren in het aanmaken van een aantal ascii-traceerbestanden, die elk volgen
de - - .tr-conventie. Het combineren van alle sporen in een
enkel bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden:
NetDeviceContainer d = ...;
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAscii (stroom, d);
U kunt ascii-tracering inschakelen op een verzameling node/net-device-paren door een
KnooppuntContainer. Voor elk Knooppunt in de KnooppuntContainer het is bijgevoegd NetApparaten worden herhaald.
Voor elk Netapparaat gekoppeld aan elk knooppunt in de container, het type van dat apparaat is
gecontroleerd. Voor elk apparaat van het juiste type (hetzelfde type als wordt beheerd door het apparaat
helper), tracering is ingeschakeld.:
KnooppuntContainer n;
...
helper.EnableAscii ("voorvoegsel", n);
Dit zou resulteren in het aanmaken van een aantal ascii-traceerbestanden, die elk volgen
de - - .tr-conventie. Het combineren van alle sporen in een
enkel bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden:
U kunt pcap-tracering inschakelen op basis van node-ID en apparaat-ID, maar ook met expliciet
Ptr. Elk Knooppunt in het systeem heeft een knooppunt-ID met een geheel getal en elk apparaat is verbonden met een knooppunt
heeft een geheel getal apparaat-ID.:
helper.EnableAscii ("voorvoegsel", 21, 1);
Natuurlijk kunnen de sporen worden gecombineerd tot een enkel bestand, zoals hierboven weergegeven.
Ten slotte kunt u pcap-tracing inschakelen voor alle apparaten in het systeem, met hetzelfde type als
die wordt beheerd door de apparaathelper.:
helper.EnableAsciiAll ("voorvoegsel");
Dit zou ertoe leiden dat er een aantal ascii-traceerbestanden wordt gemaakt, één voor elk apparaat in
het systeem van het type beheerd door de helper. Al deze bestanden volgen de
- - .tr-conventie. Combineer alle sporen tot één
bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden.
ascii Tracing Apparaat Helper Bestandsnaam Selectie
Impliciet in de prefix-achtige methodebeschrijvingen hierboven is de constructie van het complete
bestandsnamen door de implementatiemethode. Volgens afspraak worden ascii-sporen in de ns-3 systeem zijn
van het formulier - identiteitsbewijs>- id>.tr.
Zoals eerder vermeld, heeft elk knooppunt in het systeem een door het systeem toegewezen knooppunt-ID; En
elk apparaat heeft een interface-index (ook wel een apparaat-ID genoemd) ten opzichte van zijn knooppunt.
Standaard wordt dan een ascii-traceerbestand gemaakt als resultaat van het inschakelen van tracering op de eerste
apparaat van knooppunt 21, met behulp van het voorvoegsel "voorvoegsel", zou zijn voorvoegsel-21-1.tr.
Je kunt altijd de ns-3 objectnaamservice om dit duidelijker te maken. Als bijvoorbeeld
u gebruikt de objectnaamservice om de naam "server" toe te wijzen aan knooppunt 21, het resulterende
ascii trace-bestandsnaam wordt automatisch, voorvoegsel-server-1.tr en als je ook toewijst
de naam "eth0" naar het apparaat, de naam van uw ascii-traceerbestand zal dit automatisch oppikken
en gebeld worden voorvoegsel-server-eth0.tr.
Pkap Tracing Protocol helpers
Het doel hiervan mixen is om het gemakkelijk te maken om een consistente pcap-traceerfunctie toe te voegen
protocollen. We willen dat alle verschillende smaken van pcap-tracering overal hetzelfde werken
protocollen, dus de methoden van deze helpers worden geërfd door stackhelpers. Kijk eens naar
src/netwerk/helper/trace-helper.h als je de discussie wilt volgen terwijl je kijkt
echte codes.
In deze sectie zullen we de methoden illustreren zoals toegepast op het protocol Ipv4. naar
specificeer sporen in vergelijkbare protocollen, vervang gewoon het juiste type. Bijvoorbeeld,
gebruik een Ptr in plaats van een Ptr en bel PcapIpv6 inschakelen in plaats van PcapIpv4 inschakelen.
De klas PcapHelperForIpv4 biedt de functionaliteit op hoog niveau voor het gebruik van pcap-tracering
in de Ipv4 protocol. Elke protocolhelper die deze methoden mogelijk maakt, moet er een implementeren
virtuele methode geërfd van deze klasse. Er komt een aparte uitvoering voor
Ipv6, bijvoorbeeld, maar het enige verschil zit in de methodenamen en handtekeningen.
Er zijn verschillende methodenamen vereist om klasse ondubbelzinnig te maken Ipv4 vanaf Ipv6 die beide zijn
afgeleid van klasse Object, en methoden die dezelfde handtekening delen.:
virtuele leegte EnablePcapIpv4Internal (std::string prefix, Ptr ipv4, uint4_t-interface) = 32;
De signatuur van deze methode weerspiegelt de protocol- en interfacegerichte weergave van de
situatie op dit niveau. Alle openbare methoden geërfd van klasse PcapHelperForIpv4
reduceer tot het aanroepen van deze enkele apparaatafhankelijke implementatiemethode. Bijvoorbeeld de
pcap-methode op het laagste niveau:
void EnablePcapIpv4 (std::string prefix, Ptr ipv4, uint4_t-interface);
zal de implementatie van het apparaat noemen PcapIpv4Internal inschakelen direct. Al het andere publiek
pcap-traceermethoden bouwen voort op deze implementatie om extra gebruikersniveau te bieden
functionaliteit. Wat dit voor de gebruiker betekent, is dat alle protocolhelpers in het systeem dat zullen doen
alle pcap-traceermethoden beschikbaar hebben; en deze methoden zullen allemaal op dezelfde manier werken
weg over protocollen als de helper implementeert PcapIpv4Internal inschakelen correct.
Pkap Tracing Protocol Helper Methoden
Deze methoden zijn ontworpen om in een-op-een correspondentie te zijn met de Knooppunt- En
Netapparaat- centrische versies van de apparaatversies. In plaats van Knooppunt en Netapparaat paar
beperkingen, gebruiken we protocol- en interfacebeperkingen.
Merk op dat er, net als in de apparaatversie, zes methoden zijn:
void EnablePcapIpv4 (std::string prefix, Ptr ipv4, uint4_t-interface);
void EnablePcapIpv4 (std::string voorvoegsel, std::string ipv4Name, uint32_t interface);
ongeldig EnablePcapIpv4 (std::tekenreeksvoorvoegsel, Ipv4InterfaceContainer c);
ongeldig EnablePcapIpv4 (std::tekenreeksvoorvoegsel, NodeContainer n);
void EnablePcapIpv4 (std::stringvoorvoegsel, uint32_t knooppunt, uint32_t interface);
ongeldig EnablePcapIpv4All (std::tekenreeksvoorvoegsel);
U wordt aangemoedigd om de Doxygen voor de les door te nemen PcapHelperForIpv4 om de details te vinden
van deze methoden; maar om samen te vatten...
U kunt pcap-tracering inschakelen op een bepaald protocol/interfacepaar door een
Ptr en interface een Pcap inschakelen methode. Bijvoorbeeld,:
Ptr ipv4 = knooppunt->GetObject ();
...
helper.EnablePcapIpv4 ("prefix", ipv4, 0);
U kunt pcap-tracering inschakelen op een bepaald node/net-device-paar door een
std::tekenreeks die een objectnaam-servicetekenreeks vertegenwoordigt voor een Pcap inschakelen methode. De
Ptr wordt opgezocht vanuit de naamreeks. Bijvoorbeeld,:
Namen::Toevoegen ("serverIPv4" ...);
...
helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1);
U kunt pcap-tracering inschakelen voor een verzameling protocol/interface-paren door een
IPv4InterfaceContainer. Voor elk Ipv4 /interfacepaar in de container het protocoltype
is nagekeken. Voor elk protocol van het juiste type (hetzelfde type als wordt beheerd door het
apparaathulp), is tracering ingeschakeld voor de overeenkomstige interface. Bijvoorbeeld,:
NodeContainer-knooppunten;
...
NetDeviceContainer-apparaten = deviceHelper.Install (knooppunten);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer-interfaces = ipv4.Toewijzen (apparaten);
...
helper.EnablePcapIpv4 ("prefix", interfaces);
U kunt pcap-tracering inschakelen voor een verzameling protocol/interface-paren door een
KnooppuntContainer. Voor elk Knooppunt in de KnooppuntContainer het juiste protocol is gevonden. Voor
elk protocol, de interfaces worden opgesomd en tracering wordt ingeschakeld op het resulterende
paren. Bijvoorbeeld,:
KnooppuntContainer n;
...
helper.EnablePcapIpv4 ("voorvoegsel", n);
U kunt ook pcap-tracing inschakelen op basis van node-ID en interface. In dit geval,
de node-id wordt vertaald naar een Ptr en het juiste protocol wordt opgezocht in de
knooppunt. Het resulterende protocol en de interface worden gebruikt om de resulterende tracering te specificeren
bron.:
helper.EnablePcapIpv4 ("voorvoegsel", 21, 1);
Ten slotte kunt u pcap-tracering inschakelen voor alle interfaces in het systeem, met bijbehorende
protocol van hetzelfde type is als het protocol dat wordt beheerd door de apparaathelper.:
helper.EnablePcapIpv4All ("voorvoegsel");
Pkap Tracing Protocol Helper Bestandsnaam Selectie
Impliciet in alle bovenstaande methodebeschrijvingen is de constructie van het geheel
bestandsnamen door de implementatiemethode. Volgens afspraak worden pcap-traceringen genomen voor apparaten in
the ns-3 systeem zijn van de vorm - identiteitsbewijs>- id>.pcap. In het geval van
protocolsporen, is er een één-op-één-correspondentie tussen protocollen en Nodes. Dit is
want protocollair Objecten worden bij elkaar opgeteld Knooppunt Objecten. Omdat er geen wereldwijd protocol is
id in het systeem, gebruiken we de overeenkomstige node-id in bestandsnaamgeving. Daarom is er een
mogelijkheid voor bestandsnaambotsingen in automatisch gekozen traceerbestandsnamen. Voor deze
reden is de bestandsnaamconventie gewijzigd voor protocoltraceringen.
Zoals eerder vermeld, heeft elk knooppunt in het systeem een door het systeem toegewezen knooppunt-ID.
Omdat er een één-op-één-correspondentie is tussen protocolinstanties en knooppuntinstanties
we gebruiken het knooppunt-ID. Elke interface heeft een interface-ID ten opzichte van het bijbehorende protocol. We gebruiken
de conventie " -N -i .pcap" voor naamgeving van traceerbestanden
protocol helpers.
Daarom wordt standaard een pcap-traceerbestand gemaakt als gevolg van het inschakelen van tracering
interface 1 van het Ipv4-protocol van knooppunt 21 met behulp van het voorvoegsel "voorvoegsel" zou zijn
"prefix-n21-i1.pcap".
Je kunt altijd de ns-3 objectnaamservice om dit duidelijker te maken. Als bijvoorbeeld
u gebruikt de objectnaamservice om de naam "serverIpv4" toe te wijzen aan de Ptr op knooppunt
21, wordt de resulterende pcap-tracebestandsnaam automatisch,
"prefix-nserverIpv4-i1.pcap".
ascii Tracing Protocol helpers
Het gedrag van de ascii-traceerhelpers is grotendeels vergelijkbaar met het geval van pcap. Neem een
kijken naar src/netwerk/helper/trace-helper.h als je de discussie even wilt volgen
kijken naar echte code.
In deze sectie zullen we de methoden illustreren zoals toegepast op het protocol Ipv4. naar
specificeer sporen in vergelijkbare protocollen, vervang gewoon het juiste type. Bijvoorbeeld,
gebruik een Ptr in plaats van een Ptr en bel SchakelAsciiIpv6 in in plaats van
SchakelAsciiIpv4 in.
De klas AsciiTraceHelperForIpv4 voegt de functionaliteit op hoog niveau toe voor het gebruik van ascii
traceren naar een protocolhelper. Elk protocol dat deze methoden mogelijk maakt, moet een
enkele virtuele methode geërfd van deze klasse.:
virtuele leegte EnableAsciiIpv4Internal (Ptr stream, std::tekenreeksvoorvoegsel,
Ptr ipv4, uint4_t-interface) = 32;
De handtekening van deze methode weerspiegelt de protocol- en interfacegerichte weergave van de
situatie op dit niveau; en ook het feit dat de helper mogelijk schrijft naar een gedeeld
uitgangsstroom. Alle openbare methoden geërfd van klasse
PcapAndAsciiTraceHelperForIpv4 reduceer tot het noemen van dit enkele apparaat-afhankelijk
implementatie methode. Bijvoorbeeld de ascii-traceermethoden op het laagste niveau:
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint4_t-interface);
leegte EnableAsciiIpv4 (Ptr stroom, ptr ipv4, uint4_t-interface);
zal de implementatie van het apparaat noemen SchakelAsciiIpv4Internal in direct, het verstrekken van een van beide
het voorvoegsel of de stream. Alle andere openbare ascii-opsporingsmethoden zullen hierop voortbouwen
functies op laag niveau om extra functionaliteit op gebruikersniveau te bieden. Wat betekent dit voor de
gebruiker is dat alle apparaathelpers in het systeem alle ascii-traceermethoden zullen hebben
beschikbaar; en deze methoden werken allemaal op dezelfde manier in alle protocollen als de
protocollen uitvoeren Schakel AsciiIpv4Internal in correct.
ascii Tracing Apparaat Helper Methoden
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, uint4_t-interface);
leegte EnableAsciiIpv4 (Ptr stroom, ptr ipv4, uint4_t-interface);
void EnableAsciiIpv4 (std::string voorvoegsel, std::string ipv4Name, uint32_t interface);
leegte EnableAsciiIpv4 (Ptr stream, std::string ipv4Name, uint32_t interface);
ongeldig EnableAsciiIpv4 (std::tekenreeksvoorvoegsel, Ipv4InterfaceContainer c);
leegte EnableAsciiIpv4 (Ptr stroom, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (std::stringvoorvoegsel, NodeContainer n);
leegte EnableAsciiIpv4 (Ptr stroom, NodeContainer n);
void EnableAsciiIpv4 (std::stringvoorvoegsel, uint32_t knooppunt, uint32_t apparaatid);
leegte EnableAsciiIpv4 (Ptr stroom, uint32_t nodeid, uint32_t interface);
ongeldig EnableAsciiIpv4All (std::tekenreeksvoorvoegsel);
ongeldig EnableAsciiIpv4All (Ptr stroom);
U wordt aangemoedigd om de Doxygen voor de les door te nemen PcapAndAsciiHelperForIpv4 om de vondst
details van deze methoden; maar om samen te vatten...
Er zijn twee keer zoveel methoden beschikbaar voor ascii-tracering als voor pcap
traceren. Dit komt omdat, naast het pcap-achtige model, sporen van elk
uniek protocol/interface-paar worden weggeschreven naar een uniek bestand, wij ondersteunen een model waarin
traceerinformatie voor veel protocol/interface-paren wordt naar een gemeenschappelijk bestand geschreven. Dit
betekent dat de -N - het mechanisme voor het genereren van bestandsnamen is vervangen
door een mechanisme om naar een gemeenschappelijk bestand te verwijzen; en het aantal API-methoden wordt verdubbeld tot
laat alle combinaties toe.
Net als bij pcap-tracering kunt u ascii-tracering inschakelen voor een bepaald protocol/interface
paar door een Ptr en een interface een Ascii inschakelen methode. Bijvoorbeeld,:
Ptr ipv4;
...
helper.EnableAsciiIpv4 ("voorvoegsel", ipv4, 1);
In dit geval worden er geen traceercontexten naar het ascii-traceerbestand geschreven, aangezien dat wel het geval zou zijn
overbodig. Het systeem kiest de aan te maken bestandsnaam volgens dezelfde regels als
beschreven in de pcap-sectie, behalve dat het bestand het achtervoegsel ".tr" zal hebben in plaats van
".pcap".
Als u ascii-tracering op meer dan één interface wilt inschakelen en alle traceringen naar wilt laten sturen
een enkel bestand, kunt u dat ook doen door een object te gebruiken om naar een enkel bestand te verwijzen. Wij
heb al iets soortgelijks in het bovenstaande "cwnd"-voorbeeld:
Ptr protocol4 = knooppunt1->GetObject ();
Ptr protocol4 = knooppunt2->GetObject ();
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAsciiIpv4 (stroom, protocol1, 1);
helper.EnableAsciiIpv4 (stroom, protocol2, 1);
In dit geval worden traceringscontexten naar het ascii-traceerbestand geschreven, omdat ze vereist zijn
om sporen van de twee interfaces ondubbelzinnig te maken. Merk op dat aangezien de gebruiker volledig is
als u de bestandsnaam opgeeft, moet de tekenreeks de ".tr" bevatten voor consistentie.
U kunt ascii-tracering op een bepaald protocol inschakelen door een std::tekenreeks
die een objectnaam-servicetekenreeks vertegenwoordigt voor een Pcap inschakelen methode. De Ptr is
opgezocht uit de naamreeks. De in de resulterende bestandsnamen is impliciet sinds
er is een één-op-één-overeenkomst tussen protocolinstanties en knooppunten, bijvoorbeeld:
Namen::Toevoegen ("node1Ipv4" ...);
Namen::Toevoegen ("node2Ipv4" ...);
...
helper.EnableAsciiIpv4 ("voorvoegsel", "node1Ipv4", 1);
helper.EnableAsciiIpv4 ("voorvoegsel", "node2Ipv4", 1);
Dit zou resulteren in twee bestanden met de naam "prefix-nnode1Ipv4-i1.tr" en
"prefix-nnode2Ipv4-i1.tr" met traceringen voor elke interface in het betreffende traceerbestand.
Aangezien alle EnableAscii-functies overbelast zijn om een stream-wrapper te nemen, kan dat
gebruik dat formulier ook:
Namen::Toevoegen ("node1Ipv4" ...);
Namen::Toevoegen ("node2Ipv4" ...);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAsciiIpv4 (stroom, "node1Ipv4", 1);
helper.EnableAsciiIpv4 (stroom, "node2Ipv4", 1);
Dit zou resulteren in een enkel traceerbestand met de naam "trace-file-name.tr" dat alle
de traceergebeurtenissen voor beide interfaces. De gebeurtenissen zouden worden ondubbelzinnig gemaakt door de traceringscontext
snaren.
U kunt ascii-tracering inschakelen voor een verzameling protocol/interface-paren door een
IPv4InterfaceContainer. Voor elk protocol van het juiste type (hetzelfde type als wordt beheerd
door de apparaathelper), is tracering ingeschakeld voor de overeenkomstige interface. Nogmaals, de
is impliciet aangezien er een één-op-één correspondentie is tussen elk protocol en
zijn knooppunt. Bijvoorbeeld,:
NodeContainer-knooppunten;
...
NetDeviceContainer-apparaten = deviceHelper.Install (knooppunten);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer-interfaces = ipv4.Toewijzen (apparaten);
...
...
helper.EnableAsciiIpv4 ("voorvoegsel", interfaces);
Dit zou resulteren in het aanmaken van een aantal ascii-traceerbestanden, die elk volgen
de -N -i .tr-conventie. Het combineren van alle sporen in een
enkel bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden:
NodeContainer-knooppunten;
...
NetDeviceContainer-apparaten = deviceHelper.Install (knooppunten);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Ipv4InterfaceContainer-interfaces = ipv4.Toewijzen (apparaten);
...
Ptr stream = asciiTraceHelper.CreateFileStream ("trace-bestandsnaam.tr");
...
helper.EnableAsciiIpv4 (stroom, interfaces);
U kunt ascii-tracering inschakelen voor een verzameling protocol/interface-paren door een
KnooppuntContainer. Voor elk Knooppunt in de KnooppuntContainer het juiste protocol is gevonden. Voor
elk protocol, de interfaces worden opgesomd en tracering wordt ingeschakeld op het resulterende
paren. Bijvoorbeeld,:
KnooppuntContainer n;
...
helper.EnableAsciiIpv4 ("voorvoegsel", n);
Dit zou resulteren in het aanmaken van een aantal ascii-traceerbestanden, die elk volgen
de - - .tr-conventie. Het combineren van alle sporen in een
enkel bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden:
U kunt pcap-tracering ook inschakelen op basis van node-ID en apparaat-ID. In dit geval,
de node-id wordt vertaald naar een Ptr en het juiste protocol wordt opgezocht in de
knooppunt. Het resulterende protocol en de interface worden gebruikt om de resulterende tracering te specificeren
bron.:
helper.EnableAsciiIpv4 ("voorvoegsel", 21, 1);
Natuurlijk kunnen de sporen worden gecombineerd tot een enkel bestand, zoals hierboven weergegeven.
Ten slotte kunt u ascii-tracering inschakelen voor alle interfaces in het systeem, met bijbehorende
protocol van hetzelfde type is als het protocol dat wordt beheerd door de apparaathelper.:
helper.EnableAsciiIpv4All ("voorvoegsel");
Dit zou resulteren in het aanmaken van een aantal ascii-traceerbestanden, één voor elke interface
in het systeem gerelateerd aan een protocol van het type dat door de helper wordt beheerd. Al deze bestanden
zal volgen -N -i
in een enkel bestand wordt op dezelfde manier bereikt als de bovenstaande voorbeelden.
ascii Tracing Apparaat Helper Bestandsnaam Selectie
Impliciet in de prefix-achtige methodebeschrijvingen hierboven is de constructie van het complete
bestandsnamen door de implementatiemethode. Volgens afspraak worden ascii-sporen in de ns-3 systeem zijn
van het formulier " - - .tr."
Zoals eerder vermeld, heeft elk knooppunt in het systeem een door het systeem toegewezen knooppunt-ID.
Omdat er een één-op-één-overeenkomst is tussen protocollen en knooppunten, gebruiken we node-id
om de protocolidentiteit te identificeren. Elke interface op een bepaald protocol heeft een
interface-index (ook wel gewoon een interface genoemd) ten opzichte van het protocol. Standaard,
vervolgens een ascii-traceerbestand gemaakt als resultaat van het inschakelen van tracering op het eerste apparaat van
knooppunt 21, met het voorvoegsel "prefix", zou "prefix-n21-i1.tr" zijn. Gebruik het voorvoegsel om
ondubbelzinnig meerdere protocollen per knooppunt.
Je kunt altijd de ns-3 objectnaamservice om dit duidelijker te maken. Als bijvoorbeeld
u gebruikt de objectnaamservice om de naam "serverIpv4" toe te wijzen aan het protocol op het knooppunt
21, en geef ook interface één op, de resulterende ascii-traceerbestandsnaam zal automatisch
worden, "prefix-nserverIpv4-1.tr".
Tracing uitvoering gegevens
Data Collectie
Dit hoofdstuk beschrijft het ns-3 Data Collection Framework (DCF), dat hierin voorziet
mogelijkheden om gegevens te verkrijgen die zijn gegenereerd door modellen in de simulator, om online uit te voeren
reductie en gegevensverwerking, en om onbewerkte of getransformeerde gegevens om te zetten in verschillende output
formaten.
Het raamwerk ondersteunt momenteel stand-alone ns-3-runs die niet afhankelijk zijn van een externe
controle van de uitvoering van het programma. De door de DCF ter beschikking gestelde objecten mogen worden vastgehaakt ns-3 opsporen
bronnen om gegevensverwerking mogelijk te maken.
De broncode voor de klassen bevindt zich in de directory src/statistieken.
Dit hoofdstuk is als volgt opgebouwd. Eerst wordt een overzicht van de architectuur gegeven
gepresenteerd. Vervolgens worden de helpers voor deze klassen voorgesteld; deze eerste behandeling
zou basisgebruik van het raamwerk voor gegevensverzameling voor veel gebruikssituaties mogelijk moeten maken. Gebruikers die
output willen produceren buiten het bereik van de huidige helpers, of die willen creëren
hun eigen gegevensverzamelingsobjecten, zouden de rest van het hoofdstuk moeten lezen, dat gaat
in detail over alle standaard DCF-objecttypen en biedt codering op laag niveau
voorbeelden.
Design
De DCF bestaat uit drie basisklassen:
· Sonde is een mechanisme om de uitvoer van simulatiegegevens te instrumenteren en te regelen
gebruikt om interessante gebeurtenissen te volgen. Het produceert uitvoer in de vorm van een of meer ns-3
bronnen traceren. Probe-objecten zijn gekoppeld aan een of meer sporen wastafels (Zogenaamde
Verzamelaars), die monsters online verwerken en voorbereiden voor uitvoer.
· Verzamelaar verbruikt de gegevens die zijn gegenereerd door een of meer Probe-objecten. Het presteert
transformaties op de gegevens, zoals normalisatie, reductie en de berekening van
basisstatistieken. Collector-objecten produceren geen gegevens die rechtstreeks worden uitgevoerd door het
ns-3 rennen; in plaats daarvan voeren ze gegevens stroomafwaarts uit naar een ander type object, genaamd
Aggregator, die die functie vervult. Meestal voeren verzamelaars hun gegevens uit in
ook de vorm van sporenbronnen, waardoor collectoren in serie kunnen worden geketend.
· Aggregator is het eindpunt van de gegevens die zijn verzameld door een netwerk van Probes en Collectors.
De belangrijkste verantwoordelijkheid van de aggregator is het ordenen van gegevens en hun correspondentie
metadata, in verschillende uitvoerformaten zoals platte tekstbestanden, spreadsheetbestanden of
databases.
Alle drie deze klassen bieden de mogelijkheid om zichzelf dynamisch in of uit te schakelen
tijdens een simulatie.
Elke zelfstandige ns-3 simulatierun die de DCF gebruikt, zal er doorgaans minstens één maken
exemplaar van elk van de drie bovenstaande klassen.
[afbeelding] Overzicht gegevensverzamelingskader.UNINDENT
De totale stroom van gegevensverwerking wordt weergegeven in Data Collectie Kader overzicht.
Aan de linkerkant een rennende ns-3 simulatie is afgebeeld. Tijdens het runnen van de
simulatie worden gegevens beschikbaar gesteld door modellen via sporenbronnen of op andere manieren.
Het diagram laat zien dat sondes kunnen worden aangesloten op deze traceerbronnen om gegevens te ontvangen
asynchroon, of sondes kunnen gegevens opvragen. De gegevens worden vervolgens doorgegeven aan een verzamelobject
die de gegevens transformeert. Tenslotte kan een aggregator worden aangesloten op de uitgangen van de
collector, om plots, bestanden of databases te genereren.
[afbeelding] Data Collection Framework-aggregatie.UNINDENT
Een variatie op de bovenstaande figuur wordt gegeven in Data Collectie Kader aggregatie.
Deze tweede figuur illustreert dat de DCF-objecten op een bepaalde manier aan elkaar kunnen worden geketend
dat stroomafwaartse objecten invoer ontvangen van meerdere stroomopwaartse objecten. Het figuur
laat conceptueel zien dat meerdere sondes uitvoer kunnen genereren die in één sonde wordt ingevoerd
verzamelaar; een collector die een verhouding van twee tellers uitvoert, zou dat bijvoorbeeld doen
verkrijg typisch elke tellergegevens van afzonderlijke sondes. Meerdere verzamelaars kunnen ook
invoer in een enkele aggregator, die (zoals de naam al aangeeft) een aantal gegevens kan verzamelen
streams voor opname in een enkele plot, bestand of database.
Data Collectie helpers
De volledige flexibiliteit van het raamwerk voor gegevensverzameling wordt geboden door de onderlinge verbinding
van sondes, verzamelaars en aggregators. Het uitvoeren van al deze onderlinge verbindingen leidt tot
veel configuratie-instructies in gebruikersprogramma's. Voor gebruiksgemak, enkele van de meest voorkomende
bewerkingen kunnen worden gecombineerd en ingekapseld in helperfuncties. Daarnaast enkele
verklaringen met betrekking tot ns-3 traceerbronnen hebben geen Python-bindingen vanwege beperkingen in
de bindingen.
Data Collectie helpers Overzicht
In deze sectie geven we een overzicht van enkele helperklassen die zijn gemaakt voor
vergemakkelijk de configuratie van het raamwerk voor gegevensverzameling voor enkele veelvoorkomende gebruikssituaties. De
helpers stellen gebruikers in staat om gemeenschappelijke bewerkingen te vormen met slechts een paar instructies in hun C ++ of
Python-programma's. Maar dit gebruiksgemak gaat ten koste van aanzienlijk minder
flexibiliteit dan configuratie op laag niveau kan bieden, en de noodzaak om expliciet te coderen
ondersteuning voor nieuwe sondetypen in de helpers (om een hieronder beschreven probleem te omzeilen).
De nadruk bij de huidige helpers ligt op het verzamelen van gegevens ns-3 bronnen opsporen
gnuplot-plots of tekstbestanden, zonder een hoge mate van uitvoeraanpassing of statistiek
verwerking (aanvankelijk). Ook is het gebruik beperkt tot de beschikbare sondetypes
ns-3. Latere secties van deze documentatie gaan dieper in op het maken van nieuwe
Sondetypes, evenals details over het aan elkaar koppelen van sondes, verzamelaars en aggregators
in arrangementen op maat.
Tot op heden zijn er twee helpers voor gegevensverzameling geïmplementeerd:
· GnuplotHelper
· Bestandshelper
GnuplotHelper
De GnuplotHelper is een hulpklasse voor het produceren van uitvoerbestanden die worden gebruikt om gnuplots te maken. De
Het algemene doel is om gebruikers de mogelijkheid te bieden om snel grafieken te maken van geëxporteerde gegevens
in ns-3 bronnen traceren. Standaard wordt een minimale hoeveelheid gegevenstransformatie uitgevoerd;
het doel is om plots te genereren met zo min mogelijk (standaard) configuratie-instructies
mogelijk.
GnuplotHelper Overzicht
De GnuplotHelper zal aan het einde van de simulatie 3 verschillende bestanden aanmaken:
· Een door spaties gescheiden gnuplot-gegevensbestand
· Een gnuplot-controlebestand
· Een shellscript om de gnuplot te genereren
Er zijn twee configuratie-instructies die nodig zijn om plots te produceren. De eerste
instructie configureert de plot (bestandsnaam, titel, legenda's en uitvoertype, waarbij de uitvoer
typ standaard naar PNG indien niet gespecificeerd):
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::tekenreeks &titel,
const std::tekenreeks &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
De tweede verklaring haakt de traceerbron van belang:
void PlotProbe (const std::string &typeId,
const std::tekenreeks &pad,
const std::string &probeTraceSource,
const std::tekenreeks &titel);
De argumenten zijn als volgt:
· typeId: De ns-3 TypeId van de sonde
· pad: Het pad in de ns-3 configuratienaamruimte naar een of meer traceerbronnen
· probeTraceSource: Welke uitvoer van de sonde (zelf een traceerbron) moet worden geplot
· titel: de titel die aan de dataset(s) moet worden gekoppeld (in de gnuplot-legenda)
Een variant op de bovenstaande PlotProbe is het specificeren van een vijfde optioneel argument dat controleert
waar in de plot de sleutel (legenda) is geplaatst.
Een volledig uitgewerkt voorbeeld (van zevende.cc) wordt hieronder weergegeven:
// Maak de gnuplot-helper.
GnuplotHelper plotHelper;
// Configureer de plot.
// Configureer de plot. Het eerste argument is het voorvoegsel van de bestandsnaam
// voor de gegenereerde uitvoerbestanden. De tweede, derde en vierde
// argumenten zijn respectievelijk de plottitel, x-as en y-as labels
plotHelper.ConfigurePlot ("zevende-packet-byte-count",
"Pakketbytetelling vs. tijd",
"Tijd (seconden)",
"Aantal pakketbytes",
"png");
// Specificeer het sondetype, het traceerbronpad (in de configuratienaamruimte) en
// probe output trace source ("OutputBytes") om te plotten. Het vierde argument
// specificeert de naam van het gegevensreekslabel op de plot. De laatste
// argument maakt de plot op door op te geven waar de sleutel moet worden geplaatst.
plotHelper.PlotProbe (probeType,
tracePad,
"Uitvoerbytes",
"Aantal pakketbytes",
GnuplotAggregator::KEY_BELOW);
In dit voorbeeld is de probeType en tracePath zijn als volgt (voor IPv4):
probeType = "ns3::Ipv4PacketProbe";
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx";
Het probeType is een sleutelparameter om deze helper te laten werken. Dit TypeId moet worden geregistreerd
in het systeem en de handtekening op de trace-sink van de sonde moet overeenkomen met die van de trace
bron waaraan het is gekoppeld. Sondetypen zijn vooraf gedefinieerd voor een aantal gegevenstypen
overeenkomstig met ns-3 getraceerde waarden, en voor een paar andere traceerbronhandtekeningen zoals
de 'Tx' traceerbron van ns3::Ipv4L3-protocol klasse.
Houd er rekening mee dat het opgegeven traceerbronpad jokertekens kan bevatten. In dit geval meerdere
datasets worden geplot op één plot; één voor elk overeenkomend pad.
De geproduceerde hoofduitvoer bestaat uit drie bestanden:
zevende-packet-byte-count.dat
zevende-packet-byte-count.plt
zevende-packet-byte-count.sh
Op dit moment kunnen gebruikers het .plt-bestand met de hand bewerken voor verdere aanpassingen, of
voer het gewoon door gnuplot. Rennen sh zevende-packet-byte-count.sh voert gewoon de plot uit
via gnuplot, zoals hieronder weergegeven.
[afbeelding] 2-D Gnuplot Gemaakt door zevende.cc Voorbeeld..UNINDENT
Het is te zien dat de belangrijkste elementen (legenda, titel, plaatsing van legenda, xlabel, ylabel,
en pad voor de gegevens) worden allemaal op de plot geplaatst. Omdat er twee wedstrijden waren voor de
configuratiepad beschikbaar, worden de twee gegevensreeksen weergegeven:
· Packet Byte Count-0 komt overeen met /NodeList/0/$ns3::Ipv4L3Protocol/Tx
· Packet Byte Count-1 komt overeen met /NodeList/1/$ns3::Ipv4L3Protocol/Tx
GnuplotHelper ConfigureerPlot
De GnuplotHelper's ConfigureerPlot() functie kan worden gebruikt om plots te configureren.
Het heeft het volgende prototype:
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::tekenreeks &titel,
const std::tekenreeks &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
Het heeft de volgende argumenten:
┌───────────────────────────────┬─────── ────────── ─────────────────┐
│Argument │ Beschrijving │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│outputFileNameWithoutExtension │ Naam van gnuplot-gerelateerde bestanden naar │
│ │ schrijven zonder extensie. │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│titel │ Plottitelreeks om te gebruiken voor │
│ │ dit perceel. │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│xLegend │ De legenda voor de x horizontaal │
│ │ as. │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│yLegend │ De legenda voor de y-verticaal │
│ │ as. │
└───────────────────────────────┴─────── ────────── ─────────────────┘
│terminalType │ Instelreeks voor terminaltype voor │
│ │ uitvoer. De standaardterminal │
│ │ type is "png". │
└───────────────────────────────┴─────── ────────── ─────────────────┘
De GnuplotHelper's ConfigureerPlot() functie configureert hiervoor plotgerelateerde parameters
gnuplot-helper zodat het een door spaties gescheiden gnuplot-gegevensbestand met de naam
outputFileNameWithoutExtension + ".dat", een gnuplot-besturingsbestand met de naam
outputFileNameWithoutExtension + ".plt", en een shellscript om de gnuplot named
outputFileNameWithoutExtension + ".sh".
Een voorbeeld van het gebruik van deze functie is te zien in de zevende.cc hierboven beschreven code
waar het als volgt werd gebruikt:
plotHelper.ConfigurePlot ("zevende-packet-byte-count",
"Pakketbytetelling vs. tijd",
"Tijd (seconden)",
"Aantal pakketbytes",
"png");
GnuplotHelper Plot Probe
De GnuplotHelper's PlotProbe() functie kan worden gebruikt om waarden uit te zetten die zijn gegenereerd door sondes.
Het heeft het volgende prototype:
void PlotProbe (const std::string &typeId,
const std::tekenreeks &pad,
const std::string &probeTraceSource,
const std::tekenreeks &titel,
enum GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
Het heeft de volgende argumenten:
┌─────────────────┬───────────────────── ────────── ───┐
│Argument │ Beschrijving │
├─────────────────┼───────────────────── ────────── ───┤
│typeId │ De type-ID voor de sonde │
│ │ gemaakt door deze helper. │
├─────────────────┼───────────────────── ────────── ───┤
│pad │ Configuratiepad voor toegang tot de tracering │
│ │ bron. │
├─────────────────┼───────────────────── ────────── ───┤
│probeTraceSource │ De sondetraceerbron naar │
│ │ toegang. │
├─────────────────┼───────────────────── ────────── ───┤
│titel │ De titel die moet worden gekoppeld aan │
│ │ deze dataset │
├─────────────────┼───────────────────── ────────── ───┤
│keyLocation │ De locatie van de sleutel in de │
│ │ plot. De standaardlocatie is │
│ │ binnen. │
└─────────────────┴───────────────────── ────────── ───┘
De GnuplotHelper's PlotProbe() functie plot een dataset die is gegenereerd door de ns-3
traceer de bron met een sonde die door de helper is gemaakt en plot vervolgens de waarden van de
probeTraceSource. De dataset krijgt de opgegeven titel en bestaat uit de
'newValue' bij elke tijdstempel.
Als het configuratiepad meer dan één overeenkomst heeft in het systeem omdat er een jokerteken is, dan
voor elke match wordt één dataset geplot. De titels van de dataset krijgen het achtervoegsel
overeenkomende tekens voor elk van de jokertekens in het configuratiepad, gescheiden door spaties. Voor
als de titel van de voorgestelde dataset bijvoorbeeld de tekenreeks "bytes" is en er twee jokertekens zijn
in het pad, dan zijn datasettitels zoals "bytes-0 0" of "bytes-12 9" mogelijk als
labels voor de datasets die worden geplot.
Een voorbeeld van het gebruik van deze functie is te zien in de zevende.cc hierboven beschreven code
waar het als volgt werd gebruikt (met variabele vervanging):
plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Uitvoerbytes",
"Aantal pakketbytes",
GnuplotAggregator::KEY_BELOW);
Overige Voorbeelden
gnuplot Helper Voorbeeld
Een iets eenvoudiger voorbeeld dan de zevende.cc voorbeeld is te vinden in
src/stats/examples/gnuplot-helper-example.cc. De volgende 2D-gnuplot is gemaakt met behulp van
het voorbeeld.
[afbeelding] 2-D Gnuplot Gemaakt door gnuplot-helper-example.cc Voorbeeld..UNINDENT
In dit voorbeeld is er een Emitter-object dat zijn teller verhoogt volgens a
Poisson-proces en zendt vervolgens de waarde van de teller uit als een traceerbron.
Ptr zender = CreateObject ();
Namen::Toevoegen ("/Namen/Zender", zender);
Merk op dat omdat er geen jokertekens in het onderstaande pad zijn gebruikt, er slechts 1 datastroom was
getekend in het perceel. Deze enkele datastroom in de plot wordt simpelweg "Emitter Count" genoemd,
zonder extra achtervoegsels zoals men zou zien als er wildcards in het pad waren.
// Maak de gnuplot-helper.
GnuplotHelper plotHelper;
// Configureer de plot.
plotHelper.ConfigurePlot ("gnuplot-helper-voorbeeld",
"Emittertellingen vs. tijd",
"Tijd (seconden)",
"Aantal zenders",
"png");
// Zet de waarden uit die door de sonde zijn gegenereerd. Het pad dat we bieden
// helpt bij het ondubbelzinnig maken van de bron van het spoor.
plotHelper.PlotProbe ("ns3::Uinteger32Probe",
"/Namen/Zender/Teller",
"Uitvoer",
"Aantal zenders",
GnuplotAggregator::KEY_INSIDE);
Bestandshelper
De FileHelper is een hulpklasse die wordt gebruikt om gegevenswaarden in een bestand te plaatsen. Het algemene doel is
om gebruikers de mogelijkheid te bieden om snel opgemaakte tekstbestanden te maken van geëxporteerde gegevens
in ns-3 bronnen traceren. Standaard wordt een minimale hoeveelheid gegevenstransformatie uitgevoerd;
het doel is om bestanden te genereren met zo weinig (standaard) configuratie-instructies als
mogelijk.
Bestandshelper Overzicht
De FileHelper zal aan het einde van de simulatie 1 of meer tekstbestanden aanmaken.
De FileHelper kan 4 verschillende soorten tekstbestanden aanmaken:
· Geformatteerd
· Spatie gescheiden (standaard)
· Kommagescheiden
· Tab gescheiden
Geformatteerde bestanden gebruiken tekenreeksen in C-stijl en de functie sprintf() om hun bestanden af te drukken
waarden in het bestand dat wordt geschreven.
Het volgende tekstbestand met 2 kolommen met opgemaakte waarden met de naam
zevende-packet-byte-count-0.txt is gemaakt met behulp van meer nieuwe code die is toegevoegd aan de
origineel ns-3 De code van het zelfstudievoorbeeld. Alleen de eerste 10 regels van dit bestand worden getoond
hier kortheidshalve.
Tijd (seconden) = 1.000e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.004e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.004e+00 Aantal pakketbytes = 576
Tijd (seconden) = 1.009e+00 Aantal pakketbytes = 576
Tijd (seconden) = 1.009e+00 Aantal pakketbytes = 576
Tijd (seconden) = 1.015e+00 Aantal pakketbytes = 512
Tijd (seconden) = 1.017e+00 Aantal pakketbytes = 576
Tijd (seconden) = 1.017e+00 Aantal pakketbytes = 544
Tijd (seconden) = 1.025e+00 Aantal pakketbytes = 576
Tijd (seconden) = 1.025e+00 Aantal pakketbytes = 544
...
Het volgende verschillende tekstbestand met 2 kolommen met opgemaakte waarden met de naam
zevende-packet-byte-count-1.txt is ook gemaakt met dezelfde nieuwe code waaraan is toegevoegd
het origineel ns-3 De code van het zelfstudievoorbeeld. Alleen de eerste 10 regels van dit bestand worden getoond
hier kortheidshalve.
Tijd (seconden) = 1.002e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.007e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.013e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.020e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.028e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.036e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.045e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.053e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.061e+00 Aantal pakketbytes = 40
Tijd (seconden) = 1.069e+00 Aantal pakketbytes = 40
...
De nieuwe code die is toegevoegd om de twee tekstbestanden te produceren, staat hieronder. Meer details over
deze API wordt in een later gedeelte behandeld.
Merk op dat omdat er 2 overeenkomsten waren voor het jokerteken in het pad, 2 afzonderlijke tekstbestanden
werden gecreëerd. Het eerste tekstbestand, genaamd "seventh-packet-byte-count-0.txt",
komt overeen met de jokertekenovereenkomst waarbij de "*" is vervangen door "0". Het tweede tekstbestand,
met de naam "seventh-packet-byte-count-1.txt", komt overeen met de jokertekenovereenkomst met
de "*" vervangen door "1". Merk ook op dat de functie call to SchrijfProbe() zal een geven
foutmelding als er geen overeenkomsten zijn voor een pad dat jokertekens bevat.
// Maak de bestandshelper.
BestandsHelper bestandHelper;
// Configureer het te schrijven bestand.
fileHelper.ConfigureFile ("zevende-packet-byte-count",
FileAggregator::GEFORMATEERD);
// Stel de labels in voor dit geformatteerde uitvoerbestand.
fileHelper.Set2dFormat ("Tijd (seconden) = %.3e\tPacket Byte Count = %.0f");
// Schrijf de waarden die door de sonde zijn gegenereerd.
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Uitvoerbytes");
Bestandshelper ConfigureerBestand
De FileHelper's ConfigureerBestand() functie kan worden gebruikt om tekstbestanden te configureren.
Het heeft het volgende prototype:
ongeldig ConfigureFile (const std::string &outputFileNameWithoutExtension,
enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
Het heeft de volgende argumenten:
┌───────────────────────────────┬─────── ────────── ─────────────────┐
│Argument │ Beschrijving │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│outputFileNameWithoutExtension │ Naam van uitvoerbestand om te schrijven │
│ │ zonder verlenging. │
├───────────────────────────────┼─────── ────────── ─────────────────┤
│fileType │ Type bestand om te schrijven. De │
│ │ standaard type bestand is ruimte │
│ │ gescheiden. │
└───────────────────────────────┴─────── ────────── ─────────────────┘
De FileHelper's ConfigureerBestand() functie configureert tekstbestand gerelateerde parameters voor de
file helper zodat het een bestand met de naam outputFileNameWithoutExtension plus aanmaakt
mogelijke extra informatie van wildcard-overeenkomsten plus ".txt" met waarden afgedrukt als
gespecificeerd door bestandstype. Het standaard bestandstype is door spaties gescheiden.
Een voorbeeld van het gebruik van deze functie is te zien in de zevende.cc hierboven beschreven code
waar het als volgt werd gebruikt:
fileHelper.ConfigureFile ("zevende-packet-byte-count",
FileAggregator::GEFORMATEERD);
Bestandshelper Schrijf Probe
De FileHelper's SchrijfProbe() functie kan worden gebruikt om waarden gegenereerd door sondes naar te schrijven
tekst bestanden.
Het heeft het volgende prototype:
leegte WriteProbe (const std::string &typeId,
const std::tekenreeks &pad,
const std::string &probeTraceSource);
Het heeft de volgende argumenten:
┌─────────────────┬───────────────────── ────────── ───┐
│Argument │ Beschrijving │
├─────────────────┼───────────────────── ────────── ───┤
│typeId │ Het type-ID voor de sonde die moet zijn │
│ │ gemaakt. │
├─────────────────┼───────────────────── ────────── ───┤
│pad │ Configuratiepad voor toegang tot de tracering │
│ │ bron. │
├─────────────────┼───────────────────── ────────── ───┤
│probeTraceSource │ De sondetraceerbron naar │
│ │ toegang. │
└─────────────────┴───────────────────── ────────── ───┘
De FileHelper's SchrijfProbe() functie maakt uitvoertekstbestanden die zijn gegenereerd door de
ns-3 traceerbron met een sonde gemaakt door de helper, en schrijf vervolgens de waarden van de
probeTraceSource. De namen van de uitvoerbestanden hebben de tekst opgeslagen in de lidvariabele
m_outputFileNameWithoutExtension plus ".txt", en zal bestaan uit de 'newValue' bij elke
tijdstempel.
Als het configuratiepad meer dan één overeenkomst heeft in het systeem omdat er een jokerteken is, dan
er wordt één uitvoerbestand voor elke match gemaakt. De uitvoerbestandsnamen bevatten de
tekst in m_outputFileNameWithoutExtension plus de overeenkomende tekens voor elk van de
jokertekens in het configuratiepad, gescheiden door streepjes, plus ".txt". Als de waarde bijvoorbeeld
in m_outputFileNameWithoutExtension is de string "packet-byte-count", en er zijn twee
jokertekens in het pad en voer vervolgens bestandsnamen uit zoals "packet-byte-count-0-0.txt" of
"packet-byte-count-12-9.txt" zal mogelijk zijn als namen voor de bestanden die zullen worden aangemaakt.
Een voorbeeld van het gebruik van deze functie is te zien in de zevende.cc hierboven beschreven code
waar het als volgt werd gebruikt:
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
"Uitvoerbytes");
Overige Voorbeelden
Dien in Helper Voorbeeld
Een iets eenvoudiger voorbeeld dan de zevende.cc voorbeeld is te vinden in
src/stats/examples/file-helper-example.cc. Dit voorbeeld gebruikt alleen de FileHelper.
Het volgende tekstbestand met 2 kolommen met opgemaakte waarden met de naam bestand-helper-voorbeeld.txt
is gemaakt aan de hand van het voorbeeld. Alleen de eerste 10 regels van dit bestand worden hier getoond voor
beknoptheid.
Tijd (seconden) = 0.203 Telling = 1
Tijd (seconden) = 0.702 Telling = 2
Tijd (seconden) = 1.404 Telling = 3
Tijd (seconden) = 2.368 Telling = 4
Tijd (seconden) = 3.364 Telling = 5
Tijd (seconden) = 3.579 Telling = 6
Tijd (seconden) = 5.873 Telling = 7
Tijd (seconden) = 6.410 Telling = 8
Tijd (seconden) = 6.472 Telling = 9
...
In dit voorbeeld is er een Emitter-object dat zijn teller verhoogt volgens a
Poisson-proces en zendt vervolgens de waarde van de teller uit als een traceerbron.
Ptr zender = CreateObject ();
Namen::Toevoegen ("/Namen/Zender", zender);
Merk op dat er slechts 1 tekstbestand was omdat er geen jokertekens in het onderstaande pad staan
gemaakt. Dit enkele tekstbestand heet simpelweg "file-helper-example.txt", zonder extra's
achtervoegsels zoals u zou zien als er wildcards in het pad waren.
// Maak de bestandshelper.
BestandsHelper bestandHelper;
// Configureer het te schrijven bestand.
fileHelper.ConfigureFile ("file-helper-voorbeeld",
FileAggregator::GEFORMATEERD);
// Stel de labels in voor dit geformatteerde uitvoerbestand.
fileHelper.Set2dFormat ("Tijd (seconden) = %.3e\tCount = %.0f");
// Schrijf de waarden die door de sonde zijn gegenereerd. Het pad dat wij
// biedt hulp bij het ondubbelzinnig maken van de bron van het spoor.
fileHelper.WriteProbe ("ns3::Uinteger32Probe",
"/Namen/Zender/Teller",
"Uitvoer");
strekking en Beperkingen
Momenteel zijn alleen deze sondes geïmplementeerd en verbonden met de GnuplotHelper en
naar de FileHelper:
· Booleaanse sonde
· Dubbele sonde
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TijdProbe
· PakketProbe
· ApplicationPacketProbe
· IPv4PacketProbe
Deze sondes zijn daarom de enige TypeIds die beschikbaar zijn om in te gebruiken PlotProbe() en
SchrijfProbe().
In de volgende paragrafen behandelen we elk van de fundamentele objecttypen (Probe, Collector,
en Aggregator) in meer detail en laat zien hoe ze met elkaar kunnen worden verbonden
API op lager niveau.
Probes
In deze sectie worden de functionaliteiten beschreven die door de Probe-klasse worden geleverd aan een ns-3
simulatie, en geeft voorbeelden over hoe ze in een programma kunnen worden gecodeerd. Dit gedeelte is bedoeld voor
gebruikers die geïnteresseerd zijn in het ontwikkelen van simulaties met de ns-3 hulpmiddelen en het gebruik van de gegevens
Collection Framework, waarvan de klasse Probe deel uitmaakt, om gegevensuitvoer mee te genereren
de resultaten van hun simulatie.
Sonde Overzicht
Een Probe-object wordt verondersteld te zijn verbonden met een variabele uit de simulatie waarvan de waarden
tijdens het experiment relevant zijn voor de gebruiker. De sonde zal opnemen wat er was
waarden aangenomen door de variabele tijdens de simulatie en geef dergelijke gegevens door aan een andere
lid van het Data Collection Framework. Hoewel het buiten het bereik van deze sectie valt om
bespreken wat er gebeurt nadat de sonde zijn uitvoer heeft geproduceerd, is het voldoende om te zeggen dat, door
aan het einde van de simulatie heeft de gebruiker gedetailleerde informatie over welke waarden waren
opgeslagen in de variabele die tijdens de simulatie wordt onderzocht.
Meestal wordt een sonde aangesloten op een ns-3 spoor bron. Op deze manier, wanneer de
traceerbron exporteert een nieuwe waarde, de probe gebruikt de waarde (en exporteert deze stroomafwaarts
naar een ander object via zijn eigen traceerbron).
De sonde kan worden gezien als een soort filter op sporenbronnen. De belangrijkste redenen voor
mogelijk koppelen aan een sonde in plaats van rechtstreeks aan een traceerbron zijn als volgt:
· Probes kunnen dynamisch worden in- en uitgeschakeld tijdens de simulatie met oproepen naar Inschakelen()
en Uitzetten(). Het uitvoeren van gegevens kan bijvoorbeeld worden uitgeschakeld tijdens de
simulatie opwarmfase.
· Sondes kunnen bewerkingen op de gegevens uitvoeren om waarden uit meer gecompliceerde gegevens te extraheren
structuren; bijvoorbeeld het uitvoeren van de waarde van de pakketgrootte van een ontvangen ns3::pakket.
· Probes registreren een naam in de ns3::Config-naamruimte (met behulp van Namen::Toevoegen ()) zodat andere
objecten kunnen ernaar verwijzen.
· Probes bieden een statische methode waarmee men een Probe op naam kan manipuleren, zoals
wat wordt er gedaan in ns2measure [Cic06]
Stat::put ("my_metric", ID, voorbeeld);
Het ns-3-equivalent van de bovenstaande ns2measure-code is bijvoorbeeld
DoubleProbe::SetValueByPath ("/path/to/probe", voorbeeld);
Creatie
Merk op dat een Probe-basisklasseobject niet kan worden gemaakt omdat het een abstracte basis is
klasse, dwz het heeft pure virtuele functies die niet zijn geïmplementeerd. Een voorwerp van
type DoubleProbe, een subklasse van de klasse Probe, wordt hier gemaakt om te laten zien
wat gedaan moet worden.
Men declareert een DoubleProbe in dynamisch geheugen met behulp van de slimme pointerklasse (Ptr ). Naar
creëer een DoubleProbe in dynamisch geheugen met slimme pointers, je hoeft alleen maar de
ns-3 methode CreëerObject():
Ptr mijnsonde = CreateObject ();
De bovenstaande declaratie maakt DoubleProbes met de standaardwaarden voor de attributen.
Er zijn vier attributen in de klasse DoubleProbe; twee in het object van de basisklasse
DataCollectionObject, en twee in de Probe-basisklasse:
· "Naam" (DataCollectionObject), een StringValue
· "Ingeschakeld" (DataCollectionObject), een Booleaanse waarde
· "Start" (sonde), een tijdwaarde
· "Stop" (sonde), een tijdwaarde
Men kan dergelijke attributen instellen bij het maken van objecten door de volgende methode te gebruiken:
Ptr mijnsonde = CreateObjectWithAttributes (
"Naam", StringValue ("mijnprobe"),
"Ingeschakeld", Booleaanse Waarde (false),
"Start", tijdwaarde (seconden (100.0)),
"Stop", tijdwaarde (seconden (1000.0)));
Start en Stop zijn tijdvariabelen die het actie-interval van de sonde bepalen. De
De sonde voert alleen gegevens uit als de huidige tijd van de simulatie daarbinnen ligt
interval. De speciale tijdswaarde van 0 seconden voor Stop zal dit attribuut uitschakelen (bijv
houd de sonde aan gedurende de hele simulatie). Ingeschakeld is een vlag die de sonde inschakelt of
uit, en moet zijn ingesteld op waar om de sonde gegevens te laten exporteren. De naam is de naam van het object
in het DCF-kader.
Importeren en exporteren gegevens
ns-3 traceerbronnen zijn sterk getypeerd, dus de mechanismen voor het koppelen van sondes aan een spoor
bron en voor het exporteren van gegevens behoren tot zijn subklassen. Bijvoorbeeld de standaard
distributie van ns-3 biedt een klasse DoubleProbe die is ontworpen om aan een trace te koppelen
bron die een dubbele waarde exporteert. We zullen vervolgens de werking van de DoubleProbe gedetailleerd beschrijven, en
bespreek vervolgens hoe andere Probe-klassen door de gebruiker kunnen worden gedefinieerd.
Dubbele sonde Overzicht
De DoubleProbe maakt verbinding met een dubbele waarde ns-3 traceerbron en exporteert zelf een
verschillende dubbele waarde ns-3 spoor bron.
De volgende code, ontleend aan src/stats/examples/double-probe-example.cc, toont de basis
bewerkingen van het loodsen van de DoubleProbe in een simulatie, waar het een teller onderzoekt
geëxporteerd door een emitterobject (klasse Emitter).
Ptr zender = CreateObject ();
Namen::Toevoegen ("/Namen/Zender", zender);
...
Ptr probe1 = CreateObject ();
// Sluit de sonde aan op de teller van de zender
bool connected = probe1->ConnectByObject ("Teller", zender);
De volgende code onderzoekt dezelfde teller die is geëxporteerd door hetzelfde emitterobject. Dit
DoubleProbe gebruikt echter een pad in de configuratienaamruimte om het
verbinding. Merk op dat de zender zichzelf daarna registreerde in de configuratienaamruimte
het was gemaakt; anders zou ConnectByPath niet werken.
Ptr probe2 = CreateObject ();
// Let op, hier wordt geen retourwaarde gecontroleerd
probe2->ConnectByPath ("/Namen/Emitter/Teller");
De volgende weergegeven DoubleProbe die hieronder wordt weergegeven, heeft een waarde die wordt ingesteld met behulp van het pad naar binnen
de configuratienaamruimte. Merk op dat deze keer de DoubleProbe zichzelf registreerde in de
configuratienaamruimte nadat deze is gemaakt.
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// We moeten het toevoegen aan de configuratiedatabase
Namen::Toevoegen ("/Namen/Probes", probe3->GetName (), probe3);
De functie Count() van de zender kan nu de waarde voor deze DoubleProbe instellen als
volgt:
komen te vervallen
Emitter::Count (ongeldig)
{
...
m_teller += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
...
}
Het bovenstaande voorbeeld laat zien hoe de code die de Probe aanroept geen expliciete code hoeft te hebben
verwijzing naar de Probe, maar kan de waarde-instelling sturen via de Config-naamruimte.
Dit is qua functionaliteit vergelijkbaar met de Stat::Zet methode geïntroduceerd door ns2measure paper
[Cic06], en stelt gebruikers in staat om tijdelijk Probe-statements zoals printf verklaringen
binnen bestaande ns-3 modellen. Let op om de DoubleProbe hierin te kunnen gebruiken
voorbeeld als dit, 2 dingen waren nodig:
1. het headerbestand van de stats-module is opgenomen in het voorbeeld .cc-bestand
2. het voorbeeld is afhankelijk gemaakt van de stats-module in zijn wscript-bestand.
Analoge dingen moeten worden gedaan om andere sondes op andere plaatsen in het systeem toe te voegen ns-3
codebasis.
De waarden voor de DoubleProbe kunnen ook worden ingesteld met de functie DoubleProbe::SetValue(),
terwijl de waarden voor de DoubleProbe kunnen worden verkregen met behulp van de functie
DoubleProbe::GetValue().
De DoubleProbe exporteert dubbele waarden in zijn traceerbron "Uitvoer"; een stroomafwaarts object
kan hier als volgt een trace-sink (NotifyViaProbe) aan koppelen:
connected = probe1->TraceConnect ("Uitvoer", probe1->GetName (), MakeCallback (&NotifyViaProbe));
Overige probes
Naast de DoubleProbe zijn de volgende Probes ook verkrijgbaar:
· Uinteger8Probe maakt verbinding met een ns-3 traceerbron exporteert een uint8_t.
· Uinteger16Probe maakt verbinding met een ns-3 traceerbron exporteert een uint16_t.
· Uinteger32Probe maakt verbinding met een ns-3 traceerbron exporteert een uint32_t.
· PacketProbe maakt verbinding met een ns-3 traceerbron die een pakket exporteert.
· ApplicationPacketProbe maakt verbinding met een ns-3 traceerbron die een pakket en een socket exporteert
adres.
· Ipv4PacketProbe maakt verbinding met een ns-3 traceerbron die een pakket, een IPv4-object en
een interface.
Wij creëren nieuwe Sonde types
Om een nieuw sondetype te maken, moet u de volgende stappen uitvoeren:
· Zorg ervoor dat uw nieuwe Probe-klasse is afgeleid van de Probe-basisklasse.
· Zorg ervoor dat de puur virtuele functies die uw nieuwe Probe-klasse overneemt van de
Probe-basisklasse is geïmplementeerd.
· Zoek een bestaande Probe-klasse die een traceerbron gebruikt die qua type het dichtst bij de
type traceerbron die uw sonde gaat gebruiken.
· Kopieer het headerbestand (.h) en het implementatiebestand (.cc) van die bestaande Probe-klasse naar twee
nieuwe bestanden met namen die overeenkomen met uw nieuwe sonde.
· Vervang de typen, argumenten en variabelen in de gekopieerde bestanden door de juiste
typ voor uw sonde.
· Breng de nodige wijzigingen aan om de code te laten compileren en ervoor te zorgen dat deze zich gedraagt zoals u zou doen
wilt.
Voorbeelden
Twee voorbeelden zullen hier in detail worden besproken:
· Voorbeeld van dubbele sonde
· Voorbeeld van IPv4-pakketplot
Dubbel Sonde Voorbeeld
Het voorbeeld van de dubbele sonde is eerder besproken. Het voorbeeldprogramma is te vinden
in src/stats/examples/double-probe-example.cc. Om samen te vatten wat er in dit programma gebeurt,
er is een emitter die een teller exporteert die oploopt volgens een Poisson-proces.
Er worden met name twee manieren getoond om gegevens uit te zenden:
1. via een getraceerde variabele gekoppeld aan één sonde:
Getraceerde waarde m_teller; // normaal zou dit een geheel getal zijn
2. via een teller waarvan de waarde wordt gepost naar een tweede sonde, waarnaar wordt verwezen met zijn naam in
het Config-systeem:
komen te vervallen
Emitter::Count (ongeldig)
{
NS_LOG_FUNCTION (deze);
NS_LOG_DEBUG ("Tellen bij " << Simulator::Now ().GetSeconds ());
m_teller += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (Seconden (m_var->GetValue ()), &Emitter::Count, dit);
}
Laten we de Probe eens wat nauwkeuriger bekijken. Sondes kunnen hun waarden in een veelvoud ontvangen
manieren:
1. doordat de sonde rechtstreeks toegang krijgt tot de traceerbron en er een traceer-sink op aansluit
2. door de sonde toegang te krijgen tot de traceerbron via de configuratienaamruimte en verbinding te maken met een
spoor er naar toe
3. door de aanroepende code expliciet de Probe's aan te roepen Waarde instellen() methode
4. door de aanroepcode expliciet aan te roepen SetValueByPath
("/path/through/Config/naamruimte", ...)
De eerste twee technieken zullen naar verwachting de meest voorkomende zijn. Ook in het voorbeeld, de
hooking van een normale callback-functie wordt weergegeven, zoals meestal wordt gedaan in ns-3. Deze
callback-functie is niet gekoppeld aan een Probe-object. We noemen dit geval 0) hieronder.
// Dit is een functie om het koppelen van een onbewerkte functie aan de traceerbron te testen
komen te vervallen
NotifyViaTraceSource (std::string context, dubbele oldVal, dubbele newVal)
{
NS_LOG_DEBUG ("context:" << context << "oud" << oldVal << "nieuw" << newVal);
}
Eerst moet de zender worden ingesteld:
Ptr zender = CreateObject ();
Namen::Toevoegen ("/Namen/Zender", zender);
// Het Emitter-object is niet gekoppeld aan een ns-3-knooppunt, dus
// het wordt niet automatisch gestart, dus we moeten dit zelf doen
Simulator::Schedule (seconden (0.0), &Emitter::Start, zender);
De verschillende DoubleProbes werken samen met de zender in het voorbeeld zoals hieronder getoond.
Geval 0):
// Het onderstaande toont typische functionaliteit zonder een sonde
// (verbind een sink-functie met een traceerbron)
//
connected = emitter->TraceConnect ("Teller", "voorbeeldcontext", MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (verbonden, "Traceerbron niet verbonden");
zaak 1):
//
// Probe1 wordt rechtstreeks gekoppeld aan het Emitter-traceerbronobject
//
// probe1 wordt gekoppeld aan de Emitter-traceerbron
Ptr probe1 = CreateObject ();
// de naam van de sonde kan dienen als context in de tracering
probe1->SetName ("ObjectProbe");
// Sluit de sonde aan op de teller van de zender
connected = probe1->ConnectByObject ("Teller", zender);
NS_ASSERT_MSG (verbonden, "Traceerbron niet verbonden met probe1");
zaak 2):
//
// Probe2 wordt gekoppeld aan het Emitter-traceerbronobject door
// toegang krijgen via padnaam in de Config-database
//
// Maak nog een vergelijkbare sonde; dit zal aansluiten via een Config-pad
Ptr probe2 = CreateObject ();
probe2->SetName ("PathProbe");
// Let op, hier wordt geen retourwaarde gecontroleerd
probe2->ConnectByPath ("/Namen/Emitter/Teller");
geval 4) (geval 3 wordt in dit voorbeeld niet getoond):
//
// Probe3 wordt door de zender rechtstreeks via de
// statische methode SetValueByPath().
//
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// We moeten het toevoegen aan de configuratiedatabase
Namen::Toevoegen ("/Namen/Probes", probe3->GetName (), probe3);
En tot slot laat het voorbeeld zien hoe de sondes kunnen worden gekoppeld om uitvoer te genereren:
// De sonde zelf zou uitvoer moeten genereren. De context die we bieden
// naar deze sonde (in dit geval de naam van de sonde) zal helpen om ondubbelzinnig te maken
// de bron van het spoor
connected = probe3->TraceConnect ("Uitvoer",
"/Namen/Probes/StaticallyAccessedProbe/Uitvoer",
MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (verbonden, "Traceerbron niet .. verbonden met probe3-uitgang");
De volgende callback is in dit voorbeeld gekoppeld aan de Probe voor illustratieve doeleinden;
normaal gesproken zou de Probe aan een Collector-object zijn gekoppeld.
// Dit is een functie om te testen of deze aan de sonde-uitgang is gekoppeld
komen te vervallen
NotifyViaProbe (std::string context, dubbele oldVal, dubbele newVal)
{
NS_LOG_DEBUG ("context:" << context << "oud" << oldVal << "nieuw" << newVal);
}
IPv4 Pakket Plot Voorbeeld
Het IPv4-pakketplotvoorbeeld is gebaseerd op het vijfde.cc-voorbeeld uit de ns-3 Zelfstudie. Het
is te vinden in src/stats/examples/ipv4-packet-plot-example.cc.
knooppunt 0 knooppunt 1
+++++ +++
| ns-3TCP | | ns-3TCP |
+++++ +++
| 10.1.1.1 | | 10.1.1.2 |
+++++ +++
| punt naar punt | | punt naar punt |
+++++ +++
| |
+-------+
We kijken alleen naar de sonde, omdat deze illustreert dat sondes ook waarden uit kunnen pakken
structuren (in dit geval pakketten) en rapporteer die waarden eerder als uitvoer van traceerbronnen
dan alleen hetzelfde type gegevens door te geven.
Er zijn andere aspecten van dit voorbeeld die later in de documentatie worden uitgelegd.
De twee typen gegevens die worden geëxporteerd, zijn het pakket zelf (uitgang) en een telling van de
aantal bytes in het pakket (uitvoerbytes).
TypeId
Ipv4PacketProbe::GetTypeId ()
{
statisch TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
.SetOuder ()
.Constructor toevoegen ()
.AddTraceSource ("Uitvoer",
"Het pakket plus zijn IPv4-object en interface die dienen als uitvoer voor deze sonde",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource ("UitvoerBytes",
"Het aantal bytes in het pakket",
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
tijd teruggeven;
}
Wanneer de trace-sink van de sonde een pakket ontvangt en de sonde is ingeschakeld, wordt het uitgevoerd
het pakje erop uitgang trace-bron, maar het zal ook het aantal bytes op het
uitvoerbytes spoor bron.
komen te vervallen
Ipv4PacketProbe::TraceSink (Ptr pakket, Ptr ipv4, uint4_t-interface)
{
NS_LOG_FUNCTION (dit << pakket << ipv4 << interface);
als (IsIngeschakeld ())
{
m_pakket = pakket;
m_ipv4 = ipv4;
m_interface = interface;
m_output (pakket, ipv4, interface);
uint32_t packetSizeNew = pakket->GetSize ();
m_outputBytes (m_packetSizeOld, packetSizeNew);
m_packetSizeOld = pakketSizeNew;
}
}
Referenties
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, "Een geïntegreerd raamwerk voor
Effectieve gegevensverzameling en statistische analyse mogelijk maken met ns2, Workshop op
ns-2 (WNS2), Pisa, Italië, oktober 2006.
Verzamelaars
Deze sectie is een tijdelijke aanduiding om de functionaliteiten van de Collector te beschrijven
klas naar een ns-3 simulatie, en geeft voorbeelden over hoe ze in een programma kunnen worden gecodeerd.
Opmerking: Vanaf ns-3.18 zijn Collectors nog in ontwikkeling en nog niet als onderdeel geleverd
van het kader.
aggregators
In deze sectie worden de functionaliteiten beschreven die door de klasse Aggregator worden geleverd aan een ns-3
simulatie. Deze sectie is bedoeld voor gebruikers die geïnteresseerd zijn in het ontwikkelen van simulaties met de
ns-3 tools en het gebruik van het Data Collection Framework, waarvan de klasse Aggregator een
deel, om gegevensuitvoer te genereren met de resultaten van hun simulatie.
Aggregator Overzicht
Een Aggregator-object wordt verondersteld te worden gekoppeld aan een of meer traceerbronnen om dit te kunnen doen
invoer ontvangen. Aggregators zijn het eindpunt van de gegevens die door het netwerk van worden verzameld
Probes en Collectors tijdens de simulatie. Het is de taak van de aggregator om deze te nemen
waarden en zet ze om in hun uiteindelijke uitvoerformaat, zoals platte tekstbestanden,
spreadsheetbestanden, plots of databases.
Meestal is een aggregator verbonden met een of meer Collectors. Op deze manier, wanneer dan ook
de traceerbronnen van de Collectors exporteren nieuwe waarden, de aggregator kan de waarde zo verwerken
dat het kan worden gebruikt in het uiteindelijke uitvoerformaat waar de gegevenswaarden zich zullen bevinden na de
simulatie.
Let op het volgende over aggregators:
· Aggregators kunnen dynamisch worden in- en uitgeschakeld tijdens de simulatie met oproepen naar
Inschakelen() en Uitzetten(). Het samenvoegen van gegevens kan bijvoorbeeld worden uitgeschakeld tijdens
de simulatie-opwarmfase, wat betekent dat die waarden niet worden opgenomen in de finale
uitgangsmedium.
· Aggregators ontvangen gegevens van Collectors via callbacks. Wanneer een Collector is gekoppeld
naar een aggregator wordt TraceConnect aangeroepen om de tracering van de aggregator tot stand te brengen
sink-methode als callback.
Tot op heden zijn er twee aggregators geïmplementeerd:
· GnuplotAggregator
· BestandAggregator
GnuplotAggregator
De GnuplotAggregator produceert uitvoerbestanden die worden gebruikt om gnuplots te maken.
De GnuplotAggregator zal aan het einde van de simulatie 3 verschillende bestanden aanmaken:
· Een door spaties gescheiden gnuplot-gegevensbestand
· Een gnuplot-controlebestand
· Een shellscript om de gnuplot te genereren
Creatie
Hier wordt een object van het type GnuplotAggregator gemaakt om te laten zien wat er moet gebeuren.
Men declareert een GnuplotAggregator in dynamisch geheugen door gebruik te maken van de klasse smart pointer
(Ptr ). Om een GnuplotAggregator in dynamisch geheugen met slimme aanwijzers te maken, is er maar één
moet bellen ns-3 methode CreëerObject(). De volgende code van
src/stats/examples/gnuplot-aggregator-example.cc laat zien hoe je dit doet:
string fileNameWithoutExtension = "gnuplot-aggregator";
// Maak een aggregator.
Ptr aggregator =
CreateObject (bestandsnaamZonderExtensie);
Het eerste argument voor de constructor, fileNameWithoutExtension, is de naam van het
gnuplot gerelateerde bestanden te schrijven zonder extensie. Deze GnuplotAggregator zal een
door spaties gescheiden gnuplot-gegevensbestand met de naam "gnuplot-aggregator.dat", een gnuplot-besturingsbestand
genaamd "gnuplot-aggregator.plt", en een shellscript om de gnuplot genaamd + te genereren
"gnuplot-aggregator.sh".
De gnuplot die wordt gemaakt, kan zijn sleutel op 4 verschillende locaties hebben:
· Geen sleutel
· Sleutel binnen de plot (de standaard)
· Toets boven de plot
· Toets onder de plot
De volgende opsommingswaarden voor de sleutellocatie van gnuplot zijn toegestaan om de positie van de sleutel te specificeren:
enum sleutellocatie {
GEEN SLEUTEL,
KEY_INSIDE,
KEY_BOVEN,
SLEUTEL_BELOW
};
Als het gewenst was om de onderstaande sleutel te hebben in plaats van de standaardpositie van binnen, dan
je zou het volgende kunnen doen.
aggregator->SetKeyLocation(GnuplotAggregator::KEY_BELOW);
Voorbeelden
Een voorbeeld zal hier in detail worden besproken:
· Gnuplot Aggregator-voorbeeld
gnuplot Aggregator Voorbeeld
Een voorbeeld dat de GnuplotAggregator uitoefent, is te vinden in
src/stats/examples/gnuplot-aggregator-example.cc.
De volgende 2D-gnuplot is gemaakt aan de hand van het voorbeeld.
[afbeelding] 2-D Gnuplot Gemaakt door gnuplot-aggregator-example.cc Voorbeeld..UNINDENT
Deze code uit het voorbeeld laat zien hoe de GnuplotAggregator te bouwen zoals besproken
bovenstaand.
leegte Create2dPlot ()
{
namespace std; gebruiken;
string fileNameWithoutExtension = "gnuplot-aggregator";
string plotTitle = "Gnuplot Aggregator-plot";
string plotXAxisHeading = "Tijd (seconden)";
tekenreeks plotYAxisHeading = "Dubbele waarden";
string plotDatasetLabel = "Gegevenswaarden";
string datasetContext = "Dataset/Context/String";
// Maak een aggregator.
Ptr aggregator =
CreateObject (bestandsnaamZonderExtensie);
Er zijn verschillende GnuplotAggregator-attributen ingesteld, inclusief de 2D-dataset die zal worden
geplot.
// Stel de eigenschappen van de aggregator in.
aggregator->SetTerminal ("png");
aggregator->SetTitle (plotTitle);
aggregator->SetLegend (plotXAxisHeading, plotYAxisHeading);
// Voeg een dataset toe aan de aggregator.
aggregator->Add2dDataset (datasetContext, plotDatasetLabel);
// aggregator moet zijn ingeschakeld
aggregator->Inschakelen ();
Vervolgens worden de 2D-waarden berekend en wordt elke waarde afzonderlijk naar de
GnuplotAggregator met behulp van de Schrijf2d() functie.
dubbele tijd;
dubbele waarde;
// Maak de 2D-gegevensset.
voor (tijd = -5.0; tijd <= +5.0; tijd += 1.0)
{
// Bereken de 2D-kromme
//
// 2
// waarde = tijd .
//
waarde = tijd * tijd;
// Voeg dit punt toe aan de plot.
aggregator->Write2d (datasetContext, tijd, waarde);
}
// Schakel het loggen van gegevens voor de aggregator uit.
aggregator->Uitschakelen ();
}
BestandAggregator
De FileAggregator stuurt de ontvangen waarden naar een bestand.
De FileAggregator kan 4 verschillende soorten bestanden aanmaken:
· Geformatteerd
· Spatie gescheiden (standaard)
· Kommagescheiden
· Tab gescheiden
Geformatteerde bestanden gebruiken tekenreeksen in C-stijl en de functie sprintf() om hun bestanden af te drukken
waarden in het bestand dat wordt geschreven.
Creatie
Hier wordt een object van het type FileAggregator gemaakt om te laten zien wat er moet gebeuren.
Men declareert een FileAggregator in dynamisch geheugen met behulp van de slimme pointerklasse (Ptr ).
Om een FileAggregator in dynamisch geheugen met slimme pointers te maken, hoef je alleen maar te bellen
the ns-3 methode CreateObject. De volgende code van
src/stats/examples/file-aggregator-example.cc laat zien hoe je dit doet:
tekenreeks bestandsnaam = "file-aggregator-formatted-values.txt";
// Maak een aggregator met geformatteerde waarden.
Ptr aggregator =
CreateObject (bestandsnaam, bestandsaggregator::FORMATTED);
Het eerste argument voor de constructor, bestandsnaam, is de naam van het te schrijven bestand; de
tweede argument, bestandstype, is het type bestand dat moet worden geschreven. Deze FileAggregator maakt een
bestand met de naam "file-aggregator-formatted-values.txt" met de waarden afgedrukt zoals gespecificeerd door
fileType, dwz in dit geval geformatteerd.
De volgende opsommingswaarden van het bestandstype zijn toegestaan:
opsomming bestandstype {
GEFORMATTEERD,
SPACE_SEPARATED,
KOMMAGESCHEIDEN,
TAB_SEPARATED
};
Voorbeelden
Een voorbeeld zal hier in detail worden besproken:
· Voorbeeld van bestandsaggregator
Dien in Aggregator Voorbeeld
Een voorbeeld dat de FileAggregator oefent, is te vinden in
src/stats/examples/file-aggregator-example.cc.
Het volgende tekstbestand met 2 kolommen met waarden gescheiden door komma's is gemaakt met behulp van de
voorbeeld.
-5,25
-4,16
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9
4,16
5,25
Deze code uit het voorbeeld laat zien hoe je de FileAggregator opbouwt zoals besproken
bovenstaand.
void CreateCommaSeparatedFile ()
{
namespace std; gebruiken;
string fileName = "file-aggregator-comma-separated.txt";
string datasetContext = "Dataset/Context/String";
// Maak een aggregator.
Ptr aggregator =
CreateObject (bestandsnaam, bestandsaggregator::COMMA_SEPARATED);
FileAggregator-kenmerken zijn ingesteld.
// aggregator moet zijn ingeschakeld
aggregator->Inschakelen ();
Vervolgens worden de 2D-waarden berekend en wordt elke waarde afzonderlijk naar de
FileAggregator met behulp van de Schrijf2d() functie.
dubbele tijd;
dubbele waarde;
// Maak de 2D-gegevensset.
voor (tijd = -5.0; tijd <= +5.0; tijd += 1.0)
{
// Bereken de 2D-kromme
//
// 2
// waarde = tijd .
//
waarde = tijd * tijd;
// Voeg dit punt toe aan de plot.
aggregator->Write2d (datasetContext, tijd, waarde);
}
// Schakel het loggen van gegevens voor de aggregator uit.
aggregator->Uitschakelen ();
}
Het volgende tekstbestand met 2 kolommen met opgemaakte waarden is ook gemaakt met behulp van de
voorbeeld.
Tijd = -5.000e+00 Waarde = 25
Tijd = -4.000e+00 Waarde = 16
Tijd = -3.000e+00 Waarde = 9
Tijd = -2.000e+00 Waarde = 4
Tijd = -1.000e+00 Waarde = 1
Tijd = 0.000e+00 Waarde = 0
Tijd = 1.000e+00 Waarde = 1
Tijd = 2.000e+00 Waarde = 4
Tijd = 3.000e+00 Waarde = 9
Tijd = 4.000e+00 Waarde = 16
Tijd = 5.000e+00 Waarde = 25
Deze code uit het voorbeeld laat zien hoe je de FileAggregator opbouwt zoals besproken
bovenstaand.
leegte CreateFormattedFile ()
{
namespace std; gebruiken;
tekenreeks bestandsnaam = "file-aggregator-formatted-values.txt";
string datasetContext = "Dataset/Context/String";
// Maak een aggregator met geformatteerde waarden.
Ptr aggregator =
CreateObject (bestandsnaam, bestandsaggregator::FORMATTED);
FileAggregator-attributen zijn ingesteld, inclusief de C-stijl formaattekenreeks die moet worden gebruikt.
// Stel het formaat voor de waarden in.
aggregator->Set2dFormat ("Tijd = %.3e\tWaarde = %.0f");
// aggregator moet zijn ingeschakeld
aggregator->Inschakelen ();
Vervolgens worden de 2D-waarden berekend en wordt elke waarde afzonderlijk naar de
FileAggregator met behulp van de Schrijf2d() functie.
dubbele tijd;
dubbele waarde;
// Maak de 2D-gegevensset.
voor (tijd = -5.0; tijd <= +5.0; tijd += 1.0)
{
// Bereken de 2D-kromme
//
// 2
// waarde = tijd .
//
waarde = tijd * tijd;
// Voeg dit punt toe aan de plot.
aggregator->Write2d (datasetContext, tijd, waarde);
}
// Schakel het loggen van gegevens voor de aggregator uit.
aggregator->Uitschakelen ();
}
Adapters
In deze sectie worden de functionaliteiten beschreven die door de klasse Adapter worden geleverd aan een ns-3
simulatie. Deze sectie is bedoeld voor gebruikers die geïnteresseerd zijn in het ontwikkelen van simulaties met de
ns-3 tools en met behulp van het Data Collection Framework, waarvan de klasse Adapter deel uitmaakt,
om gegevensuitvoer te genereren met de resultaten van hun simulatie.
Opmerking: de term 'adapter' kan ook worden gespeld als 'adapter'; we kozen voor de spelling uitgelijnd
met de C++-standaard.
Adapter Overzicht
Een adapter wordt gebruikt om verbindingen te maken tussen verschillende typen DCF-objecten.
Tot op heden is er één adapter geïmplementeerd:
· TimeSeries-adapter
Tijd -Series Adapter
Met de TimeSeriesAdaptor kunnen Probes rechtstreeks verbinding maken met Aggregators zonder dat er een nodig is
Verzamelaar tussendoor.
Beide geïmplementeerde DCF-helpers gebruiken TimeSeriesAdaptors om gesondeerd te worden
waarden van verschillende typen en voer de huidige tijd uit plus de waarde met beide geconverteerd
om te verdubbelen.
De rol van de TimeSeriesAdaptor-klasse is die van een adapter, die onbewerkt gewaardeerd is
sonde gegevens van verschillende typen en voert een tuple van twee dubbele waarden uit. De eerste is een
tijdstempel, die kan worden ingesteld op verschillende resoluties (bijvoorbeeld seconden, milliseconden, enz.) in
de toekomst, maar die momenteel hard gecodeerd is in Seconden. De tweede is de omzetting van a
niet-dubbele waarde naar een dubbele waarde (eventueel met verlies van precisie).
Reikwijdte/beperkingen
In dit gedeelte worden de reikwijdte en beperkingen van het Data Collection Framework besproken.
Momenteel zijn alleen deze Probes geïmplementeerd in DCF:
· Booleaanse sonde
· Dubbele sonde
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TijdProbe
· PakketProbe
· ApplicationPacketProbe
· IPv4PacketProbe
Momenteel zijn er geen Collectors beschikbaar in de DCF, hoewel er een BasicStatsCollector is
ontwikkeling.
Momenteel zijn alleen deze aggregators geïmplementeerd in DCF:
· GnuplotAggregator
· BestandAggregator
Momenteel is alleen deze adapter geïmplementeerd in DCF:
Tijdreeksadapter.
toekomst werken
In dit gedeelte wordt ingegaan op de toekomstige werkzaamheden aan het kader voor gegevensverzameling.
Hier zijn enkele dingen die nog moeten gebeuren:
· Sluit meer sporenbronnen aan ns-3 code om meer waarden uit de simulator te halen.
· Implementeer meer soorten Probes dan er momenteel zijn.
· Implementeer meer dan alleen de enkele huidige 2-D Collector, BasicStatsCollector.
· Implementeer meer aggregators.
· Implementeer meer dan alleen adapters.
Statistisch Kader
Dit hoofdstuk schetst het werk aan het verzamelen van simulatiegegevens en het statistische raamwerk voor
ns-3.
De broncode voor het statistische raamwerk bevindt zich in de directory src/statistieken.
Doelen
Primaire doelstellingen voor deze inspanning zijn de volgende:
· Biedt functionaliteit voor het vastleggen, berekenen en presenteren van gegevens en statistieken voor analyse
van netwerksimulaties.
· Verhoog de simulatieprestaties door de noodzaak te verminderen om uitgebreide traceerlogboeken te genereren
om gegevens te verzamelen.
· Activeer simulatiecontrole via onlinestatistieken, bijv. beëindigen van simulaties of
herhalende beproevingen.
Afgeleide subdoelen en andere doelkenmerken zijn onder andere:
· Integratie met het bestaande ns-3-traceersysteem als basisinstrumentatiekader
van de interne simulatie-engine, bijvoorbeeld netwerkstacks, net-apparaten en kanalen.
· Gebruikers in staat stellen het statistische raamwerk te gebruiken zonder gebruik van tracering
systeem.
· Gebruikers helpen bij het maken, verzamelen en analyseren van gegevens over meerdere onderzoeken.
· Ondersteuning voor door de gebruiker gemaakte instrumenten, bijv. van toepassingsspecifieke gebeurtenissen en
maatregelen.
· Weinig geheugen en CPU-overhead wanneer het pakket niet in gebruik is.
· Zoveel mogelijk gebruik maken van bestaande analyse- en uitvoertools. Het raamwerk mag
bieden enkele basisstatistieken, maar de focus ligt op het verzamelen en maken van gegevens
toegankelijk voor manipulatie in gevestigde tools.
· Eventuele ondersteuning voor het distribueren van onafhankelijke replicaties is belangrijk maar niet inbegrepen
in de eerste ronde van functies.
Overzicht
Het statistische raamwerk bevat de volgende functies:
· Het kernframework en twee basisgegevensverzamelaars: een teller en een min/max/gem/totaal
waarnemer.
· Uitbreidingen daarvan om eenvoudig met tijden en pakketten te werken.
· Uitvoer in platte tekst geformatteerd voor OMNet++.
· Database-uitvoer met behulp van SQLite, een zelfstandige, lichtgewicht, krachtige SQL-engine.
· Verplichte en open metadata voor het beschrijven van en werken met runs.
· Een voorbeeld gebaseerd op het fictieve experiment van het onderzoeken van de eigenschappen van NS-3's
standaard ad-hoc WiFi-prestaties. Het bevat het volgende:
· Constructies van een ad-hoc WiFi-netwerk met twee knooppunten, met de knooppunten op een geparametriseerde afstand
deel.
· UDP-verkeersbron- en -sink-applicaties met iets ander gedrag en
meethaken dan de aandelenklassen.
· Gegevensverzameling van de NS-3-kern via bestaande traceersignalen, in het bijzonder gegevens op
frames verzonden en ontvangen door de WiFi MAC-objecten.
· Instrumentatie van aangepaste applicaties door nieuwe traceersignalen aan de stat te koppelen
framework, maar ook via directe updates. Er wordt informatie vastgelegd over het totale aantal pakketten
verzonden en ontvangen, verzonden bytes en end-to-end vertraging.
· Een voorbeeld van het gebruik van pakketlabels om end-to-end vertraging te volgen.
· Een eenvoudig besturingsscript dat een aantal proeven van het experiment op verschillende manieren uitvoert
afstanden en ondervraagt de resulterende database om een grafiek te produceren met behulp van GNUPlot.
Te doen
Items met hoge prioriteit zijn onder meer:
· Opname van online statistische code, bijv. voor geheugenefficiënte betrouwbaarheidsintervallen.
· Voorzieningen in de dataverzamelaars voor het beëindigen van runs, dwz wanneer een drempel of
vertrouwen is voldaan.
· Gegevensverzamelaars voor het loggen van steekproeven in de loop van de tijd en uitvoer naar de verschillende formaten.
· Demonstreer het schrijven van eenvoudige cyclische gebeurtenislijm om regelmatig wat waarde te peilen.
Elk van deze zou eenvoudig in het huidige kader moeten kunnen worden opgenomen.
Aanpak
Het raamwerk is gebaseerd op de volgende kernprincipes:
· Eén experimentproef wordt uitgevoerd door één instantie van een simulatieprogramma, hetzij in
parallel of serieel.
· Een besturingsscript voert instanties van de simulatie uit, waarbij de parameters indien nodig worden gewijzigd.
· Gegevens worden verzameld en opgeslagen voor plotten en analyse met behulp van externe scripts en
bestaande gereedschappen.
· Maatregelen binnen de ns-3 kern worden genomen door het stat-kader aan te sluiten op bestaand
traceer signalen.
· Traceersignalen of directe manipulatie van het raamwerk kunnen worden gebruikt om maatwerk te instrumenteren
simulatie code.
Die basiscomponenten van het raamwerk en hun interacties worden weergegeven in de
volgende figuur. [afbeelding]
Voorbeeld
In dit gedeelte wordt het proces doorlopen van het opzetten van een experiment in het raamwerk en
daaruit gegevens produceren voor analyse (grafieken), de structuur en API demonstreren
de weg.
Vraag
''Wat zijn de (gesimuleerde) prestaties van ns-3's WiFi NetDevices (gebruikmakend van de default
instellingen)? Hoe ver kunnen draadloze knooppunten in een simulatie uit elkaar staan voordat ze dat niet kunnen
betrouwbaar communiceren?''
· Hypothese: Gebaseerd op kennis van real-life prestaties, zouden de knooppunten moeten communiceren
redelijk goed tot minstens 100 m uit elkaar. Communicatie verder dan 200 meter zou dat niet moeten zijn
haalbaar.
Hoewel dit geen veel voorkomende vraag is in simulatiecontexten, is dit een belangrijke eigenschap
waarvan simulatieontwikkelaars een basiskennis moeten hebben. Het is ook een veel voorkomende
studie gedaan op live hardware.
Simulatie Programma
Het eerste dat u moet doen bij het implementeren van dit experiment, is het ontwikkelen van de simulatie
programma. De code voor dit voorbeeld is te vinden in voorbeelden/statistieken/wifi-example-sim.cc.
Het voert de volgende hoofdstappen uit.
· Parameters declareren en de opdrachtregel ontleden met behulp van ns3::Opdrachtregel.
dubbele afstand = 50.0;
tekenreeksformaat ("OMNet++");
tekenreeksexperiment ("wifi-afstandstest");
tekenreeksstrategie ("wifi-standaard");
tekenreeks runID;
Opdrachtregel cmd;
cmd.AddValue("afstand", "Afstand van elkaar om knooppunten te plaatsen (in meters).", afstand);
cmd.AddValue("formaat", "Formaat om te gebruiken voor gegevensuitvoer.", formaat);
cmd.AddValue("experiment", "Identifier voor experiment.", experiment);
cmd.AddValue("strategie", "Identifier voor strategie.", strategie);
cmd.AddValue("run", "Identifier voor run.", runID);
cmd.Parse (argc, argv);
· Knooppunten en netwerkstapels maken met behulp van ns3::KnooppuntContainer, ns3::WiFiHelperen
ns3::InternetStackHelper.
NodeContainer-knooppunten;
knooppunten.Creëer(2);
WifiHelper wifi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(nodes);
InternetStackHelper internet;
internet.Installeren(knooppunten);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Toewijzen(nodeDevices);
· Positionering van de knooppunten met behulp van ns3::Mobiliteitshelper. Standaard hebben de knooppunten statisch
mobiliteit en zal niet bewegen, maar moet op de gegeven afstand van elkaar worden geplaatst. Er zijn
verschillende manieren om dit te doen; het wordt hier gedaan met behulp van ns3::LijstPositietoewijzer, die trekt
posities uit een bepaalde lijst.
MobilityHelper mobiliteit;
Ptr positionAlloc =
CreateObject ();
positionAlloc->Toevoegen(Vector(0.0, 0.0, 0.0));
positionAlloc->Toevoegen(Vector(0.0, afstand, 0.0));
mobiliteit.SetPositionAllocator(positionAlloc);
mobiliteit.Installeren(knooppunten);
· Het installeren van een traffic generator en een traffic sink. De voorraad Toepassingen zou kunnen
gebruikt, maar het voorbeeld bevat aangepaste objecten in src/test/test02-apps.(cc|h). Deze
hebben een eenvoudig gedrag en genereren een bepaald aantal pakketten met een bepaald interval.
Omdat er maar één van elk is, worden ze handmatig geïnstalleerd; voor een grotere set de
ns3::ApplicatieHelper klasse kan worden gebruikt. De becommentarieerde Configuratie::Instellen regelwijzigingen
de bestemming van de pakketten, in dit voorbeeld standaard ingesteld op uitzenden. Let daar op
in het algemeen kan wifi verschillende prestaties hebben voor broadcast- en unicast-frames vanwege
ander beleid voor snelheidscontrole en MAC-doorgifte.
Ptr appSource = NodeList::GetNode(0);
Ptr afzender = CreateObject ();
appSource->AddApplication(afzender);
afzender->Start(seconden(1));
Ptr appSink = NodeList::GetNode(1);
Ptr ontvanger = CreateObject ();
appSink->AddApplication(ontvanger);
ontvanger->Start(seconden(0));
// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
// Ipv4AddressValue("192.168.0.2"));
· Configureren van de te verzamelen gegevens en statistieken. Het basisparadigma is dat een
ns3::Gegevensverzamelaar object is gemaakt om informatie over deze specifieke run vast te houden
welke waarnemers en rekenmachines zijn aangesloten om daadwerkelijk gegevens te genereren. belangrijk,
run-informatie omvat labels voor het ''experiment'', ''strategie'', ''input'' en
''loop''. Deze worden gebruikt om later gegevens uit meerdere proeven te identificeren en eenvoudig te groeperen.
· Het experiment is het onderzoek waarvan deze studie deel uitmaakt. Hier is het op wifi
prestaties en afstand.
· De strategie is de code of parameters die in deze proef worden onderzocht. In dit voorbeeld
het is opgelost, maar een voor de hand liggende uitbreiding zou zijn om een ander wifi-bit te onderzoeken
tarieven, die elk een andere strategie zouden zijn.
· De input is het specifieke probleem dat aan deze proef wordt gegeven. Hier is het gewoon de
afstand tussen de twee knooppunten.
· De runID is een unieke identificatiecode voor deze proef waarmee de informatie is getagd
voor identificatie in latere analyse. Als er geen run-ID wordt gegeven, maakt het voorbeeldprogramma
een (zwakke) run-ID met de huidige tijd.
Die vier stukjes metadata zijn vereist, maar er kan meer gewenst zijn. Ze kunnen worden toegevoegd
naar het record met behulp van de ns3::DataCollector::Metadata toevoegen() methode.
DataCollector-gegevens;
data.DescribeRun(experiment, strategie, input, runID);
data.AddMetadata("auteur", "tjkopena");
Daadwerkelijke waarneming en berekening wordt gedaan door ns3::Gegevenscalculator objecten, waarvan
er bestaan verschillende soorten. Deze worden gemaakt door het simulatieprogramma, bijgevoegd bij
rapportage- of bemonsteringscode en vervolgens geregistreerd bij de ns3::Gegevensverzamelaar dus dat zullen ze
later worden opgevraagd voor hun uitvoer. Een eenvoudig observatiemechanisme is om bestaande te gebruiken
traceer bronnen, bijvoorbeeld om objecten in de ns-3-kern te instrumenteren zonder hun te wijzigen
code. Hier wordt een teller direct aan een traceersignaal in de WiFi MAC-laag gekoppeld
het doelknooppunt.
Ptr totalRx = CreateObject ();
totalRx->SetKey("wifi-rx-frames");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
data.AddDataCalculator(totalRx);
Rekenmachines kunnen ook rechtstreeks worden gemanipuleerd. In dit voorbeeld wordt een teller gemaakt en
doorgegeven aan de traffic-sink-toepassing om te worden bijgewerkt wanneer pakketten worden ontvangen.
Ptr > appRx = CreateObject >();
appRx->SetKey("ontvanger-rx-pakketten");
ontvanger->SetCounter(appRx);
data.AddDataCalculator(appRx);
Om de telling te verhogen, roept de pakketverwerkingscode van de sink vervolgens een van de
de updatemethoden van de rekenmachine.
m_calc->Bijwerken();
Het programma bevat ook verschillende andere voorbeelden, waarbij zowel de primitieve als de primitieven worden gebruikt
rekenmachines zoals ns3::TellerRekenmachine en die zijn aangepast voor het observeren van pakketten en
keer. In src/test/test02-apps.(cc|h) het creëert ook een eenvoudige aangepaste tag die het gebruikt
om end-to-end vertraging voor gegenereerde pakketten te volgen, resultaten te rapporteren aan een
ns3::TimeMinMaxAvgTotalCalculator data rekenmachine.
· Het uitvoeren van de simulatie, die na het bouwen heel eenvoudig is.
Simulator::Uitvoeren();
· Genereren ofwel OMNet++ or SQLite uitvoer, afhankelijk van de opdrachtregelargumenten. Naar
doe dit a ns3::DataOutputInterface object is gemaakt en geconfigureerd. Het specifieke type
hiervan bepaalt het uitvoerformaat. Dit object krijgt dan de
ns3::Gegevensverzamelaar object dat het ondervraagt om de uitvoer te produceren.
Ptr uitvoer;
als (formaat == "OMNet++") {
NS_LOG_INFO("OmNet++-geformatteerde gegevensuitvoer maken.");
uitvoer = CreateObject ();
} Else {
#ifdef STAT_USE_DB
NS_LOG_INFO("Gegevensuitvoer in SQLite-indeling maken.");
uitvoer = CreateObject ();
# stop als
}
uitvoer->Uitvoer(gegevens);
· Elk geheugen vrijmaken dat door de simulatie wordt gebruikt. Dit zou aan het einde van de main moeten komen
functie voor het voorbeeld.
Simulator::Vernietigen();
Logging
Om te zien wat het voorbeeldprogramma, de applicaties en het stat-framework in detail doen, stelt u in
the NS_LOG variabel op de juiste manier. Het volgende zal overvloedige uitvoer van iedereen opleveren
drie.
$ export NS_LOG=WiFiDistanceExperiment:WiFiDistanceApps
Merk op dat dit de simulatie buitengewoon vertraagt.
Sample uitgang
Het compileren en eenvoudig uitvoeren van het testprogramma komt erbij OMNet++ geformatteerde uitvoer zoals
het volgende aan gegevens.sca.
ren run-1212239121
attr-experiment "wifi-afstandstest"
attr-strategie "wifi-standaard"
attr-invoer "50"
kenmerk beschrijving ""
attr "auteur" "tjkopena"
scalaire wifi-tx-frames tellen 30
scalaire wifi-rx-frames tellen 30
scalaire zender-tx-pakketten tellen 30
scalaire ontvanger-rx-pakketten tellen 30
scalair tx-pkt-grootte aantal 30
scalaire tx-pkt-grootte totaal 1920
scalair tx-pkt-groottegemiddelde 64
scalaire tx-pkt-grootte max 64
scalaire tx-pkt-grootte min 64
scalaire vertraging tellen 30
scalaire vertraging totaal 5884980ns
scalair vertragingsgemiddelde 196166ns
scalaire vertraging max. 196166ns
scalaire vertraging min 196166ns
Controleer: Script
Om het verzamelen van gegevens op verschillende ingangen (afstanden) te automatiseren, is een eenvoudige Bash
script wordt gebruikt om een reeks simulaties uit te voeren. Het is te vinden op
voorbeelden/statistieken/wifi-example-db.sh. Het script is bedoeld om te worden uitgevoerd vanuit de voorbeelden/statistieken/
directory.
Het script doorloopt een reeks afstanden en verzamelt de resultaten in een SQLite
databank. Op elke afstand worden vijf proeven uitgevoerd om een beter beeld te geven van de verwachting
prestatie. Het hele experiment duurt slechts enkele tientallen seconden om op een laag pitje te draaien
machine omdat er tijdens de simulatie geen uitvoer is en er weinig verkeer wordt gegenereerd.
#!/ Bin / sh
AFSTANDEN="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
PROEVEN="1 2 3 4 5"
echo wifi-experiment voorbeeld
als [ -e data.db ]
harte
echo Dood data.db?
lees ANS
als [ "$ANS" = "ja" -o "$ANS" = "y" ]
harte
echo Database verwijderen
rm data.db
fi
fi
voor proef in $TRIALS
do
voor afstand in $DISTANCES
do
echo Proef $proef, afstand $afstand
./bin/test02 --format=db --distance=$distance --run=run-$distance-$trial
gedaan
gedaan
Analyse en Conclusie
Zodra alle proeven zijn uitgevoerd, voert het script een eenvoudige SQL-query uit over het
database met behulp van de SQLite opdrachtregelprogramma. De query berekent het gemiddelde pakketverlies in
elke reeks pogingen die bij elke afstand horen. Het houdt geen rekening met anders
strategieën, maar de informatie is aanwezig in de database om enkele eenvoudige uitbreidingen te maken
en doe dat. De verzamelde gegevens worden vervolgens doorgegeven aan GNUPlot voor grafische weergave.
CMD="selecteer exp.input,avg(100-((rx.waarde*100)/tx.waarde)) \
van Singletons rx, Singletons tx, Experimenten exp \
waarbij rx.run = tx.run EN \
rx.run = exp.run EN \
rx.name='ontvanger-rx-pakketten' EN \
tx.name='sender-tx-pakketten' \
groeperen op exp.input \
bestellen door abs(exp.input) ASC;"
sqlite3 -noheader data.db "$CMD" > wifi-standaard.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-voorbeeld.gnuplot
Het GNUPlot-script gevonden op voorbeelden/statistieken/wifi-example.gnuplot definieert eenvoudig de uitvoer
formaat en wat basisopmaak voor de grafiek.
set terminal postscript portrait verbeterd lw 2 "Helvetica" 14
set maat 1.0, 0.66
#------------------------------------------------ ------
stel "wifi-default.eps" in
#set titel "Pakketverlies over afstand"
set xlabel "Afstand (m) --- gemiddeld 5 pogingen per punt"
xbereik instellen [0:200]
stel het label "% pakketverlies" in
stel een bereik in [0:110]
plot "wifi-default.data" met regels titel "WiFi Defaults"
Einde Resultaat
De resulterende grafiek geeft geen bewijs dat de prestaties van het standaard WiFi-model dat ook zijn
noodzakelijkerwijs onredelijk en geeft enig vertrouwen aan een op zijn minst symbolische trouw aan
realiteit. Wat nog belangrijker is, is dat dit eenvoudige onderzoek helemaal is uitgevoerd
met behulp van het statistisch kader. Succes! [afbeelding]
RealTime
ns-3 is ontworpen voor integratie in testbed- en virtuele machine-omgevingen. Naar
integreren met echte netwerkstacks en pakketten uitzenden/consumeren, een real-time planner is dat wel
nodig om te proberen de simulatieklok te vergrendelen met de hardwareklok. We beschrijven hier a
onderdeel hiervan: de RealTime scheduler.
Het doel van de realtime planner is om de voortgang van de simulatieklok te veroorzaken
synchroon plaatsvinden met betrekking tot een externe tijdbasis. Zonder de aanwezigheid van
een externe tijdbasis (wandklok), springt de simulatietijd onmiddellijk van een gesimuleerde
tijd naar de volgende.
Gedrag
Bij gebruik van een niet-realtime planner (de standaard in ns-3), beweegt de simulator de
simulatietijd tot het volgende geplande evenement. Tijdens de uitvoering van de gebeurtenis is de simulatietijd
bevroren. Met de realtime planner is het gedrag vergelijkbaar vanuit het perspectief van
simulatiemodellen (dwz de simulatietijd wordt bevroren tijdens de uitvoering van de gebeurtenis), maar tussen
gebeurtenissen, zal de simulator proberen de simulatieklok op één lijn te houden met de machine
klok.
Wanneer de uitvoering van een gebeurtenis is voltooid en de planner naar de volgende gebeurtenis gaat, wordt het
scheduler vergelijkt de uitvoeringstijd van de volgende gebeurtenis met de machineklok. Als de volgende
evenement is gepland voor een toekomstige tijd, slaapt de simulator totdat die realtime is bereikt
en voert vervolgens de volgende gebeurtenis uit.
Het kan gebeuren dat, vanwege de verwerking die inherent is aan de uitvoering van simulatiegebeurtenissen,
dat de simulator realtime niet bij kan houden. In dat geval is het aan de gebruiker
configuratie wat te doen. Er zijn er twee ns-3 attributen die het gedrag sturen. De
eerst is ns3::RealTimeSimulatorImpl::Synchronisatiemodus. De twee vermeldingen mogelijk voor
dit attribuut zijn Beste poging (de standaard) of Harde limiet. In de "BestEffort"-modus wordt de
simulator zal gewoon proberen realtime in te halen door gebeurtenissen uit te voeren totdat het een bereikt
punt waar de volgende gebeurtenis in de (realtime) toekomst is, anders eindigt de simulatie. In
BestEffort-modus, dan is het mogelijk dat de simulatie meer tijd in beslag neemt dan de
wandklok tijd. De andere optie "HardLimit" zorgt ervoor dat de simulatie wordt afgebroken als de
tolerantiedrempel wordt overschreden. Dit attribuut is ns3::RealTimeSimulatorImpl::HardLimit
en de standaardwaarde is 0.1 seconden.
Een andere manier van werken is er een waarin gesimuleerde tijd is niet bevroren tijdens een evenement
executie. Deze modus van realtime simulatie is geïmplementeerd maar verwijderd uit de ns-3 boom
vanwege de vraag of het nuttig zou zijn. Als gebruikers geïnteresseerd zijn in een realtime
simulator waarvoor de simulatietijd niet bevriest tijdens de uitvoering van de gebeurtenis (dwz elke
bellen naar Simulator::Nu() geeft de huidige wandkloktijd terug, niet de tijd waarop de
evenement is gestart), neem dan contact op met de ns-developers mailinglijst.
Gebruik
Het gebruik van de realtime simulator is eenvoudig, vanuit een scriptperspectief.
Gebruikers hoeven alleen het kenmerk in te stellen SimulatorImplementatieType naar Realtime
simulator, zoals hieronder:
GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl");
Er zit een script in voorbeelden/realtime/realtime-udp-echo.cc dat heeft een voorbeeld van hoe het moet
configureer het realtime gedrag. Poging:
$ ./waf --run realtime-udp-echo
Of de simulator naar beste vermogen of op een harde limiet-beleidswijze zal werken, wordt bepaald
door de attributen die in de vorige sectie zijn uitgelegd.
Implementatie
De implementatie is opgenomen in de volgende bestanden:
· src/core/model/realtime-simulator-impl.{cc,h}
· src/core/model/wandkloksynchronisatie.{cc,h}
Om een realtime planner te maken, moet u een eerste benadering maken
simulatietijd springt om real-time te verbruiken. Wij stellen voor om dit te doen met een combinatie van
slaap- en drukke wacht. Sleep-waits zorgen ervoor dat het aanroepende proces (thread) het
processor gedurende een bepaalde tijd. Ook al kan deze opgegeven hoeveelheid tijd worden verstreken
tot een resolutie van nanoseconden, wordt het feitelijk geconverteerd naar een OS-specifieke granulariteit. In
Linux, de granulariteit wordt een Jiffy genoemd. Meestal is deze resolutie onvoldoende voor
onze behoeften (in de orde van grootte van tien milliseconden), dus we ronden af naar beneden en slapen voor sommigen
kleiner aantal Jiffies. Het proces wordt vervolgens gewekt na het opgegeven aantal
Jiffies is voorbij. Op dit moment hebben we nog wat tijd om te wachten. Deze tijd is
over het algemeen kleiner dan de minimale slaaptijd, dus we wachten druk op de rest van de
tijd. Dit betekent dat de thread gewoon in een lus zit die cycli verbruikt totdat de
gewenste tijd aanbreekt. Na de combinatie van slaap- en bezet-wachttijden is de verstreken realtime
(wand)klok moet overeenkomen met de simulatietijd van de volgende gebeurtenis en de simulatie
opbrengst.
helpers
In de bovenstaande hoofdstukken heb je kennis gemaakt met verschillende ns-3 programmeerconcepten zoals smart
pointers voor referentie-geteld geheugenbeheer, attributen, naamruimten, callbacks, enz.
Gebruikers die aan deze low-level API werken, kunnen onderling communiceren ns-3 objecten met fijne korreligheid.
Een simulatieprogramma dat volledig is geschreven met behulp van de low-level API zou echter behoorlijk lang zijn
en vervelend om te coderen. Om deze reden is er een aparte zogenaamde "helper-API" overheen gelegd
op de kern ns-3 API. Als je de ns-3 tutorial, je zult al bekend zijn
met de helper-API, aangezien het de API is waarmee nieuwe gebruikers doorgaans als eerste kennismaken.
In dit hoofdstuk introduceren we de ontwerpfilosofie van de helper-API en stellen we deze tegenover
de low-level API. Als u een zware gebruiker van wordt ns-3, zul je waarschijnlijk heen en weer bewegen
tussen deze API's, zelfs in hetzelfde programma.
De helper-API heeft een aantal doelen:
1. de rest van src / heeft geen afhankelijkheden van de helper-API; alles wat er mee gedaan kan worden
de helper-API kan ook worden gecodeerd op de low-level API
2. containers: Simulaties zullen vaak een aantal identieke acties voor groepen moeten uitvoeren
van objecten. De helper-API maakt veel gebruik van containers met vergelijkbare objecten waarop
soortgelijke of identieke bewerkingen kunnen worden uitgevoerd.
3. De helper-API is niet generiek; het streeft niet naar maximaal hergebruik van code. Dus,
programmeerconstructies zoals polymorfisme en sjablonen die hergebruik van code mogelijk maken
niet zo gangbaar. Zo zijn er aparte CsmaNetDevice-helpers en
PointToPointNetDevice-helpers, maar ze zijn niet afgeleid van een gemeenschappelijke NetDevice-basis
klasse.
4. De helper-API werkt doorgaans met stack-toegewezen (vs. heap-toegewezen) objecten. Voor
sommige programma's, ns-3 gebruikers hoeven zich misschien geen zorgen te maken over Object Create of
Ptr-behandeling; ze kunnen genoegen nemen met containers met objecten en aan de stapel toegewezen helpers
die op hen inwerken.
De helper-API draait echt om maken ns-3 programma's gemakkelijker te schrijven en te lezen, zonder
de kracht van de low-level interface wegnemen. De rest van dit hoofdstuk biedt er enkele
voorbeelden van de programmeerconventies van de helper-API.
maken Percelen gebruik the gnuplot Klasse
Er zijn 2 veelgebruikte methoden om een plot te maken ns-3 en gnuplot (‐
http://www.gnuplot.info):
1. Maak een gnuplot-besturingsbestand met behulp van ns-3de Gnuplot-klasse.
2. Maak een gnuplot-gegevensbestand met waarden die zijn gegenereerd door ns-3.
Dit gedeelte gaat over methode 1, dwz het gaat over het maken van een plot met behulp van ns-3's Gnuplot
klas. Als u geïnteresseerd bent in methode 2, zie dan de subsectie "Een echt voorbeeld" onder de
"Tracering" sectie in de ns-3 Tutorial.
Wij creëren Percelen gebruik the gnuplot Klasse
De volgende stappen moeten worden genomen om een plot te maken met behulp van ns-3's Gnuplot-klasse:
1. Wijzig uw code zodat deze de klasse Gnuplot en zijn functies gebruikt.
2. Voer uw code uit zodat deze een gnuplot-besturingsbestand maakt.
3. Roep gnuplot aan met de naam van het gnuplot-besturingsbestand.
4. Bekijk het grafische bestand dat is gemaakt in uw favoriete grafische viewer.
Zie de code van de voorbeeldplots die hieronder worden besproken voor details over stap 1.
An Voorbeeld Programma uit die u gebruikt the gnuplot Klasse
Een voorbeeldprogramma dat gebruikt ns-3De Gnuplot-klasse is hier te vinden:
src/stats/examples/gnuplot-example.cc
Om dit voorbeeld uit te voeren, doet u het volgende:
$ ./waf-schaal
$ cd build/debug/src/stats/examples
$ ./gnuplot-voorbeeld
Dit zou de volgende gnuplot-besturingsbestanden moeten opleveren in de map waar het voorbeeld
bevindt zich:
plot-2d.plt
plot-2d-met-foutbalken.plt
plot-3d.plt
Om deze gnuplot-besturingsbestanden te verwerken, doet u het volgende:
$ gnuplot plot-2d.plt
$ gnuplot plot-2d-met-foutbalken.plt
$ gnuplot plot-3d.plt
Dit zou de volgende grafische bestanden moeten opleveren in de map waar het voorbeeld zich bevindt
gelegen:
plot-2d.png
plot-2d-met-foutbalken.png
plot-3d.png
U kunt deze grafische bestanden bekijken in uw favoriete grafische viewer. Als je gimp
geïnstalleerd op uw machine, kunt u bijvoorbeeld dit doen:
$ gimp plot-2d.png
$ gimp plot-2d-met-foutbalken.png
$ gimp plot-3d.png
An Voorbeeld 2-dimensionaal Plot
De volgende tweedimensionale plot
[foto]
is gemaakt met behulp van de volgende code van gnuplot-example.cc:
namespace std; gebruiken;
string fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + ".png";
tekenreeks plotFileName = fileNameWithNoExtension + ".plt";
tekenreeks plotTitle = "2D-plot";
tekenreeks dataTitle = "2D-gegevens";
// Instantiseer de plot en stel de titel in.
Gnuplot-plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Maak het grafische bestand, dat het plotbestand zal maken wanneer het
// wordt gebruikt met Gnuplot, een PNG-bestand zijn.
plot.SetTerminal ("png");
// Stel de labels voor elke as in.
plot.SetLegend ("X-waarden", "Y-waarden");
// Stel het bereik voor de x-as in.
plot.AppendExtra ("set xrange [-6:+6]");
// Instantiseer de dataset, stel de titel in en maak de punten
// uitgezet samen met verbindingslijnen.
Gnuplot2dDataset-gegevensset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);
dubbele X;
dubbele y;
// Maak de 2D-gegevensset.
voor (x = -5.0; x <= +5.0; x += 1.0)
{
// Bereken de 2D-kromme
//
// 2
// y = x.
//
y = x * x;
// Voeg dit punt toe.
dataset.Toevoegen (x, y);
}
// Voeg de dataset toe aan de plot.
plot.AddDataset (gegevensset);
// Open het plotbestand.
ofstream plotFile (plotFileName.c_str());
// Schrijf het plotbestand.
plot.GenerateOutput (plotFile);
// Sluit het plotbestand.
plotFile.sluiten ();
An Voorbeeld 2-dimensionaal Plot with Fout Bars
De volgende tweedimensionale grafiek met foutbalken in de x- en y-richting
[foto]
is gemaakt met behulp van de volgende code van gnuplot-example.cc:
namespace std; gebruiken;
string fileNameWithNoExtension = "plot-2d-met-foutbalken";
string graphicsFileName = fileNameWithNoExtension + ".png";
tekenreeks plotFileName = fileNameWithNoExtension + ".plt";
string plotTitle = "2D-plot met foutbalken";
string dataTitle = "2D-gegevens met foutbalken";
// Instantiseer de plot en stel de titel in.
Gnuplot-plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Maak het grafische bestand, dat het plotbestand zal maken wanneer het
// wordt gebruikt met Gnuplot, een PNG-bestand zijn.
plot.SetTerminal ("png");
// Stel de labels voor elke as in.
plot.SetLegend ("X-waarden", "Y-waarden");
// Stel het bereik voor de x-as in.
plot.AppendExtra ("set xrange [-6:+6]");
// Instantiseer de dataset, stel de titel in en maak de punten
// geplot zonder verbindingslijnen.
Gnuplot2dDataset-gegevensset;
dataset.SetTitle (dataTitle);
dataset.SetStyle (Gnuplot2dDataset::POINTS);
// Zorg ervoor dat de dataset foutbalken heeft in zowel de x- als de y-richting.
dataset.SetErrorBars (Gnuplot2dDataset::XY);
dubbele X;
dubbele xErrorDelta;
dubbele y;
dubbele yErrorDelta;
// Maak de 2D-gegevensset.
voor (x = -5.0; x <= +5.0; x += 1.0)
{
// Bereken de 2D-kromme
//
// 2
// y = x.
//
y = x * x;
// Maak de onzekerheid in de x-richting constant en maak
// de onzekerheid in de y-richting is een constante fractie van
// y's waarde.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;
// Voeg dit punt toe met onzekerheden in zowel de x als de y
// richting.
dataset.Toevoegen (x, y, xErrorDelta, yErrorDelta);
}
// Voeg de dataset toe aan de plot.
plot.AddDataset (gegevensset);
// Open het plotbestand.
ofstream plotFile (plotFileName.c_str());
// Schrijf het plotbestand.
plot.GenerateOutput (plotFile);
// Sluit het plotbestand.
plotFile.sluiten ();
An Voorbeeld 3-dimensionaal Plot
De volgende tweedimensionale plot
[foto]
is gemaakt met behulp van de volgende code van gnuplot-example.cc:
namespace std; gebruiken;
string fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + ".png";
tekenreeks plotFileName = fileNameWithNoExtension + ".plt";
tekenreeks plotTitle = "3D-plot";
tekenreeks dataTitle = "3D-gegevens";
// Instantiseer de plot en stel de titel in.
Gnuplot-plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Maak het grafische bestand, dat het plotbestand zal maken wanneer het
// wordt gebruikt met Gnuplot, een PNG-bestand zijn.
plot.SetTerminal ("png");
// Draai de grafiek 30 graden rond de x-as en draai vervolgens de
// plot 120 graden rond de nieuwe z-as.
plot.AppendExtra ("set weergave 30, 120, 1.0, 1.0");
// Maak de nul voor de z-as in het x-as- en y-asvlak.
plot.AppendExtra ("zet ticslevel 0");
// Stel de labels voor elke as in.
plot.AppendExtra ("stel xlabel 'X-waarden' in");
plot.AppendExtra ("set ylabel 'Y-waarden'");
plot.AppendExtra ("stel zlabel 'Z-waarden' in");
// Stel de bereiken in voor de x- en y-as.
plot.AppendExtra ("set xrange [-5:+5]");
plot.AppendExtra ("set yrange [-5:+5]");
// Instantiseer de dataset, stel de titel in en maak de punten
// verbonden door lijnen.
Gnuplot3dDataset-gegevensset;
dataset.SetTitle (dataTitle);
dataset.SetStyle ("met lijnen");
dubbele X;
dubbele y;
dubbele z;
// Maak de 3D-gegevensset.
voor (x = -5.0; x <= +5.0; x += 1.0)
{
voor (y = -5.0; y <= +5.0; y += 1.0)
{
// Bereken het 3D-oppervlak
//
// 2 2
// z = x * y .
//
z = x * x * y * y;
// Voeg dit punt toe.
dataset.Toevoegen (x, y, z);
}
// De lege regel is nodig aan het einde van de gegevens van elke x-waarde
// punten om het 3D-oppervlakteraster te laten werken.
dataset.VoegLegeLijn toe ();
}
// Voeg de dataset toe aan de plot.
plot.AddDataset (gegevensset);
// Open het plotbestand.
ofstream plotFile (plotFileName.c_str());
// Schrijf het plotbestand.
plot.GenerateOutput (plotFile);
// Sluit het plotbestand.
plotFile.sluiten ();
gebruik Python naar lopen ns-3
Python-bindingen laten de C++-code toe ns-3 worden aangeroepen vanuit Python.
Dit hoofdstuk laat zien hoe je een Python-script maakt dat kan worden uitgevoerd ns-3 alsmede de
proces van het maken van Python-bindingen voor een C++ ns-3 module.
Introductie
Het doel van Python-bindingen voor ns-3 zijn tweeledig:
1. Laat de programmeur volledige simulatiescripts schrijven in Python (‐
http://www.python.org);
2. Maak prototypes van nieuwe modellen (bijv. routeringsprotocollen).
Voorlopig is de primaire focus van de bindingen het eerste doel, maar het tweede
doel zal uiteindelijk ook worden ondersteund. Python-bindingen voor ns-3 worden ontwikkeld
met behulp van een nieuwe tool genaamd PyBindGen (http://code.google.com/p/pybindgen).
An Voorbeeld Python Script uit die Runs ns-3
Hier is een voorbeeldcode die in Python is geschreven en die wordt uitgevoerd ns-3, die is geschreven
in C++. Dit Python-voorbeeld is te vinden in voorbeelden/tutorial/first.py:
importeer ns.applicaties
importeer ns.core
importeer ns.internet
importeer ns.network
importeer ns.point_to_point
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
knooppunten = ns.network.NodeContainer()
knooppunten.Creëer(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Vertraging", ns.core.StringValue("2ms"))
apparaten = pointToPoint.Install(nodes)
stapel = ns.internet.InternetStackHelper()
stack.Install(knooppunten)
adres = ns.internet.Ipv4AddressHelper()
adres.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
interfaces = adres.Toewijzen (apparaten);
echoServer = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(knooppunten.Get(1))
serverApps.Start(ns.core.Seconden(1.0))
serverApps.Stop(ns.core.Seconden(10.0))
echoClient = ns.applicaties.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
clientApps = echoClient.Install(knooppunten.Get(0))
clientApps.Start(ns.core.Seconden(2.0))
clientApps.Stop(ns.core.Seconden(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
Hardlopen Python Scripts
waf bevat enkele opties die het Python-pad automatisch bijwerken om de ns3 te vinden
moduul. Om voorbeeldprogramma's uit te voeren, zijn er twee manieren om waf hiervoor te gebruiken. Een
is om een waf-shell uit te voeren; bijvoorbeeld:
$ ./waf --shell
$ python voorbeelden/wireless/mixed-wireless.py
en de andere is om de --pyrun optie te gebruiken om te waven:
$ ./waf --pyrun voorbeelden/wireless/mixed-wireless.py
Een python-script uitvoeren onder de C-foutopsporing:
$ ./waf --shell
$ gdb --args python voorbeelden/wireless/mixed-wireless.py
Om uw eigen Python-script uit te voeren dat aanroept ns-3 en dat heeft dit pad,
/pad/naar/uw/voorbeeld/mijn-script.py, doe het volgende:
$ ./waf --shell
$ python /pad/naar/uw/voorbeeld/mijn-script.py
Voorbehoud
Python-bindingen voor ns-3 zijn een werk in uitvoering en er zijn enkele beperkingen bekend
ontwikkelaars. Enkele van deze beperkingen (niet alle) worden hier vermeld.
Onvolledig Dekking
Houd er allereerst rekening mee dat niet 100% van de API in Python wordt ondersteund. Sommige van de
redenen zijn:
1. Sommige API's maken gebruik van pointers, waarvoor kennis nodig is van welk soort geheugen
het doorgeven van semantiek (wie bezit welk geheugen). Dergelijke kennis behoort niet tot de functie
handtekeningen, en is gedocumenteerd of soms zelfs niet gedocumenteerd. Annotaties zijn
nodig om deze functies te binden;
2. Soms wordt een ongebruikelijk fundamenteel datatype of C++-construct gebruikt dat nog niet beschikbaar is
ondersteund door PyBindGen;
3. GCC-XML rapporteert geen op sjablonen gebaseerde klassen, tenzij deze zijn geïnstantieerd.
De meeste ontbrekende API’s kunnen worden ingepakt, als er voldoende tijd, geduld en expertise wordt gegeven
zal waarschijnlijk worden ingepakt als er bugrapporten worden ingediend. Dien echter geen bugrapport in
zeggen "de bindingen zijn onvolledig", omdat we niet over de mankracht beschikken om 100% van de projecten te voltooien
bindingen.
Camper ombouw constructors
Camper ombouw constructeurs worden nog niet volledig ondersteund door PyBindGen en fungeren altijd als
expliciete constructors bij het vertalen van een API naar Python. In C++ kun je dat bijvoorbeeld doen
deze:
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
In Python moet je voorlopig het volgende doen:
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(backboneDevices)
Opdrachtregel
Commandoregel::AddValue() werkt anders in Python dan in ns-3. In Python wordt de
De eerste parameter is een tekenreeks die de naam van de opdrachtregeloptie vertegenwoordigt. Wanneer de optie
is ingesteld, wordt een attribuut met dezelfde naam als de optienaam ingesteld op de Opdrachtregel()
voorwerp. Voorbeeld:
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.Opdrachtregel()
cmd.NumNodesSide = Geen
cmd.AddValue("NumNodesSide", "Aantal knooppunten aan de rasterzijde (het totale aantal knooppunten is dit aantal in het kwadraat)")
cmd.Parse(argv)
[...]
als cmd.NumNodesSide Geen is:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
anders:
num_nodes_side = int(cmd.NumNodesSide)
Tracing
Op callback gebaseerde tracering wordt nog niet goed ondersteund voor Python, als nieuw ns-3 API moet
worden verstrekt om dit te ondersteunen.
Het schrijven van Pcap-bestanden wordt ondersteund via de normale API.
Ascii-tracering wordt sindsdien ondersteund ns-3.4 via de normale C++ API vertaald naar Python.
Ascii-tracering vereist echter de creatie van een ostream-object dat in de ascii terechtkomt
traceermethoden. In Python is de C++ std::ofstream minimaal ingepakt om dit mogelijk te maken
dit. Bijvoorbeeld:
ascii = ns3.ofstream("wifi-ap.tr") # maak het bestand aan
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Uitvoeren()
ns3.Simulator.Vernietigen()
ascii.close() # sluit het bestand
Er is één waarschuwing: u mag niet toestaan dat het bestandsobject tijdens het verwijderen van afval wordt verzameld ns-3
maakt er nog steeds gebruik van. Dat betekent dat de bovenstaande 'ascii'-variabele niet mag verdwijnen
buiten bereik, anders crasht het programma.
Cygwin beperking
Python-bindingen werken niet op Cygwin. Dit komt door een gccxml-bug.
U kunt ermee wegkomen door de API-definities opnieuw te scannen vanuit de cygwin
omgeving (./waf --python-scan). De meest waarschijnlijke oplossing zal echter waarschijnlijk wel moeten zijn
Het kan zijn dat we Python-bindingen in CygWin uitschakelen.
Als je echt geïnteresseerd bent in Python-bindingen op Windows, probeer dan eens te bouwen met mingw en native
Python in plaats daarvan. Of anders, om te bouwen zonder python-bindingen, schakel je python-bindingen uit in het
configuratiefase:
$ ./waf configure --disable-python
Werkzaam with Python bindingen
Er zijn momenteel twee soorten Python-bindingen beschikbaar ns-3:
1. Monolithische bindingen bevatten API-definities voor alle modules en zijn te vinden in
één enkele map, bindingen/python.
2. Modulaire bindingen bevatten API-definities voor een enkele module en zijn in elke module te vinden
module's bindingen directory.
Python bindingen Workflow
Het proces waarmee Python-bindingen worden afgehandeld, is als volgt:
1. Periodiek gebruikt een ontwikkelaar een GCC-XML (http://www.gccxml.org) gebaseerde API-scanning
script, dat de gescande API-definitie opslaat als bindingen/python/ns3_module_*.py bestanden
of als Python-bestanden in elke module' bindingen map. Deze bestanden worden bewaard onder
versiebeheer in de basis ns-3 opslagplaats;
2. Andere ontwikkelaars klonen de repository en gebruiken de reeds gescande API-definities;
3. Bij het configureren ns-3, pybindgen wordt automatisch gedownload als dit nog niet het geval is
geïnstalleerd. Uitgegeven ns-3 tarballs zal een kopie van pybindgen verzenden.
Als er iets misgaat bij het compileren van Python-bindingen en u deze gewoon wilt negeren
en verder gaan met C++, je kunt Python uitschakelen met:
$ ./waf --disable-python
Instructies besteld, Behandeling New Bestanden or Veranderd API's
Dus je hebt het bestaande veranderd ns-3 API's en Python-bindingen compileren niet meer? Doen
wanhoop niet, u kunt de bindingen opnieuw scannen om nieuwe bindingen te maken die de wijzigingen weerspiegelen
aan de ns-3 API.
Afhankelijk van of u monolithische of modulaire bindingen gebruikt, raadpleegt u de onderstaande discussies
leer hoe u uw Python-bindingen opnieuw kunt scannen.
monolitisch Python bindingen
Het scannen the monolitisch Python bindingen
Ga als volgt te werk om de monolithische Python-bindingen te scannen:
$ ./waf --python-scan
Organisatie of the monolitisch Python bindingen
De monolithische Python API-definities zijn als volgt georganiseerd. Voor elk ns-3 module
, het bestand bindingen/python/ns3_module_ .py beschrijft de API. Elk van deze
bestanden hebben 3 functies op het hoogste niveau:
1. def register_types(module)(): deze functie zorgt voor het registreren van nieuwe typen (bijv
C++ klassen, enums) die zijn gedefinieerd in de module;
2. def register_methoden(module)(): deze functie roept, voor elke klasse , een andere
functie register_methods_Ns3 (module). Deze laatste functies voegen methode toe
definities voor elke klasse;
3. def register_functies(module)(): deze functie registreert ns-3 functies die erbij horen
die module.
Modular Python bindingen
Overzicht
Sinds ns 3.11 worden de modulaire bindingen toegevoegd, parallel aan het oude monolithische
bindingen.
De nieuwe Python-bindingen worden gegenereerd in een 'ns'-naamruimte, in plaats van 'ns3' voor de oude
bindingen. Voorbeeld:
van ns.network import Node
n1 = Knooppunt()
Met modulaire Python-bindingen:
1. Er is voor elk een aparte Python-uitbreidingsmodule ns-3 moduul;
2. Het scannen van API-definities (apidefs) gebeurt per ns-module;
3. De apidefs-bestanden van elke module worden opgeslagen in een 'bindings'-submap van de module
map;
Het scannen the Modular Python bindingen
Om bijvoorbeeld de modulaire Python-bindingen voor de kernmodule te scannen, doet u het volgende:
$ ./waf --apiscan=kern
Ga als volgt te werk om de modulaire Python-bindingen voor alle modules te scannen:
$ ./waf --apiscan=alles
Wij creëren a New Module
Als u een nieuwe module toevoegt, blijven Python-bindingen compileren, maar niet
bedek de nieuwe module.
Om een nieuwe module af te dekken, moet u een bindingen/python/ns3_module_ .py bestand
vergelijkbaar met wat in de vorige paragrafen is beschreven, en registreer het in de variabele
LOCAL_MODULES() in bindingen/python/ns3modulegen.py
Het toevoegen van Modular bindingen Naar A Bestaand Module
Om ondersteuning voor modulaire bindingen toe te voegen aan een bestaand ns-3 module, voegt u gewoon het volgende toe
regel naar zijn wscript build() functie:
bld.ns3_python_bindings()
Organisatie of the Modular Python bindingen
De src/ /bindingen directory kan de volgende bestanden bevatten, waarvan sommige
optioneel:
· callbacks_list.py: dit is een gescand bestand, NIET AANRAKEN. Bevat een lijst van
Callback<...> sjabloonexemplaren gevonden in de gescande headers;
· modulegen__gcc_LP64.py: dit is een gescand bestand, NIET AANRAKEN. Gescande API-definities
voor de GCC, LP64-architectuur (64-bits)
· modulegen__gcc_ILP32.py: dit is een gescand bestand, NIET AANRAKEN. Gescande API-definities
voor de GCC, ILP32-architectuur (32-bits)
· modulegen_customizations.py: u kunt dit bestand optioneel toevoegen om het
pybindgen-code genereren
· scan-header.h: u kunt dit bestand optioneel toevoegen om aan te passen welk kopbestand wordt gescand
voor de moduul. In principe wordt dit bestand gescand in plaats van ns3/ -module.h.
Meestal is de eerste instructie #include "ns3/ -module.h", plus een aantal andere
dingen om sjablooninstanties te forceren;
· module_helpers.cc: u kunt extra bestanden toevoegen, zoals deze, om te koppelen aan python
uitbreidingsmodule, maar ze moeten wel geregistreerd zijn in het wscript. Kijk naar
src/core/wscript voor een voorbeeld van hoe je dit moet doen;
· .py: als dit bestand bestaat, wordt het de "frontend" python-module voor de ns3
module, en de uitbreidingsmodule (.so-bestand) wordt _ .dus in plaats van .Dus.
De .py-bestand moet alle symbolen uit de module importeren _ (dit is meer
lastiger dan het klinkt, zie src/core/bindings/core.py voor een voorbeeld), en kan dan toevoegen
enkele aanvullende pure-python-definities.
Meer Informatie besteld, Developers
Als u een ontwikkelaar bent en meer informatie nodig heeft over ns-3's Python-bindingen, zie de
Python bindingen wiki pagina.
Tests
Overzicht
Dit document gaat over het testen en valideren van ns-3 software.
Dit document biedt
· achtergrondinformatie over terminologie en softwaretesten (hoofdstuk 2);
· een beschrijving van het ns-3 toetsingskader (hoofdstuk 3);
· een gids voor modelontwikkelaars of nieuwe modelbijdragers voor het schrijven van tests (Hoofdstuk
4);
Kortom, de eerste drie hoofdstukken moeten worden gelezen door ns-ontwikkelaars en medewerkers die
moeten begrijpen hoe testcode en gevalideerde programma's kunnen worden bijgedragen, en de rest
van het document biedt ruimte voor mensen om te rapporteren over welke aspecten van geselecteerde modellen
zijn gevalideerd.
Achtergrond
In deze hoofdstuk mei be overgeslagen by lezers familiaal with the basics of software testen.
Het schrijven van foutloze software is een moeilijk voorstel. Er zijn veel dimensies aan de
probleem en er is veel verwarring over wat wordt bedoeld met verschillende termen in
verschillende contexten. We hebben het de moeite waard gevonden om wat tijd te besteden aan het bekijken van de
onderwerp en het definiëren van enkele termen.
Softwaretesten kan losjes worden gedefinieerd als het proces van het uitvoeren van een programma met de
bedoeling fouten te vinden. Wanneer men een discussie aangaat over het testen van software, is het
wordt al snel duidelijk dat er veel verschillende denkwijzen zijn waarmee men dat kan
het onderwerp benaderen.
Men zou het proces bijvoorbeeld kunnen opsplitsen in brede functionele categorieën zoals
''correctheidstesten'' ''prestatietesten'' ''robuustheidstesten'' en ''beveiliging
testen.'' Een andere manier om naar het probleem te kijken is door levenscyclus: ''testen van vereisten''
''ontwerptesten'', ''acceptatietesten'' en ''onderhoudstesten''. Nog een andere kijk
is door de reikwijdte van het geteste systeem. In dit geval kan men spreken van ''unit testing'',
''componenttesten'' ''integratietesten'' en ''systeemtesten''. Deze termen zijn
ook op geen enkele manier gestandaardiseerd, en dus ''onderhoudstesten'' en ''regressie''
testen'' kan door elkaar worden gehoord. Bovendien worden deze termen vaak misbruikt.
Er zijn ook een aantal verschillende filosofische benaderingen van het testen van software. Voor
Sommige organisaties pleiten er bijvoorbeeld voor om testprogramma's te schrijven voordat ze daadwerkelijk worden geïmplementeerd
de gewenste software, resulterend in ''test-driven development''. Sommige organisaties pleiten voor
testen vanuit klantperspectief zo snel mogelijk, in navolging van een parallel met de
agile ontwikkelproces: ''test vroeg en test vaak.'' Dit wordt ook wel eens genoemd
''agile testen.'' Het lijkt erop dat er voor iedereen minstens één benadering van testen is
ontwikkelingsmethodiek.
De ns-3 project pleit niet voor een van deze processen, maar
het project als geheel heeft vereisten die het testproces ondersteunen.
Zoals alle grote softwareproducten, ns-3 heeft een aantal kwaliteiten die voor aanwezig moeten zijn
het product om te slagen. Vanuit een testperspectief, sommige van deze kwaliteiten moeten dat zijn
aan de orde zijn dat ns-3 moet ''juist'', ''robuust'', ''performant'' en zijn
''onderhoudbaar'' Idealiter zouden er statistieken moeten zijn voor elk van deze dimensies die dat wel zijn
gecontroleerd door de tests om vast te stellen wanneer het product niet aan de verwachtingen voldoet /
vereisten.
Juistheid
Het essentiële doel van testen is om te bepalen of een stuk software zich gedraagt
''juist.'' Voor ns-3 dit betekent dat als we iets simuleren, de simulatie dat zou moeten doen
getrouw een fysieke entiteit of proces vertegenwoordigen met een gespecificeerde nauwkeurigheid en
precisie.
Het blijkt dat er twee perspectieven zijn van waaruit men correctheid kan bekijken.
Verifiëren dat een bepaald model is geïmplementeerd volgens de specificatie is
generiek genoemd verificatie. Het proces om te beslissen of het model geschikt is voor
het beoogde gebruik wordt algemeen genoemd bevestiging.
Validatie en Verificatie
Een computermodel is een wiskundige of logische weergave van iets. Het kan
vertegenwoordigen een voertuig, een olifant (zie David van Harel praten over modellering an olifant at
SIMUTools 2009, of een netwerkkaart. Modellen kunnen ook processen vertegenwoordigen, zoals globaal
opwarming, verkeersstroom op de snelweg of een specificatie van een netwerkprotocol. Modellen kunnen zijn
volledig getrouwe weergaven van een logische processpecificatie, maar ze
kan noodzakelijkerwijs nooit een fysiek object of proces volledig simuleren. In de meeste gevallen is een
Er is een aantal vereenvoudigingen aangebracht in het model om simulatie computationeel te maken
handelbaar.
Elk model heeft een doel system dat het probeert te simuleren. De eerste stap erin
het creëren van een simulatiemodel is het identificeren van dit doelsysteem en het detailniveau en
nauwkeurigheid die de simulatie moet reproduceren. Bij een logisch proces
het doelsysteem kan worden geïdentificeerd als ''TCP zoals gedefinieerd door RFC 793.'' In dit geval is het
zal waarschijnlijk wenselijk zijn om een model te maken dat RFC volledig en getrouw reproduceert
793. Bij een fysiek proces zal dit niet mogelijk zijn. Als je bijvoorbeeld
een draadloze netwerkkaart wilt simuleren, kunt u bepalen dat u ''an
nauwkeurige implementatie op MAC-niveau van de 802.11-specificatie en [...] niet zo traag
Model op PHY-niveau van de 802.11a-specificatie.''
Zodra dit is gebeurd, kan men een abstract model van het doelsysteem ontwikkelen. Dit is
typisch een oefening in het managen van de afwegingen tussen complexiteit en resourcevereisten
en nauwkeurigheid. Het proces van het ontwikkelen van een abstract model wordt genoemd model
kwalificatie in de literatuur. In het geval van een TCP-protocol resulteert dit proces in een
ontwerp voor een verzameling objecten, interacties en gedragingen die volledig zullen worden geïmplementeerd
RFC 793 binnen ns-3. In het geval van de draadloze kaart resulteert dit proces in een aantal
compromissen om de fysieke laag te simuleren en het ontwerp van een netwerkapparaat
en kanaal voor ns-3, samen met de gewenste objecten, interacties en gedragingen.
Dit abstracte model wordt vervolgens uitgewerkt tot een ns-3 model dat het abstracte implementeert
model als computerprogramma. Het proces om de implementatie in overeenstemming te brengen met de
abstract model wordt genoemd model verificatie in de literatuur.
Het proces tot nu toe is een open lus. Wat overblijft is om vast te stellen dat een gegeven ns-3
model heeft een verband met een bepaalde realiteit -- waar een model een nauwkeurige weergave van is
een echt systeem, of het nu een logisch proces is of een fysieke entiteit.
Als men een simulatiemodel gaat gebruiken om te proberen te voorspellen hoe een echt systeem gaat
om je te gedragen, moet er een reden zijn om je resultaten te geloven -- dat wil zeggen, kan men daarop vertrouwen
een gevolgtrekking uit het model vertaalt zich in een correcte voorspelling voor het echte systeem.
Het proces om het gedrag van het ns-3-model in overeenstemming te brengen met het gewenste doelsysteem
gedrag zoals gedefinieerd door het modelkwalificatieproces wordt genoemd model bevestiging in de
literatuur. In het geval van een TCP-implementatie kunt u het gedrag van
uw ns-3 TCP-model naar een referentie-implementatie om uw model te valideren. In
in het geval van een draadloze fysieke laagsimulatie, wilt u misschien het gedrag van vergelijken
uw model naar dat van echte hardware in een gecontroleerde setting,
De ns-3 testomgeving biedt tools voor zowel modelvalidatie als
testen, en moedigt de publicatie van validatieresultaten aan.
robuustheid
Robuustheid is de kwaliteit van het kunnen weerstaan van spanningen of veranderingen in omgevingen,
inputs of berekeningen etc. Een systeem of ontwerp is ''robuust'' als het daarmee om kan gaan
veranderingen met minimaal verlies van functionaliteit.
Dit soort testen wordt meestal gedaan met een bepaalde focus. Bijvoorbeeld het systeem als
een geheel kan op veel verschillende systeemconfiguraties worden uitgevoerd om aan te tonen dat dit kan
correct presteren in een groot aantal omgevingen.
Het systeem kan ook onder druk komen te staan door bijna of boven capaciteit te werken door te genereren
of het simuleren van verschillende soorten uitputting van hulpbronnen. Dit testgenre wordt genoemd
''stress testen.''
Het systeem en zijn componenten kunnen worden blootgesteld aan zogenaamde ''schone tests'' die aantonen
een positief resultaat -- dat wil zeggen dat het systeem correct werkt als reactie op een grote
variatie van verwachte configuraties.
Het systeem en zijn componenten kunnen ook worden blootgesteld aan ''vuile tests'' die invoer leveren
buiten het verwachte bereik. Als een module bijvoorbeeld een tekenreeks met nul eindigt verwacht
representatie van een geheel getal, kan een vuile test een niet-afgesloten reeks willekeurig opleveren
tekens om te controleren of het systeem niet vastloopt als gevolg van deze onverwachte invoer.
Helaas is het opsporen van dergelijke ''vuile'' input en het nemen van preventieve maatregelen om de
systeem niet catastrofaal faalt, kan een enorme hoeveelheid ontwikkelingsoverhead vergen.
Om de ontwikkelingstijd te verkorten, werd al vroeg in het project besloten om
minimaliseer de hoeveelheid parametervalidatie en foutafhandeling in de ns-3 codebase. Voor
om deze reden besteden we niet veel tijd aan vuile tests - het zou alleen maar het probleem aan het licht brengen
resultaten van de ontwerpbeslissing waarvan we weten dat we die hebben genomen.
Dat willen we wel aantonen ns-3 software werkt onder bepaalde voorwaarden. Wij
leen een paar definities om dit een beetje te verfijnen. De domein of toepasbaarheid is
een reeks voorgeschreven voorwaarden waarvoor het model is getest, vergeleken met
werkelijkheid voor zover mogelijk, en geschikt geacht voor gebruik. De reeks of nauwkeurigheid is een
overeenkomst tussen het computermodel en de werkelijkheid binnen een toepassingsgebied.
De ns-3 testomgeving biedt hulpmiddelen voor het opzetten en uitvoeren van tests
omgevingen over meerdere systemen (buildbot) en biedt klassen om clean
tests om de werking van het systeem te verifiëren binnen het verwachte ''toepasbaarheidsgebied''
en ''bereik van nauwkeurigheid''.
krachtig
Oké, ''performant'' is geen echt Engels woord. Het is echter een zeer beknopt neologisme
dat wordt vrij vaak gebruikt om te beschrijven wat we willen ns-3 zijn: krachtig en snel genoeg om
de klus klaren.
Dit gaat echt over het brede onderwerp van het testen van softwareprestaties. Een van de belangrijkste
dingen die worden gedaan is om twee systemen te vergelijken om te vinden welke beter presteert (vgl
ijkpunten). Dit wordt gebruikt om aan te tonen dat bijv. ns-3 kan een basissoort uitvoeren
van simulatie minstens zo snel als een concurrerend hulpmiddel, of kan worden gebruikt om delen van te identificeren
het systeem dat slecht presteert.
In de ns-3 test framework bieden wij ondersteuning voor het timen van verschillende soorten testen.
Onderhoudbaarheid
Een softwareproduct moet onderhoudbaar zijn. Dit is wederom een zeer brede verklaring, maar a
testkader kan helpen bij de taak. Zodra een model is ontwikkeld, gevalideerd en
geverifieerd, kunnen we de reeks tests herhaaldelijk uitvoeren om het hele systeem te garanderen
dat het gedurende zijn hele levensduur geldig en geverifieerd blijft.
Wanneer een functie niet meer functioneert zoals bedoeld na een of andere wijziging in het systeem
geïntegreerd, wordt het generiek a genoemd regressie. Oorspronkelijk de term regressie
verwees naar een wijziging die ervoor zorgde dat een eerder opgeloste bug opnieuw verscheen, maar de term heeft
geëvolueerd om elke vorm van verandering te beschrijven die bestaande functionaliteit breekt. Er zijn veel
soorten regressies die in de praktijk kunnen voorkomen.
A lokaal regressie is er een waarin een wijziging rechtstreeks van invloed is op de gewijzigde component. Voor
als een component bijvoorbeeld wordt gewijzigd om geheugen toe te wijzen en vrij te maken, maar verouderde aanwijzers dat wel zijn
gebruikt, faalt het onderdeel zelf.
A vanop regressie is er een waarin een wijziging van één component de functionaliteit onderbreekt
een ander onderdeel. Dit weerspiegelt schending van een impliciete maar mogelijk niet-herkende
overeenkomst tussen componenten.
An ontmaskerd regressie is er een die een situatie creëert waarin een eerder bestaande bug
die geen effect had, wordt plotseling in het systeem weergegeven. Dit kan zo simpel zijn als sporten
voor het eerst een codepad.
A prestatie regressie is er een die ervoor zorgt dat aan de prestatie-eisen van het systeem wordt voldaan
worden geschonden. Bijvoorbeeld wat werk doen in een functie op laag niveau die kan worden herhaald
een groot aantal keren kan het systeem plotseling onbruikbaar maken vanuit bepaalde perspectieven.
De ns-3 testframework biedt tools voor het automatiseren van het proces dat wordt gebruikt om te valideren en
verifieer de code in nachtelijke testsuites om mogelijke regressies snel te identificeren.
Testen kader
ns-3 bestaat uit een simulatie-core-engine, een reeks modellen, voorbeeldprogramma's en tests.
In de loop van de tijd dragen nieuwe bijdragers modellen, tests en voorbeelden bij. Een Python-testprogramma
test.py fungeert als de testuitvoeringsmanager; test.py kan testcode en voorbeelden uitvoeren
zoek naar regressies, kan de resultaten in een aantal vormen uitvoeren en kan code beheren
hulpmiddelen voor dekkingsanalyse. Hierop leggen we lagen Bouw bots die geautomatiseerd zijn gebouwd
robots die robuustheidstesten uitvoeren door het testframework op verschillende systemen uit te voeren
en met verschillende configuratie-opties.
BouwBots
Op het hoogste niveau van ns-3-testen bevinden zich de buildbots (bouwrobots). Als je bent
onbekende met dit systeem kijken http://djmitche.github.com/buildbot/docs/0.7.11/.
Dit is een open-source geautomatiseerd systeem dat het mogelijk maakt ns-3 elk opnieuw te bouwen en te testen
keer dat er iets is veranderd. Door de buildbots op een aantal verschillende systemen te laten draaien, kunnen we
kan ervoor zorgen dat ns-3 bouwt en werkt naar behoren op al zijn ondersteunde systemen.
Gebruikers (en ontwikkelaars) hebben doorgaans geen interactie met het buildbot-systeem, behalve om
lees zijn berichten over testresultaten. Als er een fout wordt gedetecteerd in een van de
geautomatiseerde bouw- en testtaken, stuurt de buildbot een e-mail naar de ns-ontwikkelaars
mailinglijst. Deze e-mail zal er ongeveer zo uitzien
In de URL met volledige details die in de e-mail wordt weergegeven, kan op het trefwoord worden gezocht mislukt en
selecteert u het standaard koppeling voor de overeenkomstige stap om de reden voor de storing te zien.
De buildbot zal zijn werk rustig doen als er geen fouten zijn, en het systeem zal ondergaan
bouw en test elke dag cycli om te controleren of alles in orde is.
Test.py
De buildbots gebruiken een Python-programma, test.py, die verantwoordelijk is voor het runnen van alle
tests en het verzamelen van de resulterende rapporten in een voor mensen leesbare vorm. Dit programma is
ook beschikbaar voor gebruik door gebruikers en ontwikkelaars.
test.py is zeer flexibel in het toestaan van de gebruiker om het aantal en soort tests te specificeren
loop; en ook de hoeveelheid en soort output die moet worden gegenereerd.
Voordat u gaat hardlopen test.py, zorg ervoor dat de voorbeelden en tests van ns3 al doende zijn gebouwd
de volgende
$ ./waf configure --enable-examples --enable-tests
$ ./waf
Standaard test.py zal alle beschikbare tests uitvoeren en de status zeer beknopt rapporteren
formulier. De opdracht uitvoeren
$ ./test.py
zal resulteren in een aantal PASS, FAIL, CRASH or SKIP aanduidingen gevolgd door het soort
test die is uitgevoerd en de weergavenaam.
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' invoeren
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' verlaten
'build' succesvol afgerond (0.939s)
FAIL: TestSuite ns3-wifi-propagation-loss-modellen
PASS: TestSuite objectnaamservice
PASS: TestSuite pcap-bestandsobject
GESLAAGD: TestSuite ns3-tcp-cwnd
...
PASS: TestSuite ns3-tcp-interoperabiliteit
PASS: Voorbeeld csma-uitzending
PASS: Voorbeeld csma-multicast
Deze modus is bedoeld voor gebruik door gebruikers die willen bepalen of hun
distributie correct werkt, en door ontwikkelaars die willen bepalen of
wijzigingen die ze hebben aangebracht hebben geleid tot regressies.
Er zijn een aantal opties beschikbaar om het gedrag van te sturen test.py. als je rent
test.py --help je zou een opdrachtoverzicht moeten zien zoals:
Gebruik: test.py [opties]
Opties:
-h, --help toon dit helpbericht en sluit af
-b BOUWPAD, --bouwpad=BOUWPAD
geef het pad op waar ns-3 is gebouwd (standaard ingesteld op het
build-map voor de huidige variant)
-c SOORT, --constrain=SOORT
beperk de testloper door soort test
-e VOORBEELD, --voorbeeld=VOORBEELD
geef een enkel voorbeeld op om uit te voeren (geen relatief pad is
nodig zijn)
-g, --grind voert de testsuites en voorbeelden uit met behulp van valgrind
-k, --kinds print de soorten tests die beschikbaar zijn
-l, --list druk de lijst met bekende tests af
-m, --multiple rapporteer meerdere fouten van testsuites en test
cases
-n, --nowaf voer waf niet uit voordat u begint met testen
-p PYEXAMPLE, --pyexample=PYEXAMPLE
geef een enkel python-voorbeeld op om uit te voeren (met relative
path)
-r, --retain behoudt alle tijdelijke bestanden (die normaal
verwijderd)
-s TEST-SUITE, --suite=TEST-SUITE
specificeer een enkele testsuite om uit te voeren
-t TEKSTBESTAND, --text=TEKSTBESTAND
schrijf gedetailleerde testresultaten in TEXT-FILE.txt
-v, --verbose afdrukvoortgang en informatieve berichten
-w HTML-BESTAND, --web=HTML-BESTAND, --html=HTML-BESTAND
schrijf gedetailleerde testresultaten in HTML-FILE.html
-x XML-BESTAND, --xml=XML-BESTAND
schrijf gedetailleerde testresultaten in XML-FILE.xml
Als men een optionele uitvoerstijl specificeert, kan men gedetailleerde beschrijvingen van het
testen en status. Beschikbare stijlen zijn tekst en HTML. De buildbots zullen de HTML selecteren
optie om HTML-testrapporten te genereren voor de nachtelijke builds met behulp van
$ ./test.py --html=nachtelijk.html
In dit geval zou een HTML-bestand met de naam ''nightly.html'' worden gemaakt met een mooie samenvatting
van de uitgevoerde testen. Er is een ''door mensen leesbaar'' formaat beschikbaar voor gebruikers die geïnteresseerd zijn in de
details.
$ ./test.py --text=resultaten.txt
In het bovenstaande voorbeeld controleert de testsuite de ns-3 voortplantingsverlies van draadloze apparaten
modellen mislukten. Standaard wordt er geen verdere informatie verstrekt.
Om de storing verder te onderzoeken, test.py maakt het mogelijk om een enkele testsuite te specificeren.
Het uitvoeren van de opdracht
$ ./test.py --suite=ns3-wifi-propagation-loss-modellen
of gelijkwaardig
$ ./test.py -s ns3-wifi-propagation-loss-modellen
resulteert in het uitvoeren van die ene testsuite.
FAIL: TestSuite ns3-wifi-propagation-loss-modellen
Om gedetailleerde informatie over de fout te vinden, moet men het soort output specificeren
gewenst. De meeste mensen zullen bijvoorbeeld waarschijnlijk geïnteresseerd zijn in een tekstbestand:
$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
Dit zal ertoe leiden dat die ene testsuite wordt uitgevoerd met de teststatus geschreven naar het
bestand ''resultaten.txt''.
U zou iets vergelijkbaars met het volgende in dat bestand moeten vinden
MISLUKT: Test Suite ''ns3-wifi-propagation-loss-modellen'' (echte 0.02 gebruiker 0.01 systeem 0.00)
PASS: Testcase "Check ... Friis ... model ..." (reëel 0.01 gebruiker 0.00 systeem 0.00)
MISLUKT: testcase "Controleer ... logafstand ... model" (echte 0.01 gebruiker 0.01 systeem 0.00)
Details:
Bericht: Onverwachte SNR-waarde ontvangen
Conditie: [lange beschrijving van wat er werkelijk is mislukt]
Huidig: 176.395
Limiet: 176.407 +- 0.0005
Bestand: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Line: 360
Merk op dat de Test Suite is samengesteld uit twee Testgevallen. De eerste testcase controleerde de
Friis voortplantingsverliesmodel en geslaagd. De tweede testcase kon het logboek niet controleren
Afstandsvoortplantingsmodel. In dit geval werd een SNR van 176.395 gevonden en de test
verwachtte een waarde van 176.407 correct tot op drie decimalen nauwkeurig. Het bestand dat is geïmplementeerd
de falende test wordt vermeld, evenals de coderegel die de fout veroorzaakte.
Als je wilt, had je net zo goed een HTML-bestand kunnen schrijven met behulp van de --html optie
zoals hierboven beschreven.
Doorgaans voert een gebruiker alle tests minstens één keer uit na het downloaden ns-3 verzekeren dat
zijn of haar omgeving is correct gebouwd en genereert correcte resultaten
volgens de testsuites. Ontwikkelaars zullen de testsuites doorgaans voor en uitvoeren
na het aanbrengen van een wijziging om ervoor te zorgen dat ze geen regressie hebben geïntroduceerd met hun
veranderingen. In dit geval willen ontwikkelaars misschien niet alle tests uitvoeren, maar alleen een subset. Voor
de ontwikkelaar wil bijvoorbeeld de unit-tests alleen periodiek uitvoeren tijdens het maken
wijzigingen in een opslagplaats. In dit geval, test.py kan worden verteld om de soorten te beperken
tests worden uitgevoerd naar een bepaalde testklasse. De volgende opdracht resulteert in alleen
de uitgevoerde unittests:
$ ./test.py --constrain=eenheid
Evenzo zal de volgende opdracht ertoe leiden dat alleen de voorbeeldrooktests worden uitgevoerd:
$ ./test.py --constrain=eenheid
Om een snelle lijst van de wettelijke soorten beperkingen te zien, kunt u vragen om ze op te sommen.
Het volgende commando
$ ./test.py --soorten
zal ertoe leiden dat de volgende lijst wordt weergegeven:
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' invoeren
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' verlaten
'build' is succesvol voltooid (0.939s)Waf: directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' openen
bvt: Build-verificatietests (om te zien of de build met succes is voltooid)
kern: alle op TestSuite gebaseerde tests uitvoeren (exclusief voorbeelden)
voorbeeld: Voorbeelden (om te zien of voorbeeldprogramma's succesvol worden uitgevoerd)
prestaties: prestatietests (controleer of het systeem zo snel is als verwacht)
systeem: Systeemtests (omspant modules om de integratie van modules te controleren)
unit: Unit Tests (binnen modules om basisfunctionaliteit te controleren)
Elk van deze soorten tests kan worden geleverd als een beperking met behulp van de --beperking optie.
Als u een snelle lijst wilt zien van alle beschikbare testsuites, kunt u erom vragen
genoteerd. Het volgende commando,
$ ./test.py --lijst
zal resulteren in een lijst van de testsuite die wordt weergegeven, vergelijkbaar met
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' invoeren
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' verlaten
'build' succesvol afgerond (0.939s)
histogram
ns3-wifi-interferentie
ns3-tcp-cwnd
ns3-tcp-interoperabiliteit
monster
apparaten-gaas-vlam
apparaten-mesh-dot11s
apparaten-mesh
...
objectnaam-service
Bel terug
attributen
config
globale waarde
command-line
basis-willekeurig-nummer
object
Elk van deze vermelde suites kan worden geselecteerd om zelfstandig te worden uitgevoerd met behulp van de --suite optie als
hierboven weergegeven.
Net als bij testsuites kan men een enkel C++-voorbeeldprogramma uitvoeren met behulp van de extensie --voorbeeld
keuze. Merk op dat het relatieve pad voor het voorbeeld niet hoeft te worden opgenomen en dat
de uitvoerbare bestanden die zijn gebouwd voor C++-voorbeelden hebben geen extensies. invoeren
$ ./test.py --example=udp-echo
resulteert in dat ene voorbeeld dat wordt uitgevoerd.
PASS: Voorbeeldvoorbeelden/udp/udp-echo
U kunt de map specificeren waar ns-3 is gebouwd met behulp van de --bouwpad optie als
volgt.
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
Men kan een enkel Python-voorbeeldprogramma uitvoeren met behulp van de --pyvoorbeeld optie. Merk op dat de
relatief pad voor het voorbeeld moet worden opgenomen en dat Python-voorbeelden hun
uitbreidingen. invoeren
$ ./test.py --pyexample=examples/tutorial/first.py
resulteert in dat ene voorbeeld dat wordt uitgevoerd.
PASS: Voorbeeldvoorbeelden/tutorial/first.py
Omdat Python-voorbeelden niet zijn gebouwd, hoeft u de map waarin ns-3
werd gebouwd om ze te besturen.
Wanneer voorbeeldprogramma's worden uitgevoerd, schrijven ze normaal gesproken een grote hoeveelheid traceerbestandsgegevens.
Dit wordt normaal gesproken opgeslagen in de basismap van de distributie (bijv.
/home/gebruiker/ns-3-dev). Wanneer test.py loopt een voorbeeld, het is echt helemaal onbezorgd
met de traceerbestanden. Het wil alleen bepalen of het voorbeeld kan worden gebouwd en uitgevoerd
zonder fouten. Aangezien dit het geval is, worden de traceerbestanden weggeschreven naar een
/tmp/unchecked-traces map. Als u het bovenstaande voorbeeld uitvoert, zou u moeten kunnen vinden
De bijbehorende udp-echo.tr en udp-echo-n-1.pcap bestanden daar.
De lijst met beschikbare voorbeelden wordt bepaald door de inhoud van de map ''examples'' in
de verdeling. Als u een voorbeeld selecteert voor uitvoering met behulp van de --voorbeeld keuze,
test.py zal geen enkele poging doen om te beslissen of het voorbeeld is geconfigureerd of niet, it
zal gewoon proberen het uit te voeren en het resultaat van de poging rapporteren.
. test.py draait, zal het er standaard eerst voor zorgen dat het systeem volledig is geweest
gebouwd. Dit kan worden omzeild door de te selecteren --nuaf optie.
$ ./test.py --list --nowaf
zal resulteren in een lijst van de momenteel gebouwde testsuites die worden weergegeven, vergelijkbaar met:
ns3-wifi-voortplantingsverlies-modellen
ns3-tcp-cwnd
ns3-tcp-interoperabiliteit
pcap-bestandsobject
objectnaam-service
generatoren voor willekeurige getallen
Let op de afwezigheid van de Waf berichten bouwen.
test.py ondersteunt ook het uitvoeren van de testsuites en voorbeelden onder valgrind. Valgrind is een
flexibel programma voor het debuggen en profileren van uitvoerbare Linux-bestanden. Valgrind wordt standaard uitgevoerd
een tool genaamd memcheck, die een reeks geheugencontrolefuncties uitvoert, waaronder
het detecteren van toegang tot niet-geïnitialiseerd geheugen, misbruik van toegewezen geheugen (dubbel vrijmaken,
toegang na gratis, enz.) en het opsporen van geheugenlekken. Dit kan worden geselecteerd met behulp van de
--malen optie.
$ ./test.py --grind
Zoals het loopt, test.py en de programma's die het indirect uitvoert, genereren grote aantallen
tijdelijke bestanden. Meestal is de inhoud van deze bestanden echter niet interessant
gevallen kan het nuttig zijn (voor foutopsporingsdoeleinden) om deze bestanden te bekijken. test.py biedt
--behouden optie die ervoor zorgt dat deze tijdelijke bestanden worden bewaard nadat de run is
voltooid. De bestanden worden opgeslagen in een map met de naam testpy-uitvoer onder een subdirectory
vernoemd naar de huidige Coordinated Universal Time (ook bekend als Greenwich Mean
Tijd).
$ ./test.py --retain
Tenslotte test.py biedt --uitgebreid optie die grote hoeveelheden informatie zal afdrukken
over de voortgang ervan. Het is niet te verwachten dat dit erg nuttig zal zijn, tenzij dat zo is
een fout. In dit geval kunt u toegang krijgen tot de standaarduitvoer en de standaardfout
gerapporteerd door testsuites en voorbeelden uit te voeren. Selecteer breedsprakig op de volgende manier:
$ ./test.py --verbose
Al deze opties kunnen worden gemengd en gematcht. Bijvoorbeeld om de hele ns-3-kern uit te voeren
test suites onder valgrind, in uitgebreide modus, terwijl een HTML-uitvoerbestand wordt gegenereerd, one
zou doen:
$ ./test.py --verbose --grind --constrain=core --html=resultaten.html
TestTaxonomie
Zoals hierboven vermeld, zijn tests gegroepeerd in een aantal ruim gedefinieerde classificaties
stelt gebruikers in staat om selectief tests uit te voeren om de verschillende soorten tests aan te pakken die nodig zijn
te doen.
· Bouw verificatietests
· Eenheidstests
· Systeemtests
· Voorbeelden
· Prestatie testen
BuildVerificatietests
Dit zijn relatief eenvoudige tests die samen met de distributie worden gebouwd en gebruikt
om er zeker van te zijn dat de build behoorlijk goed werkt. Onze huidige eenheidstests leven in de
bronbestanden van de code die ze testen en zijn ingebouwd in de ns-3-modules; en zo passen de
beschrijving van BVT's. BVT's leven in dezelfde broncode die is ingebouwd in de ns-3-code.
Onze huidige tests zijn voorbeelden van dit soort tests.
Eenheid Tests
Unit-tests zijn meer betrokken tests die in detail gaan om er zeker van te zijn dat een stukje code
werkt zoals geadverteerd in isolatie. Er is echt geen reden voor dit soort tests
ingebouwd in een ns-3 module. Het blijkt bijvoorbeeld dat het apparaat voor het object test
naamservice zijn ongeveer even groot als de objectnaamservicecode zelf. Eenheidstests
zijn tests die een enkel stukje functionaliteit controleren dat niet in de ns-3-code is ingebouwd,
maar woon in dezelfde map als de code die wordt getest. Het is mogelijk dat deze tests
controleer ook de integratie van meerdere implementatiebestanden in een module. Het bestand
src/core/test/names-test-suite.cc is een voorbeeld van dit soort testen. Het bestand
src/network/test/pcap-file-test-suite.cc is een ander voorbeeld dat een bekende goede pcap gebruikt
bestand als een test vectorbestand. Dit bestand wordt lokaal opgeslagen in de map src/network.
Systeem Tests
Systeemtests zijn tests waarbij meer dan één module in het systeem betrokken is. We hebben er veel
dit soort tests wordt uitgevoerd in ons huidige regressieraamwerk, maar dat zijn ze meestal
overbelaste voorbeelden. We voorzien een nieuwe plek voor dit soort testen in de directory
src/test. Het bestand src/test/ns3tcp/ns3-interop-test-suite.cc is een voorbeeld van deze soort
van proef. Het gebruikt NSC TCP om de ns-3 TCP-implementatie te testen. Vaak wordt er getest
vectoren die nodig zijn voor dit soort testen, en ze worden opgeslagen in de map waar het
levens testen. NS3tcp-interop-response-vectors.pcap is bijvoorbeeld een bestand dat bestaat uit een
aantal TCP-headers dat wordt gebruikt als de verwachte reacties van de ns-3 TCP die wordt getest
op een stimulus die wordt gegenereerd door de NSC TCP die wordt gebruikt als een ''bekende goede'' implementatie.
Voorbeelden
De voorbeelden worden getest door het framework om er zeker van te zijn dat ze zijn gebouwd en zullen worden uitgevoerd. Niets is
aangevinkt, en momenteel worden de pcap-bestanden gewoon weggeschreven naar / tmp worden weggegooid. Als
de voorbeelden lopen (niet crashen) ze slagen voor deze rooktest.
Prestaties Tests
Prestatietests zijn tests die een bepaald deel van het systeem oefenen en bepalen
als de tests binnen een redelijke tijd volledig zijn uitgevoerd.
Hardlopen Tests
Tests worden meestal uitgevoerd met behulp van het hoge niveau test.py programma. Om een lijst te krijgen van de
beschikbare opdrachtregelopties, run test.py --help
Het testprogramma test.py zal zowel tests uitvoeren als de voorbeelden waaraan zijn toegevoegd
de lijst om te controleren. Het verschil tussen tests en voorbeelden is als volgt. Testen
controleer in het algemeen of specifieke simulatie-output of gebeurtenissen voldoen aan het verwachte gedrag.
De uitvoer van voorbeelden wordt daarentegen niet gecontroleerd en het testprogramma controleert alleen de
exit-status van het voorbeeldprogramma om er zeker van te zijn dat het zonder fouten wordt uitgevoerd.
Kort gezegd, om alle tests uit te voeren, moet men eerst tests configureren tijdens de configuratiefase, en
ook (optioneel) voorbeelden als voorbeelden moeten worden gecontroleerd:
$ ./waf --configure --enable-examples --enable-tests
Bouw dan ns-3, en nadat het gebouwd is, ren gewoon test.py. test.py -h zal een nummer laten zien
van configuratie-opties die het gedrag van test.py wijzigen.
Het programma test.py roept voor C++-tests en -voorbeelden een C++-programma op een lager niveau aan met de naam
testloper om de tests daadwerkelijk uit te voeren. Zoals hieronder besproken, dit testloper kan een
handige manier om tests te debuggen.
Debugging Tests
Het debuggen van de testprogramma's kan het beste worden uitgevoerd met de low-level testrunner
programma. De testrunner is de brug van generieke Python-code naar ns-3 code. Het is
geschreven in C++ en gebruikt het automatische testontdekkingsproces in de ns-3 code om te vinden en
maken het mogelijk om alle verschillende tests uit te voeren.
De belangrijkste reden waarom test.py niet geschikt is om te debuggen, is dat het niet is toegestaan
logging in te schakelen met behulp van de NS_LOG omgevingsvariabele wanneer test.py wordt uitgevoerd. Dit
beperking is niet van toepassing op het uitvoerbare bestand van de testrunner. Dus als u logging wilt zien
uitvoer van uw tests, u moet ze rechtstreeks uitvoeren met behulp van de testrunner.
Om de testrunner uit te voeren, voer je het uit zoals elk ander ns-3-uitvoerbaar bestand - met behulp van
waf. Typ het volgende om een lijst met beschikbare opties te krijgen:
$ ./waf --run "testrunner --help"
Je zou zoiets als het volgende moeten zien
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' invoeren
Waf: map `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build' verlaten
'build' succesvol afgerond (0.353s)
--assert: Vertel tests om fout te scheiden (zoals assert) als er een fout wordt gedetecteerd
--basedir=dir: stel de basismap (waar src te vinden is) in op ''dir''
--tempdir=dir: Stel de tijdelijke map (waar gegevensbestanden te vinden zijn) in op ''dir''
--constrain=test-type: controles beperken tot testsuites van het type ''test-type''
--help: druk dit bericht af
--kinds: Maak een lijst van alle beschikbare soorten tests
--list: maak een lijst van alle testsuites (optioneel beperkt door testtype)
--out=bestandsnaam: Stel het uitvoerbestand van de teststatus in op ''bestandsnaam''
--suite=suite-name: voer de testsuite met de naam ''suite-name'' uit
--verbose: schakel berichten in de run-testsuites in
Er zijn een aantal dingen voor u beschikbaar die u bekend zullen voorkomen als u die heeft
keek test.py. Dit is te verwachten aangezien de testrunner slechts een interface is
tussen test.py en ns-3. Het is u misschien opgevallen dat voorbeeldgerelateerde opdrachten hier ontbreken.
Dat komt omdat de voorbeelden dat echt niet zijn ns-3 testen. test.py voert ze uit alsof ze dat waren
om een uniforme testomgeving te presenteren, maar ze zijn echt totaal anders en niet
hier te vinden.
De eerste nieuwe optie die hier verschijnt, maar niet in test.py is de --beweren optie. Deze
optie is handig bij het debuggen van een testcase wanneer deze wordt uitgevoerd onder een debugger zoals gdb. Wanneer
geselecteerd, vertelt deze optie de onderliggende testcase om een segmentatieovertreding te veroorzaken als
er wordt een fout gedetecteerd. Dit heeft het leuke neveneffect dat de uitvoering van het programma stopt
(inbreken in de debugger) wanneer er een fout wordt gedetecteerd. Als je gdb gebruikt, zou je kunnen gebruiken
deze optie iets als,
$ ./waf-schaal
$ cd build/debug/utils
$ gdb testloper
$ run --suite=global-value --assert
Als er vervolgens een fout wordt gevonden in de testsuite met globale waarden, wordt er een segfault gegenereerd
en de debugger (bronniveau) zou stoppen bij het NS_TEST_ASSERT_MSG dat ontdekte de
fout.
Een andere nieuwe optie die hier verschijnt, is de --basir keuze. Het blijkt dat sommige
tests moeten mogelijk verwijzen naar de bronmap van het ns-3 distributie om lokaal te vinden
data, dus er is altijd een basisdirectory nodig om een test uit te voeren.
Als u een test uitvoert vanuit test.py, biedt het Python-programma de optie basedir voor
Jij. Om een van de tests rechtstreeks vanuit de testloper uit te voeren met behulp van waf, je zal moeten
specificeer de testsuite die samen met de basismap moet worden uitgevoerd. Dus je zou de schaal kunnen gebruiken
en doe:
$ ./waf --run "testrunner --basedir=`pwd` --suite=pcap-file-object"
Let op de ''achterwaartse'' aanhalingstekens op de pwd opdracht.
Als u de testsuite vanuit een debugger uitvoert, kan het behoorlijk pijnlijk zijn om te onthouden
en typ constant het absolute pad van de basismap van de distributie. Vanwege
dit, als je de basedir weglaat, zal de testrunner proberen er een voor je uit te zoeken. Het
begint in de huidige werkdirectory en loopt omhoog in de directorystructuur op zoek naar een
directory-bestand met bestanden genaamd VERSIE en LICENTIE. Als het er een vindt, gaat het ervan uit dat
moet de basedir zijn en levert het voor u.
Test uitvoer
Veel testsuites moeten tijdens het proces tijdelijke bestanden (zoals pcap-bestanden) schrijven
het uitvoeren van de testen. De tests hebben dan een tijdelijke directory nodig om naar te schrijven. De python
het testhulpprogramma (test.py) levert automatisch een tijdelijk bestand, maar als het stand-alone wordt uitgevoerd
deze tijdelijke map moet worden opgegeven. Net als in het geval van hun, kan het zijn
vervelend om voortdurend een --tempdir, dus de testloper zal er een bedenken
voor u uit als u er geen verstrekt. Het zoekt eerst naar omgevingsvariabelen named TMP
en TEMP en gebruikt die. Als geen van beide TMP noch TEMP zijn gedefinieerd die het kiest / tmp. De code
plakt vervolgens op een identifier die aangeeft wat de map heeft gemaakt (ns-3) en vervolgens de tijd
(uu.mm.ss) gevolgd door een groot willekeurig getal. De testloper maakt daar een map van
naam die moet worden gebruikt als de tijdelijke map. Tijdelijke bestanden gaan dan naar een map die
zal zoiets heten
/tmp/ns-3.10.25.37.61537845
De tijd wordt als hint gegeven zodat je relatief eenvoudig kunt reconstrueren wat
directory werd gebruikt als je terug moet gaan om de bestanden te bekijken die daarin zijn geplaatst
directory.
Een andere uitvoerklasse is testuitvoer zoals pcap-sporen die worden gegenereerd om mee te vergelijken
referentie uitgang. Het testprogramma zal deze meestal verwijderen nadat de testsuites allemaal zijn voltooid
loop. Voer uit om het verwijderen van testuitvoer uit te schakelen test.py met de optie "behouden":
$ ./test.py -r
en testuitvoer is te vinden in de testpy-uitvoer/ directory.
Rapportage of proef mislukkingen
Wanneer u een testsuite uitvoert met behulp van de testrunner, wordt de test standaard stil uitgevoerd.
De enige indicatie dat u krijgt dat de test geslaagd is, is de afwezigheid van een bericht
vanaf waf zeggend dat het programma iets anders retourneerde dan een nul-exitcode. Krijgen
sommige uitvoer van de test, moet u een uitvoerbestand specificeren waarnaar de tests zullen
schrijven hun XML-status met behulp van de --uit keuze. Je moet voorzichtig zijn met het interpreteren van de
resultaten omdat de testsuites dat zullen doen toevoegen resultaten naar dit bestand. Poging,
$ ./waf --run "testrunner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"
Als je naar het bestand kijkt mijnbestand.xml je zou zoiets moeten zien,
pcap-bestandsobject
Controleer of PcapFile::Open met modus ''w'' werkt
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Controleer of PcapFile::Open met modus ''r'' werkt
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Controleer of PcapFile::Open met modus ''a'' werkt
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Controleer of PcapFileHeader correct wordt beheerd
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Controleer of PcapRecordHeader correct wordt beheerd
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Controleer of PcapFile een bekend goed pcap-bestand kan uitlezen
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
DOORGANG
echt 0.00 gebruiker 0.00 systeem 0.00
Als u bekend bent met XML, zou dit vrij vanzelfsprekend moeten zijn. Het is ook geen
compleet XML-bestand aangezien testsuites zijn ontworpen om hun uitvoer aan een master te laten toevoegen
XML-statusbestand zoals beschreven in de test.py pagina.
Debugging proef suite mislukkingen
Om testcrashes te debuggen, zoals
CRASH: TestSuite ns3-wifi-interferentie
U kunt als volgt toegang krijgen tot het onderliggende testrunnerprogramma via gdb en vervolgens de
"--basedir=`pwd`" argument om uit te voeren (u kunt indien nodig ook andere argumenten doorgeven, maar
basedir is het minimaal benodigde):
$ ./waf --command-template="gdb %s" --run "testrunner"
Waf: map `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build' invoeren
Waf: map `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build' verlaten
'build' succesvol afgerond (0.380s)
GNU gdb 6.8-debian
Auteursrecht (C) 2008 Free Software Foundation, Inc.
L cense GPLv3+: GNU GPL versie 3 of laterhttp://gnu.org/licenses/gpl.html>
Dit is gratis software: u bent vrij om deze te wijzigen en opnieuw te verspreiden.
Er is GEEN GARANTIE, voor zover toegestaan door de wet. Typ "toon kopiëren"
en "toon garantie" voor details.
Deze GDB is geconfigureerd als "x86_64-linux-gnu"...
(gdb) r --basedir=`pwd`
Startprogramma: <..>/build/debug/utils/test-runner --basedir=`pwd`
[Thread debugging met behulp van libthread_db ingeschakeld]
beweren mislukt. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...
Hier is nog een voorbeeld van het gebruik van valgrind om een geheugenprobleem te debuggen, zoals:
VALGR: TestSuite apparaten-mesh-dot11s-regressie
$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --run test-runner
Klasse TestRunner
De uitvoerbare bestanden die speciale testprogramma's uitvoeren, gebruiken een klasse TestRunner. Deze klas
voorziet in automatische testregistratie en -listing, evenals een manier om de test uit te voeren
individuele testen. Individuele testsuites gebruiken C++ global constructors om zichzelf aan toe te voegen
een verzameling testsuites beheerd door de testloper. De testloper wordt gebruikt om op te noemen
alle beschikbare tests en om een uit te voeren test te selecteren. Dit is een vrij eenvoudige klasse
dat drie statische methoden biedt om testsuites aan te bieden of toe te voegen en te verkrijgen aan een
verzameling testen. Zie de doxygen voor klasse ns3::TestRunner voor meer info.
Test Suite
Alles ns-3 tests zijn ingedeeld in testsuites en testcases. Een testpakket is een
verzameling testgevallen die een bepaald soort functionaliteit volledig uitoefenen. Als
hierboven beschreven, kunnen testsuites worden geclassificeerd als,
· Bouw verificatietests
· Eenheidstests
· Systeemtests
· Voorbeelden
· Prestatie testen
Deze classificatie wordt geëxporteerd vanuit de klasse TestSuite. Deze les is vrij eenvoudig,
alleen bestaand als een plek om dit type te exporteren en testgevallen te verzamelen. Van een gebruiker
perspectief, om een nieuwe TestSuite in het systeem aan te maken hoeft men alleen maar een nieuwe te definiëren
klasse die erft van klasse Test pak en voer deze twee taken uit.
De volgende code definieert een nieuwe klasse die kan worden uitgevoerd door test.py als een ''eenheid''-test
met de weergegeven naam, mijn-testsuite-naam.
class MySuite : openbare TestSuite
{
publiek:
MijnTestSuite ();
};
MijnTestSuite::MijnTestSuite ()
: TestSuite ("mijn-testsuite-naam", UNIT)
{
AddTestCase (nieuwe MyTestCase);
}
MijnTestSuite mijnTestSuite;
De basisklasse zorgt voor alle registratie en rapportage die nodig zijn om een goed te zijn
burger in het toetsingskader.
Test SITUATIE
Individuele tests worden gemaakt met behulp van een TestCase-klasse. Gangbare modellen voor het gebruik van een toets
case bevatten "één testcase per functie" en "één testcase per methode". Mengsels van
deze modellen mogen gebruikt worden.
Om een nieuwe testcase in het systeem te creëren, hoeft u alleen maar te erven van de
Testcase base class, de constructor overschrijven om de testcase een naam te geven en overschrijven
the DoRun methode om de test uit te voeren.
klasse MyTestCase: openbare TestCase
{
MijnTestCase ();
virtuele leegte DoRun (leegte);
};
MijnTestCase::MijnTestCase ()
: TestCase ("Controleer een stukje functionaliteit")
{
}
komen te vervallen
MyTestCase::DoRun (ongeldig)
{
NS_TEST_ASSERT_MSG_EQ (waar, waar, "Een foutbericht");
}
Nutsbedrijven
Er zijn een aantal hulpprogramma's van verschillende soorten die ook deel uitmaken van het testen
kader. Voorbeelden zijn onder meer een gegeneraliseerd pcap-bestand dat handig is voor het opslaan van testvectoren; A
generieke container die nuttig is voor tijdelijke opslag van testvectoren tijdens testuitvoering; En
tools voor het genereren van presentaties op basis van validatie- en verificatietestresultaten.
Deze hulpprogramma's worden hier niet beschreven, maar bekijk bijvoorbeeld hoe TCP test
gevonden in src/test/ns3tcp/ gebruik pcap-bestanden en referentie-uitvoer.
Hoe naar schrijven testen
Een primair doel van het ns-3-project is om gebruikers te helpen de validiteit en
geloofwaardigheid van hun resultaten. Er zijn veel elementen voor het verkrijgen van geldige modellen en
simulaties en testen is een belangrijk onderdeel. Als u modellen of voorbeelden aandraagt
ns-3, wordt u mogelijk gevraagd om testcode bij te dragen. Modellen die u bijdraagt, worden gebruikt
voor vele jaren door andere mensen, die waarschijnlijk op het eerste gezicht geen idee hebben of de
model klopt. De testcode die u voor uw model schrijft, helpt toekomst te voorkomen
regressies in de uitvoer en zal toekomstige gebruikers helpen bij het begrijpen van de verificatie en
grenzen van de toepasbaarheid van uw modellen.
Er zijn veel manieren om de juistheid van de implementatie van een model te verifiëren. In deze
sectie, hopen we enkele veelvoorkomende gevallen te behandelen die kunnen worden gebruikt als leidraad voor het schrijven van nieuwe
testen.
Sample Test pak skelet
Wanneer u helemaal opnieuw begint (dwz geen TestCase toevoegen aan een bestaande TestSuite), zijn deze
dingen moeten van tevoren worden beslist:
· Hoe de testsuite gaat heten
· Welk type test het zal zijn (Build Verification Test, Unit Test, System Test of
Prestatie test)
· Waar de testcode zal leven (in een bestaande ns-3-module of apart in
src/test/map). U moet het wscript-bestand in die map bewerken om
compileer uw nieuwe code, als het een nieuw bestand is.
Een programma genaamd src/create-module.py is een goed uitgangspunt. Dit programma kan zijn
aangeroepen zoals create-module.py router voor een hypothetische nieuwe module genaamd router. Eenmaal
je dit doet, zie je een router map, en een test/router-testsuite.cc test pak.
Dit bestand kan een startpunt zijn voor uw eerste test. Dit is een werkende testsuite,
hoewel de feitelijk uitgevoerde tests triviaal zijn. Kopieer het naar de test van uw module
directory, en doe een globale vervanging van "Router" in dat bestand voor iets dat betrekking heeft
naar het model dat u wilt testen. U kunt ook dingen bewerken, zoals een meer beschrijvende
testcase naam.
Je moet ook een blok toevoegen aan je wscript om deze test te laten compileren:
module_test.bron = [
'test/router-testsuite.cc',
]
Voordat u daadwerkelijk begint om dit nuttige dingen te laten doen, kan het helpen om te proberen het
skelet. Zorg ervoor dat ns-3 is geconfigureerd met de optie "--enable-tests".
Laten we aannemen dat uw nieuwe testsuite "router" wordt genoemd, zoals hier:
RouterTestSuite::RouterTestSuite ()
: TestSuite ("router", EENHEID)
Probeer deze opdracht:
$ ./test.py -s router
Uitvoer zoals hieronder moet worden geproduceerd:
PASS: TestSuite-router
1 van 1 tests geslaagd (1 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Zie src/lte/test/test-lte-antenna.cc voor een uitgewerkt voorbeeld.
Test macro's
Er zijn een aantal macro's beschikbaar voor het controleren van de testprogramma-uitvoer met verwacht
uitgang. Deze macro's zijn gedefinieerd in src/core/model/test.h.
De belangrijkste set macro's die worden gebruikt, zijn de volgende:
NS_TEST_ASSERT_MSG_EQ(actueel, limiet, bericht)
NS_TEST_ASSERT_MSG_NE(actueel, limiet, bericht)
NS_TEST_ASSERT_MSG_LT(actueel, limiet, bericht)
NS_TEST_ASSERT_MSG_GT(actueel, limiet, bericht)
NS_TEST_ASSERT_MSG_EQ_TOL(actueel, limiet, tol, bericht)
Het eerste argument daadwerkelijk is de waarde die wordt getest, de tweede waarde begrenzing is het verwachte
waarde (of de waarde waarmee moet worden getest) en het laatste argument msg is de foutmelding
afdrukken als de test mislukt.
De eerste vier bovenstaande macro's testen op gelijkheid, ongelijkheid, kleiner dan of groter dan
respectievelijk. De vijfde macro hierboven test op gelijkheid, maar binnen een bepaalde tolerantie.
Deze variant is handig bij het testen van getallen met drijvende komma voor gelijkheid tegen een limiet,
waar u een testfout als gevolg van afrondingsfouten wilt voorkomen.
Tot slot zijn er nog varianten van bovenstaande waar het zoekwoord in voorkomt BEWEREN is vervangen door VERWACHTEN.
Deze varianten zijn speciaal ontworpen voor gebruik bij methoden (met name callbacks) die terugkeren
leegte. Reserveer het gebruik ervan voor callbacks die u in uw testprogramma's gebruikt; gebruik anders
the BEWEREN varianten.
Hoe naar toevoegen an voorbeeld programma naar the proef suite
Men kan "rook testen" dat voorbeelden compileren en met succes worden voltooid (zonder
geheugenlekken) met behulp van de voorbeelden-to-run.py script in de testdirectory van uw module.
Kort gezegd, door een instantie van dit bestand in uw testdirectory op te nemen, kunt u de
testloper om de genoemde voorbeelden uit te voeren. Het is meestal het beste om ervoor te zorgen dat u
selecteer voorbeelden met redelijk korte doorlooptijden om de tests niet te vertragen. Zien
het voorbeeld erin src/lte/test/ directory.
Testen besteld, boolean resultaten
Testen resultaten wanneer willekeurigheid is betrokken zijn
Testen uitvoer gegevens tegen a bekend distributie
Het verstrekken van niet triviaal invoer vectoren of gegevens
Hoe bewaart u en referencing niet triviaal uitvoer gegevens
Presenteren jouw uitvoer proef gegevens
Support
Wij creëren a nieuwe ns-3 model
Dit hoofdstuk loopt door het ontwerpproces van een ns-3 model. In veel onderzoekscases
gebruikers zullen niet tevreden zijn met het alleen maar aanpassen van bestaande modellen, maar willen misschien de
kern van de simulator op een nieuwe manier. We zullen het voorbeeld gebruiken van het toevoegen van een ErrorModel aan een
simpel ns-3 link als een motiverend voorbeeld van hoe men dit probleem zou kunnen aanpakken en
doorloop een ontwerp en implementatie.
NOTITIE:
Documentatie
Hier concentreren we ons op het proces van het maken van nieuwe modellen en nieuwe modules, en enkele van de
ontwerpkeuzes betrokken. Omwille van de duidelijkheid stellen we de bespreking van de mechanica
van het documenteren van modellen en broncode aan de Documentatie hoofdstuk.
Design Aanpak
Bedenk hoe u wilt dat het werkt; wat moet het doen. Denk aan deze dingen:
· functionaliteit: Welke functionaliteit moet het hebben? Wat attributen of configuratie is
blootgesteld aan de gebruiker?
· herbruikbaarheid: Hoeveel mogen anderen mijn ontwerp hergebruiken? Kan ik code hergebruiken van
ns-2 starten? Hoe integreert een gebruiker het model met de rest van een ander
simulatie?
· afhankelijkheden: Hoe kan ik de introductie van externe afhankelijkheden op mijn nieuwe code verminderen
zoveel mogelijk (om het meer modulair te maken)? Moet ik er bijvoorbeeld een vermijden
afhankelijkheid van IPv4 als ik wil dat het ook door IPv6 wordt gebruikt? Moet ik elke afhankelijkheid vermijden
helemaal op IP?
Aarzel dan niet om contact op te nemen met de ns-3-gebruikers or ns-ontwikkelaars lijst als je vragen hebt.
Het is met name belangrijk om na te denken over de openbare API van uw nieuwe model en ernaar te vragen
feedback. Het helpt ook om anderen op de hoogte te stellen van uw werk voor het geval u hierin geïnteresseerd bent
medewerkers.
Voorbeeld: Foutmodel
Er bestaat een foutmodel in ns-2. Hiermee kunnen pakketten worden doorgegeven aan een stateful object dat
bepaalt op basis van een willekeurige variabele of het pakket beschadigd is. De beller kan
beslis vervolgens wat u met het pakket gaat doen (laat het vallen, enz.).
De hoofd-API van het foutmodel is een functie om een pakket aan door te geven, en de retourwaarde van
deze functie is een booleaanse waarde die de beller vertelt of er corruptie is opgetreden. Opmerking
dat, afhankelijk van het foutmodel, de pakketgegevensbuffer al dan niet beschadigd is.
Laten we deze functie "IsCorrupt()" noemen.
Tot nu toe hebben we in ons ontwerp:
klasse ErrorModel
{
publiek:
/ **
* \geeft waar terug als het pakket moet worden beschouwd als foutief/beschadigd
* \param pkt Pakket om foutmodel op toe te passen
*/
bool IsCorrupt (Ptr pkt);
};
Merk op dat we geen const-pointer doorgeven, waardoor de functie de
pakket als IsCorrupt() waar retourneert. Niet alle foutmodellen zullen het pakket daadwerkelijk wijzigen;
of de pakketgegevensbuffer al dan niet beschadigd is, moet worden gedocumenteerd.
Mogelijk willen we ook gespecialiseerde versies hiervan, zoals in ns-2, dus hoewel het niet de
enige ontwerpkeuze voor polymorfisme, gaan we ervan uit dat we een basisklasse subklassen
ErrorModel voor gespecialiseerde klassen, zoals RateErrorModel, ListErrorModel, enz., zoals
is gedaan in ns-2.
U denkt nu misschien: "Waarom zou u IsCorrupt() niet een virtuele methode maken?". Dat is
één benadering; de andere is om de openbare niet-virtuele functie indirect te maken door middel van a
private virtuele functie (dit staat in C++ bekend als het niet-virtuele interface-idioom en is
aangenomen in de ns-3 ErrorModel-klasse).
Moet dit apparaat vervolgens afhankelijk zijn van IP of andere protocollen? We willen niet
om afhankelijkheden van internetprotocollen te creëren (het foutmodel moet van toepassing zijn op
ook niet-internetprotocollen), dus daar houden we later rekening mee.
Een andere overweging is hoe objecten dit foutmodel zullen bevatten. We stellen ons voor om te zetten
een expliciete setter in bepaalde NetDevice-implementaties, bijvoorbeeld:
/ **
* Koppel een Receive ErrorModel aan het PointToPointNetDevice.
*
* Het PointToPointNetDevice kan optioneel een ErrorModel bevatten
* de pakketontvangstketen.
*
* @zie ErrorModel
* @param em Ptr naar het ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr em);
Nogmaals, dit is niet de enige keuze die we hebben (foutmodellen kunnen worden samengevoegd tot vele
andere objecten), maar het voldoet aan onze primaire use case, namelijk een gebruiker toestaan te forceren
fouten bij verder succesvolle pakkettransmissies, op NetDevice-niveau.
Na wat nadenken en kijken naar bestaande ns-2 code, hier is een voorbeeld-API van een basis
klasse en eerste subklasse die kunnen worden gepost voor eerste beoordeling:
klasse ErrorModel
{
publiek:
FoutModel ();
virtueel ~ErrorModel ();
bool IsCorrupt (Ptr pkt);
nietig Reset (nietig);
ongeldig Inschakelen (nietig);
nietig Uitschakelen (nietig);
bool IsEnabled (nietig) const;
private:
virtuele bool DoCorrupt (Ptr pkt) = 0;
virtuele leegte DoReset (leegte) = 0;
};
enum ErrorUnit
{
EU_BIT,
EU_BYTE,
EU_PKT
};
// Bepaal welke pakketten een fout vertonen die overeenkomt met een onderliggend
// distributie van willekeurige variabelen, een foutenpercentage en eenheid voor het percentage.
klasse RateErrorModel: openbaar ErrorModel
{
publiek:
RateErrorModel ();
virtueel ~RateErrorModel ();
enum ErrorUnit GetUnit (nietig) const;
ongeldig SetUnit (enum ErrorUnit error_unit);
dubbele GetRate (nietig) const;
ongeldig SetRate (dubbel tarief);
ongeldig SetRandomVariable (const RandomVariable &ranvar);
private:
virtuele bool DoCorrupt (Ptr pkt);
virtuele leegte DoReset (leegte);
};
steiger
Laten we zeggen dat u klaar bent om te beginnen met implementeren; je hebt er een vrij duidelijk beeld van
wat u wilt bouwen, en misschien heeft u om een eerste beoordeling of suggesties gevraagd
de lijst. Een manier om de volgende stap (implementatie) te benaderen is het creëren van steigers en
vul de details in terwijl het ontwerp rijpt.
In dit gedeelte worden veel van de stappen beschreven die u zou moeten overwegen om steigers te definiëren, of
een niet-functioneel skelet van wat uw model uiteindelijk zal implementeren. Het is meestal goed
oefen om niet te wachten om deze details aan het eind geïntegreerd te krijgen, maar om in plaats daarvan a te loodsen
skelet van uw model vroeg in het systeem en voeg later functies toe zodra de API en
integratie lijkt ongeveer goed.
Merk op dat u een paar dingen in de onderstaande presentatie voor uw model wilt wijzigen
want als je het foutmodel letterlijk volgt, zal de code die je produceert in botsing komen met de
bestaand foutenmodel. Het onderstaande is slechts een schets van hoe ErrorModel is gebouwd dat u
kan aanpassen aan andere modellen.
Beoordeling the ns-3 codering Style Document
Op dit punt wilt u misschien even pauzeren en de ns-3 coderingsstijldocument, vooral
als u overweegt om uw code terug te dragen aan het project. De codeerstijl
document is gekoppeld aan de hoofdprojectpagina: ns-3 codering stijl.
Beslissen Waar in the Bron Boom the Model Moeten Wonen
Alle ns-3 modelbroncode bevindt zich in de directory src /. U moet kiezen welke
subdirectory waarin het zich bevindt. Als het een of andere nieuwe modelcode is, is het logisch om het te plaatsen
in de src / directory ergens, met name voor het gemak van integratie met de build
systeem.
In het geval van het foutenmodel is het erg gerelateerd aan de pakketklasse, dus het is logisch
om dit door te voeren in de src/netwerk/ module waar ns-3 pakketten worden uitgevoerd.
waf en wscript
ns-3 gebruikt Waf bouw systeem. U wilt uw nieuwe integreren ns-3 maakt gebruik van de Waf
bouw systeem. U zult uw nieuwe bronbestanden in dit systeem willen integreren. Dit
vereist dat u uw bestanden toevoegt aan het wscript bestand gevonden in elke map.
Laten we beginnen met lege bestanden error-model.h en error-model.cc, en dit toevoegen aan
src/netwerk/wscript. Het is eigenlijk gewoon een kwestie van het .cc-bestand toevoegen aan de rest van het
bronbestanden en het .h-bestand naar de lijst met headerbestanden.
Ga nu naar de map op het hoogste niveau en typ "./test.py". Je had niet moeten breken
alles door deze operatie.
omvatten bewakers
Laten we er vervolgens wat aan toevoegen omvatten bewakers in ons headerbestand.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
...
#stop als
namespace ns3
ns-3 gebruikt ns-3 namespace om zijn symbolen te isoleren van andere naamruimten. Typisch, een
gebruiker zal vervolgens een ns-3 naamruimteblok in zowel het cc- als het h-bestand.:
naamruimte ns3 {
...
}
Op dit moment hebben we enkele skeletbestanden waarin we onze nieuwe klassen kunnen definiëren.
Het headerbestand ziet er als volgt uit:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
naamruimte ns3 {
} // naamruimte ns3
#stop als
terwijl de error-model.cc bestand ziet er gewoon zo uit:
#include "foutmodel.h"
naamruimte ns3 {
} // naamruimte ns3
Deze bestanden zouden moeten worden gecompileerd omdat ze niet echt inhoud hebben. We zijn er nu klaar voor
begin met het toevoegen van klassen.
Eerste Implementatie
Op dit moment werken we nog aan wat steigers, maar we kunnen beginnen met het definiëren van onze
klassen, met de functionaliteit die later wordt toegevoegd.
erven vanaf the Object Klas?
Dit is een belangrijke ontwerpstap; of je klasse moet gebruiken Object als basisklasse voor je nieuwe
klassen.
Zoals beschreven in het hoofdstuk over de ns-3 Objectmodel, klassen die erven van klasse
Object krijg speciale eigenschappen:
· de ns-3 type en attribuutsysteem (zie Attributen)
· een objectaggregatiesysteem
· een smart-pointer referentietelsysteem (klasse Ptr)
Klassen die voortkomen uit klasse ObjectBase} krijg de eerste twee eigenschappen hierboven, maar niet
krijg slimme aanwijzingen. Klassen die voortkomen uit klasse RefCountBase krijg alleen de slimme aanwijzer
referentie telsysteem.
In de praktijk klasse Object is de variant van de drie hierboven die de ns-3 ontwikkelaar zal
het meest tegenkomen.
In ons geval willen we gebruik maken van het attribuutsysteem, en we zullen instanties doorgeven
van dit object over de ns-3 openbare API, dus klasse Object past bij ons.
Eerste Klassen
Een manier om verder te gaan is om te beginnen met het definiëren van de absolute minimumfuncties en te kijken of ze dat zullen doen
compileren. Laten we eens kijken wat er allemaal nodig is om te implementeren wanneer we afleiden van klasse Object.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
#include "ns3/object.h"
naamruimte ns3 {
klasse ErrorModel: openbaar object
{
publiek:
statisch TypeId GetTypeId (ongeldig);
FoutModel ();
virtueel ~ErrorModel ();
};
klasse RateErrorModel: openbaar ErrorModel
{
publiek:
statisch TypeId GetTypeId (ongeldig);
RateErrorModel ();
virtueel ~RateErrorModel ();
};
#stop als
Een paar dingen om hier op te merken. We moeten opnemen object.h. De conventie in ns-3 is dat als?
het header-bestand zich in dezelfde map bevindt, kan zonder enig pad worden opgenomen
voorvoegsel. Daarom, als we ErrorModel zouden implementeren in src/kern/model map, wij
had net kunnen zeggen "#include "object.h"". Maar we zijn binnen src/netwerk/model, dus dat moeten we
voeg het toe als "#include "ns3/object.h"". Merk ook op dat dit buiten de naamruimte valt
verklaring.
Ten tweede moet elke klasse een statische openbare lidfunctie genaamd implementeren GetTypeId (leegte).
Ten derde is het een goed idee om constructors en destructors te implementeren in plaats van de
compiler om ze te genereren en om de destructor virtueel te maken. Let in C++ ook op dat copy
toewijzingsoperator en kopieerconstructors worden automatisch gegenereerd als ze niet zijn gedefinieerd, dus
als je die niet wilt, moet je die als particulier lid implementeren. Dit aspect van
C++ wordt besproken in het boek Effective C++ van Scott Meyers. artikel 45.
Laten we nu eens kijken naar een overeenkomstige skeletimplementatiecode in het .cc-bestand.:
#include "foutmodel.h"
naamruimte ns3 {
NS_OBJECT_ENSURE_REGISTERED (Foutmodel);
TypeId ErrorModel::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::ErrorModel")
.SetOuder ()
;
tijd teruggeven;
}
Foutmodel::ErrorModel ()
{
}
Foutmodel::~ErrorModel ()
{
}
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
TypeId RateErrorModel::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::RateErrorModel")
.SetOuder ()
.Constructor toevoegen ()
;
tijd teruggeven;
}
RateErrorModel::RateErrorModel ()
{
}
RateErrorModel::~RateErrorModel ()
{
}
Wat is het GetTypeId (leegte) functie? Deze functie doet een aantal dingen. Het registreert een
unieke tekenreeks in het TypeId-systeem. Het bepaalt de hiërarchie van objecten in de
attribuutsysteem (via Ouder instellen). Het verklaart ook dat bepaalde objecten kunnen worden gemaakt via
het raamwerk voor het maken van objecten (Constructeur toevoegen).
de macro NS_OBJECT_ENSURE_REGISTERED (naam van de klasse) is ook een keer nodig voor elke klas die
definieert een nieuwe GetTypeId-methode en doet de daadwerkelijke registratie van de klasse in de
systeem. In het hoofdstuk Objectmodel wordt hier nader op ingegaan.
Inclusief Extern Bestanden
Logging Support
Hier schrijven a beetje over toe te voegen |ns3| logging macro's. Note uit die LOG_COMPONENT_DEFINE is
gedaan buiten the namespace ns3
Constructeur, Leeg Functie Prototypes
sleutel Variabelen (Standaard Waarden, attributen)
Test Programma 1
Object Kader
Het toevoegen van a Sample Script
Op dit punt kan men proberen de hierboven gedefinieerde basissteiger te nemen en toe te voegen
in het systeem. Door deze stap nu uit te voeren, kan men een eenvoudiger model gebruiken bij het loodgieterswerk
in het systeem en kan ook onthullen of er ontwerp- of API-aanpassingen nodig zijn
gemaakt. Zodra dit is gebeurd, gaan we terug naar het uitbouwen van de functionaliteit van het
ErrorModels zelf.
Toevoegen Basic Support in the Klasse
/* point-to-point-net-apparaat.h */
klasse ErrorModel;
/ **
* Foutmodel voor ontvangstpakketgebeurtenissen
*/
Ptr m_receiveErrorModel;
Toevoegen accessoire
komen te vervallen
PointToPointNetDevice::SetReceiveErrorModel (Ptr em)
{
NS_LOG_FUNCTION (deze << em);
m_receiveErrorModel = em;
}
.AddAttribute ("ErrorModel ontvangen",
"Het ontvangerfoutmodel dat wordt gebruikt om pakketverlies te simuleren",
PointerWaarde (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MaakPointerChecker ())
leiden In the Systeem
ongeldig PointToPointNetDevice::Ontvangen (Ptr pakket)
{
NS_LOG_FUNCTION (dit << pakket);
uint16_t-protocol = 0;
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (pakket) )
{
//
// Als we een foutenmodel hebben en het geeft aan dat het tijd is om a
// beschadigd pakket, stuur dit pakket niet door, laat het gaan.
//
m_dropTrace (pakket);
}
anders
{
//
// Druk op de ontvang-traceerhaak, verwijder de point-to-point protocolheader
// en stuur dit pakket door naar de protocolstack.
//
m_rxTrace (pakket);
ProcessHeader(pakket, protocol);
m_rxCallback (dit, pakket, protocol, GetRemote ());
als (!m_promiscCallback.IsNull ())
{ m_promiscCallback (dit, pakket, protocol, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}
creëren Null Functioneel Script
/* eenvoudig-fout-model.cc */
// Foutmodel
// We willen een foutmodel toevoegen aan het NetDevice van knooppunt 3
// We kunnen een ingang naar het NetDevice verkrijgen via het kanaal en de node
// wijzers
Ptr nd3 = PointToPointTopology::GetNetDevice
(n3, kanaal2);
Ptr em = creëren ();
nd3->SetReceiveErrorModel (em);
bool
ErrorModel::DoCorrupt (Pakket& p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("Corrupt!");
return false;
}
Op dit punt kunnen we het programma uitvoeren met ons triviale ErrorModel in de ontvangst
pad van het PointToPointNetDevice. Het print de string "Corrupt!" voor elk pakket
ontvangen op knooppunt n3. Vervolgens keren we terug naar het foutmodel om een subklasse toe te voegen die presteert
interessantere foutmodellering.
Toevoegen a subklasse
De triviale basisklasse ErrorModel doet niets interessants, maar biedt een
handige basisklasse-interface (Corrupt () en Reset ()), doorgestuurd naar virtuele functies die
kan worden ondergeklasseerd. Laten we vervolgens kijken naar wat we een BasicErrorModel noemen waarop is gebaseerd
the ns-2 ErrorModel-klasse (in ns-2/wachtrij/errmodel.{cc,h}).
Welke eigenschappen willen we dat dit heeft, vanuit het perspectief van de gebruikersinterface? We zouden graag
zodat de gebruiker het type ErrorModel dat wordt gebruikt in de
NetDevice. We zouden ook graag de mogelijkheid willen hebben om configureerbare parameters in te stellen.
Hier zijn een paar eenvoudige vereisten die we zullen overwegen:
· Mogelijkheid om de willekeurige variabele in te stellen die de verliezen regelt (standaard is UniformVariable)
· Mogelijkheid om de eenheid (bit, byte, pakket, tijd) van granulariteit in te stellen waarover fouten zijn
toegepast.
· Mogelijkheid om het foutenpercentage in te stellen (bijv. 10^-3) dat overeenkomt met de bovenstaande eenheid van
granulariteit.
· Mogelijkheid om in/uit te schakelen (standaard is ingeschakeld)
Hoe naar subklasse
We verklaren BasicErrorModel als volgt een subklasse van ErrorModel te zijn:
klasse BasicErrorModel: openbaar ErrorModel
{
publiek:
statisch TypeId GetTypeId (ongeldig);
...
private:
// Implementeer pure virtuele functies van de basisklasse
virtuele bool DoCorrupt (Ptr P);
virtuele bool DoReset (ongeldig);
...
}
en configureer de GetTypeId-functie van de subklasse door een unieke TypeId-tekenreeks in te stellen en
de ouder instellen op ErrorModel:
TypeId RateErrorModel::GetTypeId (ongeldig)
{
statisch TypeId tid = TypeId ("ns3::RateErrorModel")
.SetOuder ()
.Constructor toevoegen ()
...
Bouw Kern Functies en Eenheid Tests
Beweren Macro's
schrijf- Eenheid Tests
Het toevoegen van a New Module naar ns-3
Wanneer u een groep gerelateerde klassen, voorbeelden en tests hebt gemaakt, kunnen ze dat zijn
samengevoegd tot een ns-3 module zodat ze kunnen worden gebruikt met bestaande ns-3 modules
en door andere onderzoekers.
Dit hoofdstuk leidt u door de stappen die nodig zijn om een nieuwe module toe te voegen ns-3.
Stap voor 0 - Module Layout
Alle modules zijn terug te vinden in de src map. Elke module is terug te vinden in een directory
die dezelfde naam heeft als de module. Bijvoorbeeld de spectrum module vind je hier:
src/spectrum. We citeren uit de spectrum moduul ter illustratie.
Een voorbeeldgetrouwe module heeft de volgende mappenstructuur en vereiste bestanden:
src /
module naam/
bindingen/
document/
voorbeelden/
wscript
helper/
model/
test/
voorbeelden-to-run.py
wscript
Niet alle mappen zullen aanwezig zijn in elke module.
Stap voor 1 - creëren a Module Skelet
Er is een python-programma beschikbaar in de bronmap dat een skelet zal maken voor een nieuw
moduul. Voor de doeleinden van deze discussie gaan we ervan uit dat uw nieuwe module heet
nieuwe module. Van de src directory, doet u het volgende om de nieuwe module te maken:
$ ./create-module.py nieuwe-module
Vervolgens cd om in nieuwe module; u vindt deze directory-indeling:
$ cd nieuwe module
$ls
doc voorbeelden helper model test wscript
Meer in detail, de create-module.py script zal zowel de mappen als de initialen maken
skelet wscript, .h, . Cc en .eerste bestanden. De complete module met skeletbestanden ziet eruit
soortgelijk:
src /
nieuwe-module/
document/
nieuwe-module.eerst
voorbeelden/
nieuwe-module-voorbeeld.cc
wscript
helper/
nieuwe-module-helper.cc
nieuwe-module-helper.h
model/
nieuwe-module.cc
nieuwe-module.h
test/
nieuwe-module-testsuite.cc
wscript
(Desgewenst de bindingen/ directory vermeld in Stap-0 wordt automatisch aangemaakt tijdens
de opbouw.)
Vervolgens bekijken we hoe u deze module kunt aanpassen. Informeren waf over de bestanden die
make-up van uw module wordt gedaan door de twee te bewerken wscript bestanden. We lopen door de
belangrijkste stappen in dit hoofdstuk.
Alles ns-3 modules zijn afhankelijk van de kern module en meestal op andere modules. Deze afhankelijkheid
is gespecificeerd in de wscript bestand (op het hoogste niveau van de module, niet het afzonderlijke wscript
bestand in de voorbeelden map!). In het skelet wscript de oproep die uw zal verklaren
nieuwe module aan waf ziet er als volgt uit (vóór bewerking):
def bouwen(bld):
module = bld.create_ns3_module('nieuwe-module', ['kern'])
Laten we aannemen dat nieuwe module hangt af van de internet, mobiliteiten aodv modules. Na
het bewerken van de wscript bestand zou er als volgt uit moeten zien:
def bouwen(bld):
module = bld.create_ns3_module('nieuwe-module', ['internet', 'mobiliteit', 'aodv'])
Houd er rekening mee dat alleen module-afhankelijkheden van het eerste niveau moeten worden vermeld, daarom hebben we deze verwijderd
kern; de internet module is op zijn beurt afhankelijk van kern.
Uw module heeft hoogstwaarschijnlijk modelbronbestanden. Eerste skeletten (die zullen
succesvol compileren) zijn gemaakt in model/nieuwe-module.cc en model/nieuwe-module.h.
Als uw module helper-bronbestanden heeft, gaan deze naar het helper/
map; nogmaals, in die directory worden initiële skeletten gemaakt.
Ten slotte is het een goede gewoonte om tests en voorbeelden te schrijven. Deze zullen er vrijwel zeker zijn
vereist om nieuwe modules in de ambtenaar te laten accepteren ns-3 bron boom. Een skelet
testsuite en testcase worden gemaakt in het test/ map. De skelettestsuite zal dat wel doen
bevat de onderstaande constructor, die een nieuwe eenheidstest met de naam declareert nieuwe module, Met een
enkele testcase bestaande uit de klasse NieuweModuleTestCase1:
NieuweModuleTestSuite::NieuweModuleTestSuite ()
: TestSuite ("nieuwe module", UNIT)
{
AddTestCase (nieuwe NewModuleTestCase1);
}
Stap voor 3 - Verklaren Bron Bestanden
De openbare header- en broncodebestanden voor uw nieuwe module moeten worden gespecificeerd in het
wscript bestand door het te wijzigen met uw teksteditor.
Bijvoorbeeld, na het aangeven van de spectrum module, de src/spectrum/wscript specificeert de
broncodebestanden met de volgende lijst:
def bouwen(bld):
module = bld.create_ns3_module('spectrum', ['internet', 'voortplanting', 'antenne', 'toepassingen'])
module.bron = [
'model/spectrum-model.cc',
'model/spectrumwaarde.cc',
.
.
.
'model/microgolf-oven-spectrum-waarde-helper.cc',
'helper/spectrum-helper.cc',
'helper/adhoc-aloha-noack-ideaal-phy-helper.cc',
'helper/golfvorm-generator-helper.cc',
'helper/spectrum-analyzer-helper.cc',
]
De objecten die voortkomen uit het samenstellen van deze bronnen worden samengevoegd tot een linkbibliotheek,
die zal worden gekoppeld aan alle programma's die afhankelijk zijn van deze module.
Maar hoe leren dergelijke programma's de openbare API van onze nieuwe module? Lees verder!
Stap voor 4 - Verklaren Publieke Voorvoegsel Bestanden
De headerbestanden die de openbare API van uw model en helpers definiëren, zouden dat ook moeten zijn
gespecificeerd in de wscript bestand.
Doorgaan met de spectrum modelillustratie, zijn de openbare headerbestanden gespecificeerd
met de volgende strofe. (Merk op dat het argument voor de bld functie vertelt waf naar
installeer de headers van deze module met de andere ns-3 koppen):
headers = bld(features='ns3header')
headers.module = 'spectrum'
kopteksten.bron = [
'model/spectrum-model.h',
'model/spectrumwaarde.h',
.
.
.
'model/magnetron-spectrum-waarde-helper.h',
'helper/spectrum-helper.h',
'helper/adhoc-aloha-noack-ideaal-phy-helper.h',
'helper/golfvorm-generator-helper.h',
'helper/spectrum-analyzer-helper.h',
]
Headers die op deze manier openbaar zijn gemaakt, zijn toegankelijk voor gebruikers van uw model met include
uitspraken als
#include "ns3/spectrum-model.h"
Headers die strikt intern in uw implementatie worden gebruikt, mogen hier niet worden opgenomen. Zij
zijn nog steeds toegankelijk voor uw implementatie door middel van include-statements zoals
#include "mijn-module-implementatie.h"
Stap voor 5 - Verklaren Tests
Als uw nieuwe module tests heeft, moeten deze worden gespecificeerd in uw wscript bestand door
wijzigen met uw teksteditor.
De spectrum modeltesten worden gespecificeerd met de volgende strofe:
module_test = bld.create_ns3_module_test_library('spectrum')
module_test.bron = [
'test/spectrum-interferentie-test.cc',
'test/spectrumwaarde-test.cc',
]
Bekijk Tests voor meer informatie over het schrijven van testgevallen.
Stap voor 6 - Verklaren Voorbeelden
Als uw nieuwe module voorbeelden heeft, moeten deze worden gespecificeerd in uw voorbeelden/script
bestand. (Het skelet op het hoogste niveau wscript zal recursief omvatten voorbeelden/script alleen als
de voorbeelden waren ingeschakeld tijdens de configuratie.)
De spectrum model definieert zijn eerste voorbeeld in src/spectrum/voorbeelden/wscript with
def bouwen(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideaal-phy',
['spectrum', 'mobiliteit'])
obj.source = 'adhoc-aloha-ideaal-phy.cc'
Merk op dat het tweede argument van de functie create_ns3_programma() is de lijst met modules
waarvan het programma dat wordt gemaakt afhankelijk is van; nogmaals, vergeet niet op te nemen nieuwe module in
de lijst. Het is een goede gewoonte om alleen de afhankelijkheden van de directe module op te sommen en te laten waf
de volledige afhankelijkheidsboom afleiden.
Af en toe, voor de duidelijkheid, wilt u misschien de implementatie voor uw voorbeeld opsplitsen
meerdere bronbestanden. Voeg in dit geval die bestanden gewoon toe als extra expliciet
bronnen van het voorbeeld:
obj = bld.create_ns3_program('nieuwe-module-voorbeeld', [nieuwe-module])
obj.source = ['nieuwe-module-voorbeeld.cc', 'nieuwe-module-voorbeeld-onderdeel.cc']
Python-voorbeelden worden gespecificeerd met behulp van de volgende functieaanroep. Merk op dat de tweede
argument voor de functie register_ns3_script() is de lijst met modules die de Python
voorbeeld hangt af van:
bld.register_ns3_script('nieuwe-module-voorbeeld.py', ['nieuwe-module'])
Stap voor 7 - Voorbeelden lopen as Tests
Naast het uitvoeren van expliciete testcode, kan het testframework ook worden geïnstrumenteerd
voer volledige voorbeeldprogramma's uit om te proberen regressies in de voorbeelden op te vangen. Echter niet allemaal
voorbeelden zijn geschikt voor regressietesten. Het bestand test/voorbeelden-to-run.py regelt de
aanroepen van de voorbeelden wanneer het testframework draait.
De spectrum model voorbeelden gerund door test.py zijn gespecificeerd in
src/spectrum/test/examples-to-run.py met behulp van de volgende twee lijsten van C ++ en Python
voorbeelden:
# Een lijst met C++-voorbeelden die moeten worden uitgevoerd om ervoor te zorgen dat ze blijven bestaan
# bouwbaar en uitvoerbaar in de loop van de tijd. Elke tuple in de lijst bevat
#
# (voorbeeld_naam, do_run, do_valgrind_run).
#
# Zie test.py voor meer informatie.
cpp_voorbeelden = [
("adhoc-aloha-ideaal-phy", "True", "True"),
("adhoc-aloha-ideaal-fy-met-microgolfoven", "True", "True"),
("adhoc-aloha-ideaal-phy-matrix-voortplantingsverlies-model", "True", "True"),
]
# Een lijst met Python-voorbeelden die moeten worden uitgevoerd om ervoor te zorgen dat ze blijven bestaan
# uitvoerbaar in de tijd. Elke tuple in de lijst bevat
#
# (voorbeeld_naam, do_run).
#
# Zie test.py voor meer informatie.
python_voorbeelden = [
("sample-simulator.py", "True"),
]
Zoals aangegeven in de opmerking, bevat elk item in de C++-lijst met uit te voeren voorbeelden de
tuple (voorbeeld_naam, doen_rennen, do_valgrind_run), Waar
· voorbeeld_naam is het uitvoerbare bestand dat moet worden uitgevoerd,
· doen_rennen is een voorwaarde waaronder het voorbeeld moet worden uitgevoerd, en
· do_valgrind_run is een voorwaarde om het voorbeeld onder valgrind uit te voeren. (Dit
is nodig omdat NSC illegale instructie-crashes veroorzaakt bij sommige tests wanneer ze
worden uitgevoerd onder valgrind.)
Merk op dat de twee voorwaarden Python-instructies zijn die afhankelijk kunnen zijn van waf configuratie
variabelen. Bijvoorbeeld,
("tcp-nsc-lfn", "NSC_ENABLED == True", "NSC_ENABLED == False"),
Elk item in de Python-lijst met uit te voeren voorbeelden bevat de tuple (voorbeeld_naam,
doen_rennen), waar, wat betreft de C++-voorbeelden,
· voorbeeld_naam is het Python-script dat moet worden uitgevoerd, en
· doen_rennen is een voorwaarde waaronder het voorbeeld moet worden uitgevoerd.
Nogmaals, de voorwaarde is een Python-instructie die kan afhangen van waf configuratie variabelen.
Bijvoorbeeld
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
Stap voor 8 - Configure en Bouw
U kunt nu uw module zoals gewoonlijk configureren, bouwen en testen. U moet de
project als een eerste stap zodat waf cachet de nieuwe informatie in uw wscript bestanden, of
anders wordt uw nieuwe module niet opgenomen in de build.
$ ./waf configure --enable-examples --enable-tests
$ ./waf bouwen
$ ./test.py
Zoek naar de testsuite van uw nieuwe module (en voorbeeldprogramma's, als uw module die heeft
ingeschakeld) in de testuitgang.
Stap voor 9 - Python bindingen
Het toevoegen van Python-bindingen aan uw module is optioneel en de stap wordt becommentarieerd door
standaard in de create-module.py scripts.
# bld.ns3_python_bindings()
Als u Python-bindingen wilt opnemen (alleen nodig als u Python ns-3
programma's in plaats van C++ ns-3 programma's), moet u het commentaar van het bovenstaande ongedaan maken en het
Python API-scansysteem (elders in deze handleiding behandeld) en scan uw module naar
nieuwe bindingen genereren.
Wij creëren Documentatie
ns-3 levert twee soorten documentatie: verklarende hoofdstukken in "gebruikershandleiding"-stijl, en
broncode API-documentatie.
De hoofdstukken "gebruikershandleiding" zijn met de hand geschreven reStructuredText formaat (.eerste), wat is
verwerkt door het Python-documentatiesysteem Sfinx om webpagina's en pdf-bestanden te genereren.
De API-documentatie wordt gegenereerd op basis van de broncode zelf, met behulp van zuurstofoxy, genereren
gecrosslinkte webpagina's. Beide zijn belangrijk: de Sfinx-hoofdstukken leggen de Waarom
en overzicht van het gebruik van een model; de API-documentatie verklaart de hoe details.
Dit hoofdstuk geeft een kort overzicht van deze tools, met nadruk op voorkeursgebruik en
aanpassingen voor ns-3.
Om alle standaarddocumentatie samen te stellen:
$ ./waf-documenten
Lees verder voor meer gespecialiseerde opties.
documenteren with Sfinx
Wij gebruiken Sfinx om verklarende hoofdstukken te genereren die het ontwerp en het gebruik van elk beschrijven
moduul. U leest nu de Documentatie Hoofdstuk. De Importeer & toon Bron schakel in de
zijbalk toont u de reStructuredText-bron voor dit hoofdstuk.
Het toevoegen van New hoofdstukken
Het toevoegen van een nieuw hoofdstuk duurt drie stappen (hieronder in meer detail beschreven):
1. Kiezen Waar? het (de) documentatiebestand(en) zullen leven.
2. Link van een bestaande pagina naar de nieuwe documentatie.
3. Voeg het nieuwe bestand toe aan het Makefile.
Waar?
Documentatie voor een specifieke module, foo, zou normaal gesproken naar binnen moeten gaan src/foo/doc/. Bijvoorbeeld
src/foo/doc/foo.rst zou het document op het hoogste niveau voor de module zijn. De
src/create-module.py script zal dit bestand voor u aanmaken.
Sommige modellen hebben er meerdere nodig .eerste bestanden en figuren; deze moeten allemaal in de
src/foo/doc/ map. De documenten zijn eigenlijk gebouwd door een Sphinx Makefile. Voor vooral
betrokken documentatie, kan het nuttig zijn om een lokale te hebben Makefile in de src/foo/doc/
directory om het bouwen van de documentatie voor deze module te vereenvoudigen (Antenne is een voorbeeld).
Het instellen hiervan is niet bijzonder moeilijk, maar valt buiten het bestek van dit hoofdstuk.
In sommige gevallen omvat de documentatie meerdere modellen; de Netwerk hoofdstuk is een voorbeeld. In
deze gevallen het toevoegen van de .eerste bestanden rechtstreeks naar doc/modellen/bron/ passend zou kunnen zijn.
Link
Sfinx moet het weten met de meeste je nieuwe hoofdstuk zou moeten verschijnen. In de meeste gevallen een nieuw model
hoofdstuk zou de in moeten verschijnen Modellen boek. Om je hoofdstuk daar toe te voegen, bewerk
doc/models/source/index.rst
.. tortree::
:maxdiepte: 1
organisatie
animatie
antenne
aodv
toepassingen
...
Voeg de naam van uw document toe (zonder de .eerste extensie) toe aan deze lijst. Bewaar de
Modelleer hoofdstukken in alfabetische volgorde om visueel scannen naar specifieke hoofdstukken te vergemakkelijken.
Makefile
U moet ook uw document toevoegen aan de juiste Makefile, dus maken weet het te controleren
voor updates. Het Modellenboek Makefile is doc/modellen/Makefile, het handboek Makefile is
doc/handleiding/Makefile.
# maak een lijst van alle .rst-bestanden van de modelbibliotheek die naar $SOURCETEMP moeten worden gekopieerd
BRONNEN = \
bron/conf.py \
bron/_statisch \
bron/index.rst \
bron/vervangen.txt \
bron/organisatie.rst \
...
$(SRC)/antenne/doc/source/antenne.rst \
...
U voegt uw . toe .eerste bestanden naar de BRONNEN variabel. Om cijfers toe te voegen, lees de opmerkingen in de
Makefile om te zien welke variabele uw afbeeldingsbestanden moet bevatten. Nogmaals, bewaar deze alstublieft
In alfabetische volgorde.
Gebouw Sfinx Documenten
Het bouwen van de Sphinx-documentatie is vrij eenvoudig. Om de hele Sfinx te bouwen
documentatie:
$ ./waf sfinx
Om alleen de Models-documentatie te bouwen:
$ maak -C doc/modellen
Om de gegenereerde documentatie te zien, richt u uw browser op doc/models/build/html.
Zoals u kunt zien, gebruikt Sphinx Make om het proces te begeleiden. Het standaarddoel bouwt alles op
ingeschakelde uitvoerformulieren, die in ns-3 zijn de meerdere pagina's html, enkele pagina singlehtmlen
pdf (latex). Om alleen de HTML met meerdere pagina's te bouwen, voegt u de html doelwit:
$ make -C doc/models html
Dit kan handig zijn om de bouwtijd (en de grootte van het gebabbel) te verminderen terwijl u
ben je hoofdstuk aan het schrijven.
Voordat u uw documentatie aan de repo toewijst, moet u controleren of deze zonder is gebouwd
fouten of waarschuwingen. Het bouwproces genereert veel uitvoer (meestal normale chatter
van LaTeX), wat het moeilijk kan maken om te zien of er Sphinx-waarschuwingen zijn of
fouten. Om belangrijke waarschuwingen en fouten te vinden, bouwt u alleen het html versie, zoek dan
het bouwlogboek voor waarschuwing or fout.
ns-3 Bijzonderheden
De sfinx documentatie en zelfstudie zijn redelijk goed. We zullen de basis niet dupliceren
hier, in plaats daarvan focussen op voorkeursgebruik voor ns-3.
· Start documenten met deze twee regels:
.. omvatten:: vervang.txt
.. hoogtepunt:: cpp
De eerste regel maakt enkele eenvoudige vervangingen mogelijk. Typen bijvoorbeeld |ns3| rendert als
ns-3. De tweede stelt de standaard markeertaal voor de broncode expliciet in voor de
bestand, omdat de schatting van de parser niet altijd nauwkeurig is. (Het is ook mogelijk om de
taal expliciet voor een enkel codeblok, zie hieronder.)
· Secties:
Sphinx is vrij liberaal over het markeren van sectiekoppen. Volgens afspraak geven we hier de voorkeur aan
hiërarchie:
.. rubriek hiërarchie:
------------- Hoofdstuk
************* Sectie (#.#)
============= Onderafdeling (#.#.#)
############## Sub-subsectie
· Syntaxisaccentuering:
Om de standaard syntax highlighter te gebruiken, start u gewoon een broncodeblok:
┌─────────────────────────────────────── ───────┬── ───────────────────────────────┐
│Sphinx-bron │ gerenderde uitvoer │
├─────────────────────────────────────── ───────┼── ───────────────────────────────┤
│ │ De Frobnitz is toegankelijk via: │
│ De ``Frobnitz`` is toegankelijk voor:: │ │
│ │ Foo::Frobnitz frob; │
│ Foo::Frobnitz frob; │ frob.Set (...); │
│ frob.Set (...); │ │
└─────────────────────────────────────── ───────┴── ───────────────────────────────┘
Om bijvoorbeeld een specifieke syntax highlighter te gebruiken, slaan shell-commando's:
┌─────────────────────────────────┬───── ────────── ───┐
│Sphinx-bron │ gerenderde uitvoer │
├─────────────────────────────────┼───── ────────── ───┤
│ │
│ .. broncode:: bash │ $ ls │
│ │
│ $ ls │ │
└─────────────────────────────────┴───── ────────── ───┘
· Afkortingen:
Deze afkortingen zijn gedefinieerd:
┌────────────────────────┬────────────── ───┐
│Sphinx-bron │ gerenderde uitvoer │
├────────────────────────┼────────────── ───┤
│ ns-3 │
│ |ns3| │ │
├────────────────────────┼────────────── ───┤
│ ns-2 │
│ |ns2| │ │
├────────────────────────┼────────────── ───┤
│ │
│ |controleren| │ │
├────────────────────────┼────────────── ───┤
│ RFC 6282 │
│ :rfc:`6282` │ │
└────────────────────────┴────────────── ───┘
documenteren with zuurstofoxy
Wij gebruiken zuurstofoxy genereren doorzoekbaar API-documentatie. Doxygen biedt een aantal
handige functies:
· Overzichtstabel van alle klasleden.
· Grafieken van overerving en samenwerking voor alle klassen.
· Koppelingen naar de broncode die elke functie implementeert.
· Links naar elke plaats waar een lid wordt gebruikt.
· Koppelingen naar elk object dat wordt gebruikt bij het implementeren van een functie.
· Groepering van gerelateerde klassen, zoals alle klassen die betrekking hebben op een specifiek protocol.
Daarnaast gebruiken we de TypeId systeem toe te voegen aan de documentatie voor elke klasse
· De Config paden waarlangs dergelijke objecten kunnen worden bereikt.
· Documentatie voor elk Attributen, waaronder Attributen gedefinieerd in ouderklassen.
· Documentatie voor elk Opsporen bronnen gedefinieerd door de klasse.
Doxygen werkt door de broncode te scannen, op zoek naar speciaal gemarkeerde opmerkingen. Het
creëert ook een kruisverwijzing, die aangeeft met de meeste elk bestand, klasse, methode en variabele is
gebruikt.
Preferent Style
De voorkeursstijl voor Doxygen-opmerkingen is de JavaDoc-stijl:
/ **
* Korte beschrijving van deze klasse of methode.
* Aangrenzende regels worden een enkele alinea.
*
* Langere beschrijving, met veel details.
*
* Blanco regels scheiden alinea's.
*
* Leg uit wat de klasse of methode doet, met behulp van welk algoritme.
* Leg de eenheden van argumenten en retourwaarden uit.
*
* \note Let op eventuele beperkingen of valkuilen.
*
* (Voor functies met argumenten of retourwaarde :)
* \param foo Korte zelfstandig naamwoord-zin die dit argument beschrijft.
* \param bar Opmerking Hoofdlettergebruik en eindpunt.
* \return Korte zelfstandig naamwoord-zin die de waarde beschrijft.
*
* \intern
*
* U kunt ook interne uitvoeringsdetails bespreken.
* Het begrijpen van dit materiaal zou niet nodig moeten zijn om het te gebruiken
* de klasse of methode.
*/
klasse Voorbeeld
In deze stijl begint het Doxygen-commentaarblok met twee `*'-tekens: / **, en gaat vooraf
het item dat wordt gedocumenteerd.
Voor items die alleen een korte beschrijving nodig hebben, is een van deze korte formulieren geschikt:
/** Destructor-implementatie. */
ongeldig DoDispose ();
int m_count; //!< Telling van ...
Let op de speciale vorm van het commentaar aan het einde van de regel, //!, wat aangeeft dat het verwijst naar de
voorafgaat item.
Enkele punten om op te merken:
· Gebruik hoofdlettergebruik, inclusief beginhoofdletter.
· Gebruik interpunctie, vooral `.'s aan het einde van zinnen of zinsdelen.
· De \kort label is niet nodig; de eerste zin zal worden gebruikt als de opdracht
Beschrijving.
Elke klasse, methode, typedef, lidvariabele, functieargument en retourwaarde zou moeten
worden gedocumenteerd in alle broncodebestanden die de formele API en implementatie vormen
ns-3, zoals src/ /model/*, src/ /helper/* en src/ /utils/*.
Documentatie voor items in src/ /test/* en src/ /voorbeelden/* heeft de voorkeur,
maar niet vereist.
Nuttig Kenmerken
· Overgeërfde leden nemen automatisch documenten over van de ouder (maar kunnen worden vervangen
door lokale documentatie).
1. Documenteer de basisklasse.
2. Markeer in de subklasse geërfde functies met een gewoon commentaar:
// Overgeërfde methoden
virtuele leegte FooBar (leegte);
virtuele int BarFoo (dubbele baz);
Merk op dat de handtekeningen exact moeten overeenkomen, dus neem het formele argument op (leegte)
Dit werkt niet voor statische functies; zien GetTypeId, hieronder bijvoorbeeld.
Gebouw zuurstofoxy Documenten
Het bouwen van de Doxygen-documentatie is vrij eenvoudig:
$ ./waf zuurstof
Dit bouwt met behulp van de standaardconfiguratie, die documentatiesecties genereert voor
allen items, zelfs als ze geen expliciete commentaardocumentatieblokken hebben. Dit heeft de
effect van het onderdrukken van waarschuwingen voor ongedocumenteerde items, maar zorgt ervoor dat alles verschijnt
in de gegenereerde uitvoer.
Bij het schrijven van documentatie is het vaak handiger om te zien welke items worden gegenereerd
waarschuwingen, meestal over ontbrekende documentatie. Gebruik de om de volledige lijst met waarschuwingen te zien
doc/doxygen.warnings.report.sh script:
$ doc/doxygen.warnings.report.sh
Waf: Directory `build' invoeren
...
Waf: map `build' verlaten
'build' succesvol afgerond (3m24.094s)
Doxygen-documenten opnieuw opbouwen met volledige fouten ... Klaar.
Rapport van Doxygen-waarschuwingen
----------------------------------------
(Alle tellingen zijn ondergrenzen.)
Waarschuwingen per module/directory:
Tel de map
------------------- ----------------------------------
3844 src/lte/model
1718 src/wimax/model
1423 src/kern/model
....
138 extra ongedocumenteerde parameters.
----------------------------------------
15765 totale waarschuwingen
126 mappen met waarschuwingen
Waarschuwingen per bestand (alfabetisch)
Tel bestand
------------------- ----------------------------------
17 doc/introspected-doxygen.h
15 voorbeelden/routing/manet-routing-compare.cc
26 voorbeelden/statistieken/wifi-voorbeeld-apps.h
....
----------------------------------------
967 bestanden met waarschuwingen
Waarschuwingen per bestand (numeriek)
Tel bestand
------------------- ----------------------------------
374 src/lte/model/lte-asn1-header.h
280 src/lte/model/lte-rrc-sap.h
262 src/lte/model/lte-rrc-header.h
....
----------------------------------------
967 bestanden met waarschuwingen
Samenvatting van Doxygen-waarschuwingen
----------------------------------------
126 mappen
967-bestanden
15765 waarschuwingen
Het script past de configuratie aan om alle waarschuwingen weer te geven en om de looptijd te verkorten.
Zoals je kunt zien, hebben we bij dit schrijven a lot van ongedocumenteerde items. Het verslag
vat waarschuwingen per module samen src/*/*, en per bestand, in alfabetische en numerieke volgorde.
Het script heeft een paar opties om dingen te verkleinen en dit beter beheersbaar te maken. Voor hulp,
Gebruik de -h keuze. Nadat ik het een keer heb uitgevoerd om de Doxygen-build te doen en het volledige te genereren
waarschuwingen log, kunt u het logbestand opnieuw verwerken met verschillende "filters", zonder dat u dat hoeft te doen
de volledige Doxygen gebouwd door, opnieuw met behulp van de -s keuze. U kunt waarschuwingen uitsluiten van
*/voorbeelden/* bestanden (-e optie), en/of */test/* bestanden (-t).
Misschien wel de handigste optie bij het schrijven van documentatiecommentaar -m , welke
zal het rapport beperken tot alleen overeenkomende bestanden src/ /*, en volg het rapport met
de eigenlijke waarschuwingslijnen. Combineren met -en en u kunt zich concentreren op de waarschuwingen die er zijn
meest urgent in één module:
$ doc/doxygen.warnings.report.sh -m mesh/helper
...
Samenvatting van Doxygen-waarschuwingen
----------------------------------------
1 mappen
3-bestanden
149 waarschuwingen
Gefilterde waarschuwingen
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: waarschuwing: lid m_root (variabele) van klasse ns3::Dot11sStack is niet gedocumenteerd.
src/mesh/helper/dot11s/dot11s-installer.h:35: waarschuwing: retourneer type lid ns3::Dot11sStack::GetTypeId is niet gedocumenteerd
src/mesh/helper/dot11s/dot11s-installer.h:56: waarschuwing: retourneer type lid ns3::Dot11sStack::InstallStack is niet gedocumenteerd
src/mesh/helper/flame/lfame-installer.h:40: waarschuwing: Lid GetTypeId() (functie) van klasse ns3::FlameStack is niet gedocumenteerd.
src/mesh/helper/flame/flame-installer.h:60: waarschuwing: type lid retourneren ns3::FlameStack::InstallStack is niet gedocumenteerd
src/mesh/helper/mesh-helper.h:213: waarschuwing: Lid m_nInterfaces (variabel) van klasse ns3::MeshHelper is niet gedocumenteerd.
src/mesh/helper/mesh-helper.h:214: waarschuwing: lid m_spreadChannelPolicy (variabel) van klasse ns3::MeshHelper is niet gedocumenteerd.
src/mesh/helper/mesh-helper.h:215: waarschuwing: lid m_stack (variabel) van klasse ns3::MeshHelper is niet gedocumenteerd.
src/mesh/helper/mesh-helper.h:216: waarschuwing: lid m_stackFactory (variabel) van klasse ns3::MeshHelper is niet gedocumenteerd.
src/mesh/helper/mesh-helper.h:209: waarschuwing: parameters van lid ns3::MeshHelper::CreateInterface zijn niet (alle) gedocumenteerd
src/mesh/helper/mesh-helper.h:119: waarschuwing: parameters van lid ns3::MeshHelper::SetStandard zijn niet (alle) gedocumenteerd
Nu is het gewoon een kwestie van de code begrijpen en wat documenten schrijven!
ns-3 Bijzonderheden
Wat betreft Sphinx, de Doxygen docs en referentie zijn redelijk goed. We zullen de
basics hier, in plaats daarvan gericht op voorkeursgebruik voor ns-3.
· Gebruik Doxygen Modules gerelateerde items te groeperen.
Maak in de hoofdkop van een module een Doxgyen-groep aan:
/ **
* \defgroep foo Foo-protocol.
*/
Markeer elke gekoppelde klasse als behorend tot de groep:
/ **
* \ingroep foo
*
* Foo-pakkettype.
*/
klasse Foo
· Wist je dat typedefs formele argumenten kunnen hebben? Dit maakt documentatie van de functie mogelijk
aanwijzer handtekeningen:
/ **
* Bar callback-functiehandtekening.
*
* \param ale De grootte van een halve liter bier, in imperiale ounces.
*/
typedef void (* BarCallback)(const int ale);
· Kopieer de Kenmerk hulpstrings van de GetTypeId methode om als opdracht te gebruiken
beschrijvingen van aangesloten leden.
· \bugid{298} maakt een link naar bug 298 in onze Bugzilla.
· \pnaam{foo} in een beschrijving zal opmaken foo een \param foo parameter, waardoor het duidelijk wordt
dat je verwijst naar een echt argument.
· \RFC{301} maakt een link naar RFC 301.
· \intern mag alleen worden gebruikt om een bespreking van implementatiedetails op gang te brengen, niet om
Mark privaat functies (ze zijn al gemarkeerd, zoals privaat!)
· Maak geen klassen aan met triviale namen, zoals klasse A, zelfs in testsuites. Deze
ervoor zorgen dat alle instanties van de letterlijke klassenaam `A' worden weergegeven als koppelingen.
Zoals hierboven opgemerkt, erven statische functies niet de documentatie van dezelfde functies in
de ouderklasse. ns-3 gebruikt alomtegenwoordig een paar statische functies; de voorgestelde
documentatieblok voor deze gevallen is:
· Standaard constructor/destructor:
Mijn klas (); //!< Standaardconstructor
~MijnKlasse (); //!< Vernietiger
· Dummy destructor en DoDispose:
/** Dummy destructor, zie DoDispose. */
~MijnKlasse ();
/** Destructor-implementatie */
virtuele leegte DoDispose ();
· GetTypeId:
/ **
* Registreer dit type.
* \return Het object TypeId.
*/
statisch TypeId GetTypeId (ongeldig);
inschakelen subsets of ns-3 Modules
Zoals bij de meeste softwareprojecten, ns-3 wordt steeds groter qua aantal modules,
regels code en geheugengebruik. Gebruikers mogen echter maar een paar van die modules gebruiken
tegelijk. Om deze reden willen gebruikers misschien expliciet alleen de subset van het
mogelijk ns-3 modules die ze daadwerkelijk nodig hebben voor hun onderzoek.
In dit hoofdstuk wordt besproken hoe u alleen de ns-3 modules waarin je geïnteresseerd bent
gebruikt.
Hoe naar in staat stellen a subgroep of ns-3's modules
Als er gedeelde bibliotheken worden gebouwd, veroorzaakt het inschakelen van een module er minstens één
bibliotheek te bouwen:
libns3-modulenaam.so
Als de module een testbibliotheek heeft en testbibliotheken worden gebouwd, dan
libns3-modulenaam-test.so
zal ook gebouwd worden. Andere modules waarvan de module afhankelijk is en hun testbibliotheken
zal ook gebouwd worden.
Standaard zijn alle modules ingebouwd ns-3. Er zijn twee manieren om een subset hiervan in te schakelen
modulen:
1. De --enable-modules optie van waf gebruiken
2. De ... gebruiken ns-3 configuratiebestand
Enable modules gebruik waf's --activeer-modules optie
Om bijvoorbeeld alleen de kernmodule met voorbeeld en tests in te schakelen, probeert u deze opdrachten:
$ ./waf schoon
$ ./waf configureren --enable-examples --enable-tests --enable-modules=core
$ ./waf bouwen
$ cd bouwen/debuggen/
$ls
en de volgende bibliotheken moeten aanwezig zijn:
bindingen libns3-core.so ns3 scratch-hulpprogramma's
voorbeelden libns3-core-test.so monsters src
Merk op ./waff schoon stap wordt hier alleen gedaan om het duidelijker te maken welke modulebibliotheken
waren gebouwd. Je hoeft niet te doen ./waff schoon om subsets van modules in te schakelen.
Het uitvoeren van test.py zorgt ervoor dat alleen die tests worden uitgevoerd die afhankelijk zijn van de modulekern:
24 van 24 tests geslaagd (24 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Herhaal de bovenstaande stappen voor de module "netwerk" in plaats van de module "kern", en de
het volgende zal worden gebouwd, aangezien het netwerk afhankelijk is van de kern:
bindingen libns3-core.so libns3-network.so ns3 scratch-hulpprogramma's
voorbeelden libns3-core-test.so libns3-netwerk-test.so samples src
Door test.py uit te voeren, worden die tests uitgevoerd die alleen afhankelijk zijn van de kern- en netwerkmodules
worden gerund:
31 van 31 tests geslaagd (31 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Enable modules gebruik the ns-3 configuratie filet
Er is een configuratiebestand, .ns3rc, toegevoegd ns-3 waarmee gebruikers kunnen specificeren welke
modules moeten worden opgenomen in de build.
Bij het inschakelen van een subset van ns-3 modules zijn de voorrangsregels als volgt:
1. de configuratiereeks --enable-modules overschrijft elk .ns3rc-bestand
2. het .ns3rc-bestand op het hoogste niveau ns-3 directory wordt vervolgens geraadpleegd, indien aanwezig
3. het systeem zoekt naar ~/.ns3rc als de bovenstaande twee niet gespecificeerd zijn
Als geen van de bovenstaande de te bouwen modules beperkt, zullen alle modules die waf kent dat wel doen
gebouwd worden.
De onderhouden versie van het .ns3rc-bestand in de ns-3 broncode-repository bevindt zich in
the utils map. De reden hiervoor is dat als het zich in de directory op het hoogste niveau van de
repository, zou het vatbaar zijn voor onbedoelde check-ins van beheerders die de
modules die ze willen gebruiken. Daarom moeten gebruikers de .ns3rc handmatig kopiëren van de
utils directory naar hun voorkeurslocatie (directory op het hoogste niveau of hun homedirectory).
maak een permanente modulaire build-configuratie mogelijk.
Ervan uitgaande dat je op het hoogste niveau zit ns-3 directory, kunt u een kopie van de .ns3rc
bestand dat zich in de utils map als volgt:
$ cp utils/.ns3rc .
Het .ns3rc-bestand zou nu op uw hoogste niveau moeten staan ns-3 map, en het bevat de
volgende:
#! /usr/bin/env python
# Een lijst met de modules die worden ingeschakeld wanneer ns-3 wordt uitgevoerd.
# Modules die afhankelijk zijn van de vermelde modules worden ook ingeschakeld.
#
# Alle modules kunnen worden ingeschakeld door 'all_modules' te kiezen.
modules_enabled = ['alle_modules']
# Stel dit in op true als u wilt dat voorbeelden worden uitgevoerd.
voorbeelden_enabled = Onwaar
# Stel dit in op true als u wilt dat tests worden uitgevoerd.
tests_enabled = Onwaar
Gebruik uw favoriete editor om het .ns3rc-bestand aan te passen om alleen de kernmodule in te schakelen
voorbeelden en tests zoals deze:
#! /usr/bin/env python
# Een lijst met de modules die worden ingeschakeld wanneer ns-3 wordt uitgevoerd.
# Modules die afhankelijk zijn van de vermelde modules worden ook ingeschakeld.
#
# Alle modules kunnen worden ingeschakeld door 'all_modules' te kiezen.
modules_enabled = ['kern']
# Stel dit in op true als u wilt dat voorbeelden worden uitgevoerd.
voorbeelden_enabled = Waar
# Stel dit in op true als u wilt dat tests worden uitgevoerd.
tests_enabled = Waar
Alleen de kernmodule wordt nu ingeschakeld als u deze opdrachten probeert:
$ ./waf schoon
$./waf configureren
$ ./waf bouwen
$ cd bouwen/debuggen/
$ls
en de volgende bibliotheken moeten aanwezig zijn:
bindingen libns3-core.so ns3 scratch-hulpprogramma's
voorbeelden libns3-core-test.so monsters src
Merk op ./waff schoon stap wordt hier alleen gedaan om het duidelijker te maken welke modulebibliotheken
waren gebouwd. Je hoeft niet te doen ./waff schoon om subsets van modules in te schakelen.
Het uitvoeren van test.py zorgt ervoor dat alleen die tests worden uitgevoerd die afhankelijk zijn van de modulekern:
24 van 24 tests geslaagd (24 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Herhaal de bovenstaande stappen voor de module "netwerk" in plaats van de module "kern", en de
het volgende zal worden gebouwd, aangezien het netwerk afhankelijk is van de kern:
bindingen libns3-core.so libns3-network.so ns3 scratch-hulpprogramma's
voorbeelden libns3-core-test.so libns3-netwerk-test.so samples src
Door test.py uit te voeren, worden die tests uitgevoerd die alleen afhankelijk zijn van de kern- en netwerkmodules
worden gerund:
31 van 31 tests geslaagd (31 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Inschakelen/uitschakelen ns-3 Tests en Voorbeelden
De ns-3 distributie bevat veel voorbeelden en tests die worden gebruikt om de ns-3
systeem. Gebruikers willen echter niet altijd dat deze voorbeelden en tests voor hun worden uitgevoerd
installatie van ns-3.
In dit hoofdstuk wordt besproken hoe te bouwen ns-3 met of zonder zijn voorbeelden en tests.
Hoe naar inschakelen / uitschakelen voorbeelden en testen in ns-3
Er zijn 3 manieren om voorbeelden en tests in/uit te schakelen ns-3:
1. Build.py gebruiken wanneer ns-3 wordt voor het eerst gebouwd
2. Waf een keer gebruiken ns-3 is gebouwd
3. De ... gebruiken ns-3 configuratiebestand een keer ns-3 is gebouwd
Inschakelen / uitschakelen voorbeelden en testen gebruik bouwen.py
U kunt build.py gebruiken om voorbeelden en tests wanneer in of uit te schakelen ns-3 is gebouwd voor de eerste
tijd.
Voorbeelden en tests zijn standaard niet ingebouwd ns-3.
Vanuit de ns-3-allinone-directory kunt u bouwen ns-3 zonder voorbeelden of tests eenvoudig
door te doen:
$ ./build.py
Test.py uitvoeren op het hoogste niveau ns-3 directory zal er nu voor zorgen dat er geen voorbeelden of tests zijn
rennen:
0 van 0 tests geslaagd (0 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Als je wilt bouwen ns-3 met voorbeelden en tests, en doe dan het volgende vanuit de
ns-3-allinone-directory:
$ ./build.py --enable-examples --enable-tests
Test.py uitvoeren op het hoogste niveau ns-3 directory zal alle voorbeelden en tests veroorzaken
te runnen:
170 van 170 tests geslaagd (170 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Inschakelen / uitschakelen voorbeelden en testen gebruik waf
U kunt waf gebruiken om voorbeelden en tests één keer in of uit te schakelen ns-3 is gebouwd.
Voorbeelden en tests zijn standaard niet ingebouwd ns-3.
Van het hoogste niveau ns-3 directory, kunt u bouwen ns-3 zonder voorbeelden of tests eenvoudig
door te doen:
$./waf configureren
$ ./waf bouwen
Als test.py nu wordt uitgevoerd, worden er geen voorbeelden of tests uitgevoerd:
0 van 0 tests geslaagd (0 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Als je wilt bouwen ns-3 met voorbeelden en tests, en doe dan het volgende van bovenaf
niveau ns-3 directory:
$ ./waf configure --enable-examples --enable-tests
$ ./waf bouwen
Als u test.py uitvoert, worden alle voorbeelden en tests uitgevoerd:
170 van 170 tests geslaagd (170 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Inschakelen / uitschakelen voorbeelden en testen gebruik the ns-3 configuratie filet
Er is een configuratiebestand, .ns3rc, toegevoegd ns-3 waarmee gebruikers kunnen specificeren of
voorbeelden en tests moeten worden gebouwd of niet. U kunt dit bestand gebruiken om in/uit te schakelen
voorbeelden en tests een keer ns-3 is gebouwd.
Bij het inschakelen van het uitschakelen van voorbeelden en tests zijn de voorrangsregels als volgt:
1. de configuratiereeksen --enable-examples/--disable-examples overschrijven elk .ns3rc-bestand
2. de configuratiereeksen --enable-tests/--disable-tests overschrijven elk .ns3rc-bestand
3. het .ns3rc-bestand op het hoogste niveau ns-3 directory wordt vervolgens geraadpleegd, indien aanwezig
4. het systeem zoekt naar ~/.ns3rc als het .ns3rc-bestand niet is gevonden in de vorige stap
Als geen van bovenstaande bestaat, worden er geen voorbeelden en tests gebouwd.
De onderhouden versie van het .ns3rc-bestand in de ns-3 broncode-repository bevindt zich in
the utils map. De reden hiervoor is dat als het zich in de directory op het hoogste niveau van de
repository, zou het vatbaar zijn voor onbedoelde check-ins van beheerders die de
modules die ze willen gebruiken. Daarom moeten gebruikers de .ns3rc handmatig kopiëren van de
utils directory naar hun voorkeurslocatie (directory op het hoogste niveau of hun homedirectory).
het permanent inschakelen van voorbeelden en tests mogelijk maken.
Ervan uitgaande dat je op het hoogste niveau zit ns-3 directory, kunt u een kopie van de .ns3rc
bestand dat zich in de utils map als volgt:
$ cp utils/.ns3rc .
Het .ns3rc-bestand zou nu op uw hoogste niveau moeten staan ns-3 map, en het bevat de
volgende:
#! /usr/bin/env python
# Een lijst met de modules die worden ingeschakeld wanneer ns-3 wordt uitgevoerd.
# Modules die afhankelijk zijn van de vermelde modules worden ook ingeschakeld.
#
# Alle modules kunnen worden ingeschakeld door 'all_modules' te kiezen.
modules_enabled = ['alle_modules']
# Stel dit in op true als u wilt dat voorbeelden worden uitgevoerd.
voorbeelden_enabled = Onwaar
# Stel dit in op true als u wilt dat tests worden uitgevoerd.
tests_enabled = Onwaar
Van het hoogste niveau ns-3 directory, kunt u bouwen ns-3 zonder voorbeelden of tests eenvoudig
door te doen:
$./waf configureren
$ ./waf bouwen
Als test.py nu wordt uitgevoerd, worden er geen voorbeelden of tests uitgevoerd:
0 van 0 tests geslaagd (0 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Als je wilt bouwen ns-3 met voorbeelden en tests, gebruik je favoriete editor om te wijzigen
de waarden in het .ns3rc-bestand voor het bestand example_enabled en tests_enabled zijn True:
#! /usr/bin/env python
# Een lijst met de modules die worden ingeschakeld wanneer ns-3 wordt uitgevoerd.
# Modules die afhankelijk zijn van de vermelde modules worden ook ingeschakeld.
#
# Alle modules kunnen worden ingeschakeld door 'all_modules' te kiezen.
modules_enabled = ['alle_modules']
# Stel dit in op true als u wilt dat voorbeelden worden uitgevoerd.
voorbeelden_enabled = Waar
# Stel dit in op true als u wilt dat tests worden uitgevoerd.
tests_enabled = Waar
Van het hoogste niveau ns-3 directory, kunt u bouwen ns-3 met voorbeelden en tests gewoon door
aan het doen:
$./waf configureren
$ ./waf bouwen
Als u test.py uitvoert, worden alle voorbeelden en tests uitgevoerd:
170 van 170 tests geslaagd (170 geslaagd, 0 overgeslagen, 0 mislukt, 0 gecrasht, 0 valgrind-fouten)
Problemen oplossen
Dit hoofdstuk bevat informatie over mogelijk veelvoorkomende fouten bij het bouwen of uitvoeren
ns-3 's.
Houd er rekening mee dat de wiki (http://www.nsnam.org/wiki/Troubleshooting) kan hebben bijgedragen
artikelen.
Bouw fouten
Run-time fouten
Soms kunnen er fouten optreden met een programma na een succesvolle build. Dit zijn looptijden
fouten, en kan vaak optreden wanneer het geheugen beschadigd is of de aanwijzerwaarden onverwacht zijn
nul.
Hier is een voorbeeld van wat er kan gebeuren:
$ ./waf --voer tcp-punt-naar-punt uit
Map '/home/tomh/ns-3-nsc/build' invoeren
Compilatie is succesvol voltooid
Commando ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] afgesloten met code -11
De foutmelding zegt dat het programma zonder succes is beëindigd, maar het is niet duidelijk
op basis van deze informatie wat er mis zou kunnen zijn. Probeer het onder te lopen om het nauwkeuriger te onderzoeken
the gdb debugger:
$ ./waf --run tcp-point-to-point --command-template="gdb %s"
Map '/home/tomh/ns-3-nsc/build' invoeren
Compilatie is succesvol voltooid
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Auteursrecht 2004 Free Software Foundation, Inc.
GDB is vrije software, gedekt door de GNU General Public License, en dat geldt ook voor u
welkom om het te wijzigen en/of kopieën ervan te verspreiden onder bepaalde voorwaarden.
Typ "kopiëren weergeven" om de voorwaarden te bekijken.
Er is absoluut geen garantie voor GDB. Typ "toon garantie" voor details.
Deze GDB is geconfigureerd als "i386-redhat-linux-gnu"...met behulp van host libthread_db
bibliotheek "/lib/libthread_db.so.1".
(gdb) uitvoeren
Startprogramma: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Symbolen lezen van gedeeld object gelezen uit doelgeheugen... klaar.
Geladen systeem leverde DSO op 0xf5c000
Programma ontvangen signaal SIGSEGV, Segmentatiefout.
0x0804aa12 in hoofd (argc=1, argv=0xbfdfefa4)
op ../examples/tcp-point-to-point.cc:136
136 Ptr localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
$1 = {m_ptr = 0x3c5d65}
(gdb) p socketFactory
$2 = {m_ptr = 0x0}
(gdb) stoppen
Het programma loopt. Toch afstappen? (j of n) j
Let eerst op de manier waarop het programma werd aangeroepen: geef de opdracht om te draaien door als een argument voor het
opdrachtsjabloon "gdb %s".
Dit vertelt ons dat er een poging was om de verwijzing naar een null pointer socketFactory ongedaan te maken.
Laten we eens kijken rond regel 136 van tcp-point-to-point, zoals gdb suggereert:
Ptr socketFactory = n2->GetObject (Tcp::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();
De boosdoener hier is dat de retourwaarde van GetObject niet wordt gecontroleerd en mogelijk wel
nul.
Soms moet u de valgrind geheugen controleur voor subtielere fouten. Opnieuw,
u beroept zich op dezelfde manier op het gebruik van valgrind:
$ ./waf --run tcp-point-to-point --command-template="valgrind %s"
BRON
Dit document is geschreven in reStructuredText besteld, Sfinx en wordt onderhouden in de
document/handleiding directory van de broncode van ns-3.
Gebruik ns-3-manual online met behulp van onworks.net-services
