Aceasta este comanda ns-3-manual care poate fi rulată în furnizorul de găzduire gratuit OnWorks folosind una dintre multiplele noastre stații de lucru online gratuite, cum ar fi Ubuntu Online, Fedora Online, emulator online Windows sau emulator online MAC OS
PROGRAM:
NUME
ns-3-manual - ns-3 manual
Aceasta este ns-3 Manual. Documentația principală pentru proiectul ns-3 este disponibilă în cinci
forme:
· ns-3 Oxigen: Documentarea API-urilor publice ale simulatorului
· Tutorial, Manual (acest document), și Biblioteca de modele pentru Ultimele eliberaţi și
dezvoltare copac
· ns-3 Wiki
CUPRINS
Organizație
Acest capitol descrie generalul ns-3 organizarea software și cea corespunzătoare
organizarea acestui manual.
ns-3 este un simulator de rețea cu evenimente discrete în care se află nucleul și modelele de simulare
implementat în C++. ns-3 este construit ca o bibliotecă care poate fi static sau dinamic
legat la un program principal C++ care definește topologia de simulare și pornește
simulator. ns-3 de asemenea, exportă aproape toate API-urile sale în Python, permițând programelor Python
importați un modul „ns3” în același mod ca și modulul ns-3 biblioteca este legată de executabile
în C++.
[imagine] Organizarea software-ului ns-3.NEINDENT
Codul sursă pentru ns-3 este în cea mai mare parte organizată în src director și poate fi descris
după diagrama din Software organizație of ns-3. Ne vom croi drumul de jos
sus; în general, modulele au dependențe numai de modulele de sub ele în figură.
Mai întâi descriem nucleul simulatorului; acele componente care sunt comune tuturor
protocol, hardware și modele de mediu. Nucleul de simulare este implementat în
src/core. Pachetele sunt obiecte fundamentale într-un simulator de rețea și sunt implementate în
src/rețea. Aceste două module de simulare în sine sunt destinate să cuprindă a
nucleu de simulare generic care poate fi utilizat de diferite tipuri de rețele, nu doar
Rețele bazate pe internet. Modulele de mai sus ale ns-3 sunt independente de o anumită rețea
și modele de dispozitive, care sunt tratate în părțile ulterioare ale acestui manual.
Pe lângă cele de mai sus ns-3 de bază, introducem, de asemenea, în porțiunea inițială a
manual, alte două module care completează API-ul de bază bazat pe C++. ns-3 programele pot
accesează direct toate API-urile sau pot folosi un așa-numit ajutor API care oferă
împachetare convenabile sau încapsularea apelurilor API de nivel scăzut. Faptul că ns-3 programe
poate fi scris în două API-uri (sau o combinație a acestora) este un aspect fundamental al
simulator. De asemenea, descriem modul în care Python este suportat în ns-3 înainte de a trece la specific
modele relevante pentru simularea rețelei.
Restul manualului se concentrează pe documentarea modelelor și pe suport
capabilități. Următoarea parte se concentrează pe două obiecte fundamentale în ns-3: Nod și
NetDevice. Două tipuri speciale de NetDevice sunt concepute pentru a sprijini utilizarea emulării rețelei
cazuri, iar emularea este descrisă în continuare. Următorul capitol este dedicat
Modele legate de internet, inclusiv socket-urile API utilizate de aplicațiile de internet. The
capitolul următor acoperă aplicații, iar capitolul următor descrie suport suplimentar
pentru simulare, cum ar fi animatori și statistici.
Proiectul menține un manual separat dedicat testării și validării ns-3 cod
(A se vedea ns-3 Testarea și Validare manual).
Întâmplător Variabile
ns-3 conține un generator de numere pseudo-aleatorie (PRNG) încorporat. Este important pentru
utilizatorii serioși ai simulatorului să înțeleagă funcționalitatea, configurația și utilizarea
din acest PRNG și să decidă dacă este suficient pentru utilizarea sa în cercetare.
Rapid Descriere
ns-3 numerele aleatorii sunt furnizate prin instanțe de ns3::RandomVariableStream.
· în mod implicit, ns-3 simulările folosesc o sămânță fixă; dacă există ceva aleatoriu în
simulare, fiecare rulare a programului va produce rezultate identice, cu excepția cazului în care sămânța și/sau
numărul de rulare este schimbat.
· în ns-3.3 si mai devreme, ns-3 simulările au folosit o sămânță aleatoare în mod implicit; aceasta marcheaza a
schimbarea politicii începând cu ns-3.4.
· în ns-3.14 si mai devreme, ns-3 simulările au folosit o clasă de wrapper diferită numită
ns3::RandomVariable. Ca din ns-3.15, această clasă a fost înlocuită cu
ns3::RandomVariableStream; generatorul de numere pseudo-aleatoare subiacent nu are
schimbat.
· pentru a obține aleatorie în mai multe rulări de simulare, trebuie fie să setați seed
diferit sau setați diferit numărul de rulare. Pentru a seta o sămânță, sunați
ns3::RngSeedManager::SetSeed() la începutul programului; pentru a seta un număr de rulare cu
aceeași sămânță, sunați ns3::RngSeedManager::SetRun() la începutul programului; vedea
Crearea aleator variabile.
· fiecare RandomVariableStream utilizat în ns-3 are asociat un generator virtual de numere aleatoare
Cu acesta; toate variabilele aleatoare folosesc fie o sămânță fixă, fie aleatorie, pe baza utilizării
sămânță globală (punctul precedent);
· dacă intenționați să efectuați mai multe rulări ale aceluiași scenariu, cu diferite aleatorii
numere, asigurați-vă că citiți secțiunea despre cum să efectuați replicări independente:
Crearea aleator variabile.
Citiți mai departe pentru mai multe explicații despre facilitatea numărului aleatoriu pentru ns-3.
Context
Simulările folosesc o mulțime de numere aleatorii; un studiu a constatat că majoritatea simulărilor de rețea
cheltuiește până la 50% din CPU generând numere aleatorii. Utilizatorii de simulare trebuie să fie
preocupat de calitatea numerelor (pseudo) aleatoare și de independența dintre
fluxuri diferite de numere aleatorii.
Utilizatorii trebuie să fie preocupați de câteva probleme, cum ar fi:
· seeding-ul generatorului de numere aleatoare și dacă rezultatul unei simulări este
determinist sau nu,
· cum să achiziționați diferite fluxuri de numere aleatorii care sunt independente de unul
altul, și
· cât durează ciclurile fluxurilor
Vom introduce aici câțiva termeni: un RNG oferă o secvență lungă de (pseudo) aleatorie
numerele. Lungimea acestei secvențe se numește ciclu lungime or perioadă, după care
RNG-ul se va repeta. Această secvență poate fi împărțită în disjuncturi fluxuri. O
fluxul unui RNG este un subset sau un bloc contiguu al secvenței RNG. De exemplu, dacă
Perioada RNG este de lungime N, iar din acest RNG sunt furnizate două fluxuri, apoi primul
fluxul poate folosi primele valori N/2, iar al doilea flux poate produce al doilea N/2
valorile. O proprietate importantă aici este că cele două fluxuri sunt necorelate. De asemenea,
fiecare flux poate fi partiționat disjunct la un număr de necorelate subfluxuri.
RNG subiacent sperăm să producă o secvență pseudo-aleatorie de numere cu un foarte lung
lungimea ciclului și împărțiți aceasta în fluxuri și subfluxuri într-un mod eficient.
ns-3 folosește același generator de numere aleatoare subiacent ca și ns-2: MRG32k3a
generator de la Pierre L'Ecuyer. O descriere detaliată poate fi găsită în
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. Generatorul MRG32k3a
furnizează 1.8x10^{19} fluxuri independente de numere aleatorii, fiecare dintre ele constând din
2.3x10^{15} subfluxuri. Fiecare subflux are o perioadă (de exemplu, numărul de numere aleatorii
înainte de suprapunere) de 7.6x10^{22}. Perioada întregului generator este 3.1x10^{57}.
Clasă ns3::RandomVariableStream este interfața publică pentru acest număr aleator subiacent
generator. Când utilizatorii creează noi variabile aleatoare (cum ar fi ns3::UniformRandomVariable,
ns3::ExponentialRandomVariable, etc.), ei creează un obiect care utilizează unul dintre
fluxuri distincte, independente ale generatorului de numere aleatorii. Prin urmare, fiecare obiect al
tip ns3::RandomVariableStream are, conceptual, propriul RNG „virtual”. În plus,
fiecare ns3::RandomVariableStream poate fi configurat pentru a utiliza unul din setul de substreamuri desenate
din curentul principal.
O implementare alternativă ar fi să permită fiecărei variabile aleatorii să aibă propria sa
(semănat diferit) RNG. Cu toate acestea, nu putem garanta la fel de puternic că este diferit
secvențele ar fi necorelate într-un astfel de caz; prin urmare, preferăm să folosim un singur RNG și
fluxuri și subfluxuri din acesta.
Crearea aleator variabile
ns-3 acceptă un număr de obiecte variabile aleatorii din clasa de bază
RandomVariableStream. Aceste obiecte derivă din ns3::Obiect și sunt gestionate de smart
indicii.
Modul corect de a crea aceste obiecte este utilizarea modelului CreateObject<> Metoda,
cum ar fi:
Ptr x = CreateObject ();
apoi puteți accesa valori apelând metode pe obiect, cum ar fi:
myRandomNo = x->GetInteger ();
Dacă încerci să faci ceva de genul acesta:
myRandomNo = UniformRandomVariable().GetInteger ();
programul dumneavoastră va întâlni o eroare de segmentare, deoarece implementarea se bazează pe
oarecare construcţie de atribute care apare numai atunci când CreateObject se numește.
O mare parte din restul acestui capitol discută acum proprietățile fluxului de
numere pseudo-aleatoare generate de astfel de obiecte și cum să controlezi însămânțarea acestora
obiecte.
Semănat și independent replicări
ns-3 simulările pot fi configurate pentru a produce rezultate deterministe sau aleatorii. Dacă
ns-3 simularea este configurată să utilizeze o sămânță fixă, deterministă, cu același număr de rulare,
ar trebui să dea aceeași ieșire de fiecare dată când este rulat.
În mod implicit, ns-3 simulările folosesc un număr fix și de rulare. Aceste valori sunt stocate în
Două ns3::GlobalValue instanțe: g_rngSeed și g_rngRun.
Un caz de utilizare tipic este de a rula o simulare ca o secvență de încercări independente, astfel încât
calculează statistici pentru un număr mare de rulări independente. Utilizatorul poate schimba
semințe globale și reluați simularea, sau poate avansa starea subfluxului RNG, care
este denumită creșterea numărului de rulare.
O clasa ns3::RngSeedManager oferă un API pentru a controla numărul de seeding și rulare
comportament. Această setare a stării de seeding și substream trebuie apelată înainte de orice aleator
sunt create variabile; de exemplu:
RngSeedManager::SetSeed (3); // Schimbă seed de la implicit de la 1 la 3
RngSeedManager::SetRun (7); // Modifică numărul de rulare de la implicit de la 1 la 7
// Acum, creați variabile aleatoare
Ptr x = CreateObject ();
Ptr y = CreateObject ();
...
Ce este mai bine, stabilirea unei noi sămânțe sau avansarea stării substream? Nu este
garantează că fluxurile produse de două semințe aleatorii nu se vor suprapune. Singura cale de a
garantarea faptului că două fluxuri nu se suprapun este de a folosi capacitatea substream oferită de
implementarea RNG. Prin urmare, utilizare il substream capacitate la produce multiplu
independent ruleaza of il acelaşi simulare. Cu alte cuvinte, cu atât mai riguros din punct de vedere statistic
modalitatea de a configura mai multe replicări independente este de a folosi o sămânță fixă și de a avansa
numărul de rulare. Această implementare permite un maxim de 2.3x10^{15} independent
replicări folosind subfluxurile.
Pentru ușurință în utilizare, nu este necesar să controlați numărul de semințe și de rulare din interiorul
program; utilizatorul poate seta NS_GLOBAL_VALUE variabilă de mediu după cum urmează:
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run program-name
O altă modalitate de a controla acest lucru este prin trecerea unui argument de linie de comandă; deoarece aceasta este o ns-3
Instanță GlobalValue, se face în mod echivalent după cum urmează:
$ ./waf --command-template="%s --RngRun=3" --run program-name
sau, dacă rulați programe direct în afara waf:
$ ./build/optimized/scratch/program-name --RngRun=3
Variantele de linie de comandă de mai sus fac mai ușor să rulați o mulțime de rulări diferite dintr-un shell
script prin trecerea unui index RngRun diferit.
Clasă RandomVariableStream
Toate variabilele aleatoare ar trebui să provină din clasă Variabilă aleatorie. Această clasă de bază oferă a
câteva metode pentru configurarea globală a comportamentului generatorului de numere aleatorii. Derivat
clasele furnizează API pentru a desena variații aleatorii din distribuția particulară
sprijinit.
Fiecare RandomVariableStream creat în simulare primește un generator care este nou
RNGStream din PRNG-ul de bază. Folosită în acest mod, implementarea L'Ecuyer
permite un maxim de 1.8x10^19 variabile aleatoare. Fiecare variabilă aleatoare într-o singură
replicarea poate produce până la 7.6x10^22 numere aleatorii înainte de suprapunere.
bază clasă public API
Mai jos sunt extrase câteva metode publice de clasă RandomVariableStream care accesează
următoarea valoare din substream.
/ **
* \brief Returnează o dublă aleatorie din distribuția de bază
* \return O valoare aleatorie în virgulă mobilă
*/
dublu GetValue (void) const;
/ **
* \brief Returnează un număr întreg aleatoriu din distribuția de bază
* \return Numărul întreg al lui ::GetValue()
*/
uint32_t GetInteger (void) const;
Am descris deja configurația de însămânțare mai sus. Variabilă aleatorie diferită
subclasele pot avea API suplimentare.
Tipuri de of Variabile aleatoare
Următoarele tipuri de variabile aleatoare sunt furnizate și sunt documentate în ns-3
Doxygen sau prin citire src/core/model/random-variable-stream.h. De asemenea, utilizatorii pot crea
propriile variabile aleatoare personalizate prin derivarea din clasă RandomVariableStream.
· clasa UniformRandomVariable
· clasa ConstantRandomVariable
· clasa SequentialRandomVariable
· clasa ExponentialRandomVariable
· clasa ParetoRandomVariable
· clasa WeibullRandomVariable
· clasa NormalRandomVariable
· clasa LogNormalRandomVariable
· clasa GammaRandomVariable
· clasa ErlangRandomVariable
· clasa TriangularRandomVariable
· clasa ZipfRandomVariable
· clasa ZetaRandomVariable
· clasa DeterministăRandomVariable
· clasa EmpiricalRandomVariable
Semantică of RandomVariableStream obiecte
Obiectele RandomVariableStream derivă din ns3::Obiect și sunt gestionate de indicatori inteligenti.
Instanțele RandomVariableStream pot fi, de asemenea, utilizate în ns-3 atribute, ceea ce înseamnă că
valorile pot fi setate pentru ele prin intermediul ns-3 sistem de atribute. Un exemplu este în
modele de propagare pentru WifiNetDevice:
TypeId
RandomPropagationDelayModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.SetParent ()
.AddConstructor ()
.AddAttribute ("Variabilă",
„Variabila aleatoare care generează întârzieri aleatorii”,
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker ())
;
return tid;
}
Aici ns-3 utilizatorul poate schimba variabila aleatoare implicită pentru acest model de întârziere (adică
o UniformRandomVariable care variază de la 0 la 1) prin sistemul de atribute.
Utilizarea alte PRNG
În prezent, nu există suport pentru înlocuirea unui număr aleator subiacent diferit
generator (de exemplu, Biblioteca științifică GNU sau pachetul Akaroa). Patch-urile sunt binevenite.
reglaj il curent număr
Generatorul MRG32k3a de bază oferă 2^64 fluxuri independente. În ns-3, acestea sunt
atribuit secvenţial începând de la primul flux ca noi instanţe RandomVariableStream
efectuează primul apel către GetValue().
Ca urmare a modului în care aceste obiecte RandomVariableStream sunt alocate fluxurilor subiacente,
atribuirea este sensibilă la perturbările configurației simulării. The
consecința este că dacă orice aspect al configurației simulării este modificat, maparea
de RandomVariables la fluxuri se pot schimba (sau nu).
Ca exemplu concret, un utilizator care execută un studiu comparativ între protocoalele de rutare poate
găsiți că acțiunea de a schimba un protocol de rutare cu altul va observa că
modelul de mobilitate subiacent sa schimbat de asemenea.
Începând cu ns-3.15, utilizatorilor le-a fost oferit un anumit control pentru a le permite utilizatorilor
opțional, remediați alocarea obiectelor RandomVariableStream selectate la baza
cursuri. Acesta este Pârâu atribut, parte a clasei de bază RandomVariableStream.
Prin partiționarea secvenței existente de fluxuri de înainte:
<------------------------------------------------- ------------------------->
flux 0 flux (2^64 - 1)
în două seturi de dimensiuni egale:
<------------------------------------------------- ------------------------->
^ ^^ ^
| || |
flux 0 flux (2^63 - 1) flux 2^63 flux (2^64 - 1)
<- atribuit automat -----------><- atribuit de utilizator ------------------>
Primele 2^63 de fluxuri continuă să fie atribuite automat, în timp ce ultimele 2^63 sunt
dați indici de flux începând cu zero până la 2^63-1.
Atribuirea fluxurilor unui număr fix de flux este opțională; cazuri de
RandomVariableStream care nu au o valoare de flux atribuită va fi alocată următorului
unul din bazinul de fluxuri automate.
Pentru a fixa un RandomVariableStream unui anumit flux subiacent, atribuiți-i Pârâu
atribut unui număr întreg nenegativ (valoarea implicită -1 înseamnă că o valoare va fi
alocate automat).
Editare ta rezultate obținute
Când publicați rezultatele simulării, o informație cheie de configurare pe care o aveți
ar trebui să precizeze întotdeauna cum ați folosit generatorul de numere aleatorii.
· ce semințe ai folosit,
· ce RNG ați folosit dacă nu este cel implicit,
· cum au fost efectuate alergările independente,
· pentru simulări mari, cum ați verificat dacă nu ați făcut ciclul.
Este de competența cercetătorului care publică rezultatele să includă suficiente informații
permite altora să-și reproducă rezultatele. De asemenea, este de sarcina cercetătorului să
convinge-te că numerele aleatoare utilizate au fost valide statistic și să precizezi în
lucrarea de ce se presupune o asemenea încredere.
Rezumat
Să revizuim ce lucruri ar trebui să faceți atunci când creați o simulare.
· Decideți dacă alergați cu o sămânță fixă sau cu o sămânță aleatorie; o sămânță fixă este
Mod implicit,
· Decideți cum veți gestiona replicările independente, dacă este cazul,
· Convinge-te că nu desenezi mai multe valori aleatorii decât lungimea ciclului, dacă
rulezi o simulare foarte lungă și
· Când publicați, urmați instrucțiunile de mai sus despre documentarea utilizării aleatorii
generator de numere.
Hașiș funcţii
ns-3 oferă o interfață generică pentru funcțiile hash de uz general. În cel mai simplu
utilizare, funcția hash returnează hash-ul de 32 de biți sau 64 de biți al unui buffer sau șir de date.
Funcția hash de bază implicită este murmur3, ales pentru că are o funcție hash bună
proprietăți și oferă o versiune pe 64 de biți. Venerabilul FNV1a hash este, de asemenea, disponibil.
Există un mecanism simplu pentru a adăuga (sau a furniza în timpul executării) hash alternativ
implementari de functii.
pachet de bază Folosire
Cel mai simplu mod de a obține o valoare hash a unui buffer de date sau a unui șir este doar:
#include „ns3/hash.h”
folosind namespace ns3;
char * buffer = ...
size_t buffer_size = ...
uint32_t buffer_hash = Hash32 ( buffer, buffer_size);
std::string s;
uint32_t string_hash = Hash32 (s);
Funcțiile echivalente sunt definite pentru valori hash pe 64 de biți.
incrementală hashing
În unele situații, este util să se calculeze hash-ul mai multor buffer-uri, ca și cum ar fi avut
fost unite. (De exemplu, ați putea dori hash-ul unui flux de pachete, dar nu
doriți să asamblați un singur buffer cu conținutul combinat al tuturor pachetelor.)
Acesta este aproape la fel de simplu ca primul exemplu:
#include „ns3/hash.h”
folosind namespace ns3;
char * buffer;
size_t buffer_size;
Hasher hasher; // Folosește funcția hash implicită
pentru ( )
{
buffer = get_next_buffer ();
hasher (buffer, buffer_size);
}
uint32_t combined_hash = hasher.GetHash32 ();
În mod implicit Hasher păstrează starea internă pentru a permite hashingul incremental. Dacă doriți să
reutilizare a Hasher obiect (de exemplu, deoarece este configurat cu un hash care nu este implicit
funcția), dar nu doriți să adăugați la hashul calculat anterior, trebuie clar()
primul:
hasher.clear ().GetHash32 (buffer, buffer_size);
Aceasta reinițializează starea internă înainte de hashing-ul tampon.
Utilizarea an alternativă Hașiș Funcţie
Funcția hash implicită este murmur3. FNV1a este de asemenea disponibil. Pentru a specifica hash-ul
funcția în mod explicit, utilizați acest constructor:
Hasher hasher = Hasher (Creează () );
Adăugare Nou Hașiș Funcţie implementari
Pentru a adăuga funcția hash foo, urmează hash-murmur3.h/. Cc model:
· Creați o declarație de clasă (.h) și definiție (. Cc) mostenind din
Hash::Implementare.
· include declarația în haş.h (în punctul în care hash-murmur3.h este inclus.
· În propriul cod, instanțiați a Hasher obiect prin intermediul constructorului Hasher
(Ptr ())
Dacă funcția ta hash este o singură funcție, de ex hashf, nici măcar nu trebuie să creați un
clasă nouă derivată din HashImplementation:
Hasher hasher =
Hasher (Creează (&hashf) );
Pentru ca aceasta să fie compilată, dvs hashf trebuie să se potrivească cu una dintre semnăturile indicatorului de funcție:
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
Surse pentru Hașiș funcţii
Sursele pentru implementările altor funcții hash includ:
· 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
Evenimente și Simulator
ns-3 este un simulator de rețea cu evenimente discrete. Din punct de vedere conceptual, simulatorul ține evidența a
numărul de evenimente care sunt programate să se execute la un moment de simulare specificat. Meseria de
simulatorul trebuie să execute evenimentele în ordine temporală secvențială. Odată finalizată
are loc un eveniment, simulatorul va trece la următorul eveniment (sau va ieși dacă nu există
mai multe evenimente în coada de evenimente). Dacă, de exemplu, un eveniment este programat pentru timpul de simulare
„100 de secunde” este executat, iar următorul eveniment nu este programat până la „200 de secunde”,
simulatorul va sări imediat de la 100 de secunde la 200 de secunde (de timp de simulare) la
executa următorul eveniment. Aceasta este ceea ce se înțelege prin simulator „eveniment discret”.
Pentru ca toate acestea să se întâmple, simulatorul are nevoie de câteva lucruri:
1. un obiect simulator care poate accesa o coadă de evenimente unde sunt stocate evenimente și care poate
gestionează execuția evenimentelor
2. un planificator responsabil cu inserarea și eliminarea evenimentelor din coadă
3. o modalitate de a reprezenta timpul de simulare
4. evenimentele în sine
Acest capitol al manualului descrie aceste obiecte fundamentale (simulator, planificator,
timp, eveniment) și modul în care sunt utilizate.
eveniment
La be terminat
Simulator
Clasa Simulator este punctul de intrare public pentru a accesa facilitățile de programare a evenimentelor. O singura data
au fost programate câteva evenimente pentru a începe simularea, utilizatorul poate începe
executați-le prin intrarea în bucla principală a simulatorului (call Simulator::Run). Odată ce bucla principală
începe să ruleze, va executa secvenţial toate evenimentele programate în ordine de la cel mai vechi până la
cel mai recent până când fie nu mai sunt evenimente rămase în coada de evenimente sau
Simulator::Stop a fost apelat.
Pentru a programa evenimente pentru execuția de către bucla principală a simulatorului, clasa Simulator oferă
familia de funcții Simulator::Schedule*.
1. Gestionarea gestionatorilor de evenimente cu semnături diferite
Aceste funcții sunt declarate și implementate ca șabloane C++ pentru a gestiona automat
mare varietate de semnături C++ de gestionare a evenimentelor utilizate în sălbăticie. De exemplu, pentru a programa un
eveniment pentru a executa 10 secunde în viitor și invocați o metodă sau o funcție C++ cu
argumente specifice, ați putea scrie asta:
void handler (int arg0, int arg1)
{
std::cout << "handler apelat cu argumentul arg0=" << arg0 << " și
arg1=" << arg1 << std::endl;
}
Simulator::Schedule(secunde(10), &handler, 10, 5);
Care va scoate:
handler apelat cu argumentul arg0=10 și arg1=5
Desigur, aceste șabloane C++ pot gestiona și metodele membre în mod transparent pe C++
obiecte:
La be efectuat: membru metodă exemplu
note:
· metodele ns-3 Schedule recunosc automat funcțiile și metodele numai dacă acestea
luați mai puțin de 5 argumente. Dacă aveți nevoie de ele pentru a susține mai multe argumente, vă rugăm să introduceți a
raportul de eroare.
· Cititorii familiarizați cu termenul „functori complet legați” vor recunoaște
Metode Simulator::Schedule ca o modalitate de a construi automat astfel de obiecte.
2. Operațiuni comune de programare
API-ul Simulator a fost conceput pentru a face cu adevărat simplă programarea majorității evenimentelor. Aceasta
oferă trei variante pentru a face acest lucru (ordonate de la cel mai frecvent utilizat la cel mai puțin utilizat):
· Metode de programare care vă permit să programați un eveniment în viitor, oferind
întârziere între ora de simulare curentă și data de expirare a evenimentului țintă.
· Metode ScheduleNow care vă permit să programați un eveniment pentru simularea curentă
timp: se vor executa _după_ executarea evenimentului curent, dar _inainte_ de
timpul de simulare este modificat pentru următorul eveniment.
· Metode ScheduleDestroy care vă permit să vă conectați la procesul de oprire a Simulatorului
pentru a curăța resursele de simulare: fiecare eveniment „distruge” este executat atunci când utilizatorul sună
metoda Simulator::Destroy.
3. Mentinerea contextului de simulare
Există două moduri de bază de a programa evenimente, cu și fără context. Ce face aceasta
Adică?
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
Raport
Simulator::ScheduleWithContext (context uint32_t, Time const &time, MEM mem_ptr, OBJ obj);
Cititori care investesc timp și efort în dezvoltarea sau utilizarea unui model de simulare non-trivial
va cunoaște valoarea cadrului de înregistrare ns-3 pentru a depana simulări simple și complexe
deopotrivă. Una dintre caracteristicile importante oferite de acest cadru de logare este
afișarea automată a ID-ului nodului de rețea asociat cu evenimentul care rulează „în prezent”.
Id-ul nodului nodului de rețea care se execută în prezent este de fapt urmărit de Simulator
clasă. Poate fi accesat cu metoda Simulator::GetContext care returnează
„context” (un număr întreg de 32 de biți) asociat și stocat în evenimentul care se execută în prezent. În
unele cazuri rare, când un eveniment nu este asociat cu un anumit nod de rețea, acesta
„context” este setat la 0xffffffff.
Pentru a asocia un context fiecărui eveniment, metodele Schedule și ScheduleNow automat
reutilizați contextul evenimentului în curs de execuție ca context al evenimentului programat
pentru executare ulterior.
În unele cazuri, mai ales atunci când se simulează transmiterea unui pachet de la un nod către
altul, acest comportament este de nedorit deoarece contextul așteptat al evenimentului de recepție este
cea a nodului receptor, nu a nodului expeditor. Pentru a evita această problemă, Simulatorul
clasa oferă o metodă specifică de planificare: ScheduleWithContext, care permite să furnizeze
în mod explicit, id-ul nodului nodului receptor asociat cu evenimentul de primire.
XXX: cod exemplu
În unele cazuri foarte rare, dezvoltatorii ar putea fi nevoiți să modifice sau să înțeleagă cum este contextul
(ID nodului) al primului eveniment este setat la cel al nodului său asociat. Acest lucru este realizat
de clasa NodeList: ori de câte ori este creat un nou nod, clasa NodeList folosește
ScheduleWithContext pentru a programa un eveniment de „inițializare” pentru acest nod. Evenimentul „inițializare”.
astfel se execută cu un context setat la cel al nodului id și poate folosi varietatea normală a
Metode de programare. Invocă metoda Node::Initialize care propagă „inițializarea”
eveniment prin apelarea metodei DoInitialize pentru fiecare obiect asociat cu nodul. The
Metoda DoInitialize este suprascrisă în unele dintre aceste obiecte (mai ales în aplicația
clasa de bază) va programa unele evenimente (mai ales Application::StartApplication) care
va programa la rândul său evenimente de generare de trafic care la rândul lor vor programa
evenimente la nivel de rețea.
note:
· Utilizatorii trebuie să fie atenți să propagă metodele DoInitialize între obiecte prin apelare
Inițializați explicit pe obiectele lor membre
· ID-ul de context asociat fiecărei metode ScheduleWithContext are alte utilizări dincolo
logging: este folosit de o ramură experimentală a ns-3 pentru a efectua simulări paralele
sisteme multicore care utilizează multithreading.
Funcțiile Simulator::* nu știu care este contextul: ele doar se asigură că
orice context pe care îl specificați cu ScheduleWithContext este disponibil atunci când este corespunzător
evenimentul se execută cu ::GetContext.
Depinde de modelele implementate deasupra Simulator::* să interpreteze valoarea contextului.
În ns-3, modelele de rețea interpretează contextul ca id-ul nodului care
a generat un eveniment. Acesta este motivul pentru care este important să apelați ScheduleWithContext în
ns3::Subclase de canal pentru că generăm un eveniment de la nodul i la nodul j și
vreau să vă asigurați că evenimentul care va rula pe nodul j are contextul potrivit.
Timp
La be terminat
Scheduler
La be terminat
Rambursări
Unii utilizatori noi la ns-3 nu sunt familiarizați cu un limbaj de programare utilizat pe scară largă
în tot codul: the ns-3 suna inapoi. Acest capitol oferă unele motivații privind
apel invers, îndrumări despre cum să îl utilizați și detalii despre implementarea acestuia.
Rambursări motivaţia
Luați în considerare că aveți două modele de simulare A și B și doriți să le treceți
informații dintre ele în timpul simulării. O modalitate prin care poți face asta este că tu
poate face pe A și B fiecare în mod explicit informat despre celălalt, astfel încât să poată invoca
metode una pe cealaltă:
clasa a {
public:
void ReceiveInput ( // parametrii );
...
}
(în alt fișier sursă:)
clasa B {
public:
void DoSomething (void);
...
privat:
A* o_instanță; // pointer către un A
}
anula
B::Fă ceva()
{
// Spune-i unei_instanțe că s-a întâmplat ceva
a_instance->ReceiveInput ( // parametri);
...
}
Acest lucru funcționează cu siguranță, dar are dezavantajul că introduce o dependență de A și B
să știți despre celălalt în timpul compilării (acest lucru face mai greu să aveți independent
unități de compilare în simulator) și nu este generalizat; dacă într-un scenariu de utilizare ulterioară,
B trebuie să vorbească cu un obiect C complet diferit, codul sursă pentru B trebuie să fie
schimbat pentru a adăuga a c_instanță si asa mai departe. Este ușor de văzut că aceasta este o forță brută
mecanism de comunicare care poate duce la programare cruft în modele.
Acest lucru nu înseamnă că obiectele nu ar trebui să cunoască unele despre altele dacă există un dur
dependența dintre ele, dar că adesea modelul poate fi flexibilizat dacă este
interacțiunile sunt mai puțin constrânse în timpul compilării.
Aceasta nu este o problemă abstractă pentru cercetarea de simulare a rețelei, ci mai degrabă a fost o
sursă de probleme în simulatoarele anterioare, când cercetătorii doresc să extindă sau să modifice
sistem să facă lucruri diferite (cum sunt apte să facă în cercetare). Luați în considerare, de exemplu,
un utilizator care dorește să adauge un substrat de protocol de securitate IPsec între TCP și IP:
------------ -----------
| TCP | | TCP |
------------ -----------
| devine -> |
----------- -----------
| IP | | IPsec |
----------- -----------
|
-----------
| IP |
-----------
Dacă simulatorul a făcut ipoteze și a codificat greu în cod, IP-ul respectiv vorbește întotdeauna
la un protocol de transport de mai sus, utilizatorul poate fi forțat să pirateze sistemul pentru a obține
interconexiunile dorite. Aceasta nu este în mod clar o modalitate optimă de a proiecta un generic
simulator.
Rambursări Context
NOTĂ:
Cititorii familiarizați cu programarea apelurilor inverse pot sări peste această secțiune tutorial.
Mecanismul de bază care permite cuiva să abordeze problema de mai sus este cunoscut sub numele de a suna inapoi.
Scopul final este de a permite unei bucăți de cod să apeleze o funcție (sau o metodă în C++)
fără nicio dependență specifică între module.
Acest lucru înseamnă în cele din urmă că aveți nevoie de un fel de indirect -- tratați adresa
numită funcție ca variabilă. Această variabilă se numește variabilă pointer-to-function.
Relația dintre funcție și pointer-la-funcție nu este cu adevărat diferită
că cea de obiect și pointer-la-obiect.
În C exemplul canonic al unui pointer-la-funcție este a
indicator-la-funcție-întoarcere-întreg (PFI). Pentru un PFI care ia un parametru int, acesta
ar putea fi declarat astfel:
int (*pfi)(int arg) = 0;
Ceea ce obțineți din aceasta este o variabilă numită simplu pfi care este inițializată la valoarea 0.
Dacă doriți să inițializați acest indicator la ceva semnificativ, trebuie să aveți un
funcția cu o semnătură potrivită. În acest caz:
int Funcția mea (int arg) {}
Dacă aveți această țintă, puteți inițializa variabila pentru a indica funcția dvs., cum ar fi:
pfi = MyFunction;
Apoi puteți apela MyFunction indirect folosind forma mai sugestivă a apelului:
int rezultat = (*pfi) (1234);
Acest lucru este sugestiv, deoarece se pare că dereferențiați doar indicatorul funcției
ca și cum ai dereferi orice indicator. De obicei, totuși, oamenii profită de
faptul că compilatorul știe ce se întâmplă și va folosi doar o formă mai scurtă:
int rezultat = pfi (1234);
Observați că indicatorul funcției se supune semanticii valorii, așa că îl puteți transmite ca oricare
altă valoare. De obicei, atunci când utilizați o interfață asincronă, veți trece o entitate
astfel la o funcție care va efectua o acțiune și apel înapoi pentru a vă anunța
efectuat. Apelează înapoi urmând direcția și executând funcția furnizată.
În C++ aveți complexitatea adăugată a obiectelor. Analogia cu PFI-ul de mai sus înseamnă tu
au un pointer către o funcție membru care returnează un int (PMI) în loc de pointer către
funcție care returnează un int (PFI).
Declarația variabilei care furnizează indirectarea arată doar ușor diferită:
int (MyClass::*pmi) (int arg) = 0;
Aceasta declară o variabilă numită pmi la fel cum exemplul anterior a declarat o variabilă numită
pfi. Deoarece va fi să apelăm o metodă a unei instanțe a unei anumite clase, trebuie
declara acea metoda intr-o clasa:
clasa MyClass {
public:
int MyMethod (int arg);
};
Având în vedere această declarație de clasă, se va inițializa apoi variabila astfel:
pmi = &MyClass::MyMethod;
Aceasta atribuie variabilei adresa codului care implementează metoda, completând
indirecta. Pentru a apela o metodă, codul are nevoie de un acest indicator. Asta in schimb,
înseamnă că trebuie să existe un obiect din MyClass la care să se facă referire. Un exemplu simplist în acest sens este doar
apelarea unei metode indirect (gândiți-vă la funcția virtuală):
int (MyClass::*pmi) (int arg) = 0; // Declarați un PMI
pmi = &MyClass::MyMethod; // Indicați codul de implementare
MyClass myClass; // Este nevoie de o instanță a clasei
(myClass.*pmi) (1234); // Apelați metoda cu un obiect ptr
La fel ca în exemplul C, puteți utiliza acest lucru într-un apel asincron către alt modul
care va apel înapoi folosind o metodă și un indicator de obiect. Extensia simplă
s-ar putea lua în considerare este de a trece un pointer către obiect și variabila PMI. Modulul
ar face doar:
(*objectPtr.*pmi) (1234);
pentru a executa apelul înapoi pe obiectul dorit.
S-ar putea întreba în acest moment, ceea ce este il punct? Modulul apelat va trebui să înțeleagă
tipul concret al obiectului apelant pentru a efectua corect callback-ul. De ce nu
acceptați acest lucru, treceți indicatorul de obiect introdus corect și faceți obiect->Metodă(1234) in
codul în loc de apel invers? Aceasta este tocmai problema descrisă mai sus. Ce este
necesar este o modalitate de a decupla complet funcția de apelare de clasa apelată. Acest
cerința a condus la dezvoltarea Functor.
Un functor este rezultatul a ceva inventat în anii 1960 numit închidere. Este
practic, doar un apel de funcție pachet, eventual cu o anumită stare.
Un functor are două părți, o parte specifică și o parte generică, legate prin moștenire.
Codul de apelare (codul care execută apelul invers) va executa o supraîncărcare generică
operator () a unui functor generic pentru a determina apelarea inversă. Codul numit (the
cod care dorește să fie apelat înapoi) va trebui să ofere o implementare specializată a
il operator () care efectuează munca specifică clasei care a provocat cuplarea strânsă
problema de mai sus.
Cu functorul specific și supraîncărcarea acestuia operator () creat, codul numit atunci
dă codul specializat modulului care va executa apelul invers (callback-ul
cod).
Codul de apelare va lua un functor generic ca parametru, deci se face o transformare implicită
în apelul de funcție pentru a converti functorul specific într-un functor generic. Acest lucru înseamnă
că modulul de apelare trebuie doar să înțeleagă tipul de functor generic. Este decuplat
din codul de apel complet.
Informațiile de care aveți nevoie pentru a face un anumit functor sunt indicatorul obiect și
adresa pointer-to-metod.
Esența a ceea ce trebuie să se întâmple este că sistemul declară o parte generică a
functor:
șablon
clasă Functor
{
public:
virtual int operator() (T arg) = 0;
};
Apelantul definește o parte specifică a functorului care este într-adevăr acolo doar pentru implementare
specificul operator() metodă:
șablon
clasa SpecificFunctor : Functor public
{
public:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}
operator virtual int() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
privat:
int (T::*m_pmi)(ARG arg);
T* m_p;
};
Iată un exemplu de utilizare:
clasa a
{
public:
A (int a0): a (a0) {}
int Bună ziua (int b0)
{
std::cout << "Bună ziua de la A, a = " << a << " b0 = " << b0 << std::endl;
}
int a;
};
int main ()
{
A a(10);
SpecificFunctor sf(&a, &A::Bună ziua);
sf(5);
}
NOTĂ:
Codul anterior nu este un cod ns-3 real. Este un exemplu de cod simplist folosit doar pentru
să ilustreze conceptele implicate și să vă ajute să înțelegeți mai bine sistemul. Nu face
așteptați-vă să găsiți acest cod oriunde în arborele ns-3.
Observați că există două variabile definite în clasa de mai sus. Variabila m_p este
object pointer și m_pmi este variabila care conține adresa funcției către
a executa.
Observați că atunci când operator() este apelat, acesta la rândul său apelează metoda furnizată cu
pointer obiect folosind sintaxa C++ PMI.
Pentru a utiliza acest lucru, se poate declara un cod de model care ia un functor generic ca a
parametru:
void LibraryFunction (functor functor);
Codul care va vorbi cu modelul ar construi un anumit functor și i-ar transmite
Funcția Bibliotecă:
MyClass myClass;
SpecificFunctor functor (&myclass, MyClass::MyMethod);
Cand Funcția Bibliotecă este gata, execută apelarea utilizând operator() pe generic
functor a fost transmis și, în acest caz particular, oferă argumentul întreg:
anula
LibraryFunction (functor functor)
{
// Executați funcția de bibliotecă
functor(1234);
}
Observa asta Funcția Bibliotecă este complet decuplat de tipul specific de client.
Conexiunea se face prin polimorfismul Functor.
API-ul Callback în ns-3 implementează apeluri orientate pe obiect folosind mecanismul functor.
Acest API de apel invers, fiind bazat pe șabloane C++, este sigur de tip; adică efectuează statică
verificări de tip pentru a impune compatibilitatea corectă a semnăturilor între apelanți și cei apelați. Este
prin urmare, mai sigur de utilizat decât indicatorii de funcție tradiționali, dar sintaxa poate
arata impunator la inceput. Această secțiune este concepută pentru a vă ghida prin sistemul de apel invers
astfel încât să vă simțiți confortabil să îl folosiți ns-3.
Utilizarea il Callback API
API-ul Callback este destul de minim, oferind doar două servicii:
1. declarație tip callback: o modalitate de a declara un tip de callback cu o semnătură dată,
și,
2. instanțiere de apel invers: o modalitate de a instanția un apel invers de redirecționare generat de șablon
care poate redirecționa orice apel către o altă metodă de membru al clasei C++ sau către o funcție C++.
Acest lucru se observă cel mai bine prin mersul printr-un exemplu, bazat pe samples/main-callback.cc.
Utilizarea il Callback API implementate cu static funcții
Luați în considerare o funcție:
dublu static
CbOne (dublu a, dublu b)
{
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
retur a;
}
Luați în considerare și următorul fragment de program principal:
int main (int argc, char *argv[])
{
// tip de returnare: dublu
// primul tip de argument: dublu
// al doilea tip de argument: dublu
Suna inapoi unu;
}
Acesta este un exemplu de apel invers în stil C -- unul care nu include sau nu are nevoie de un acest
indicator. Șablonul funcției Callback este în esență declarația variabilei
conţinând pointer-la-funcţie. În exemplul de mai sus, am arătat în mod explicit un indicator
la o funcție care a returnat un număr întreg și a luat un singur întreg ca parametru, The
Callback funcția șablon este o versiune generică a acesteia -- este folosită pentru a declara tipul
a unui apel invers.
NOTĂ:
Cititorii care nu sunt familiarizați cu șabloanele C++ pot consulta
http://www.cplusplus.com/doc/tutorial/templates/.
Callback șablonul necesită un argument obligatoriu (tipul de returnare al funcției către
fie atribuite acestui apel invers) și până la cinci argumente opționale, fiecare specificând
tipul argumentelor (dacă funcția dvs. de apel inversă are mai mult de cinci argumente,
atunci acest lucru poate fi gestionat prin extinderea implementării callback).
Deci, în exemplul de mai sus, avem un apel invers declarat numit „unul” care va fi în cele din urmă
țineți apăsat un indicator de funcție. Semnătura funcției pe care o va deține trebuie să revină
duble și trebuie să susțină două argumente duble. Dacă cineva încearcă să treacă o funcție a cărei
semnătura nu se potrivește cu callback-ul declarat, va apărea o eroare de compilare. De asemenea, dacă
se încearcă să atribuie unui apel invers unul incompatibil, compilarea va reuși dar a
NS_FATAL_ERROR în timpul rulării va fi generat. Programul exemplu
src/core/examples/main-callback.cc demonstrează ambele cazuri de eroare la sfârșitul
il main () programul.
Acum, trebuie să legăm împreună această instanță de apel invers și funcția țintă reală
(CbOne). Observați mai sus că CbOne are aceleași tipuri de semnături de funcție ca și callback--
asta e important. Putem transmite orice astfel de funcție scrisă corect la acest apel invers.
Să ne uităm la asta mai atent:
static dublu CbOne (dublu a, dublu b) {}
^ ^ ^
| | |
| | |
Suna inapoi unu;
Puteți lega o funcție la un apel invers numai dacă are semnătura potrivită. Primul
Argumentul șablon este tipul returnat, iar argumentele șablonului suplimentare sunt tipurile
a argumentelor semnăturii funcţiei.
Acum, să legăm apelul nostru „unu” la funcția care se potrivește cu semnătura sa:
// construiește o instanță de apel invers care indică funcția cbOne
unul = MakeCallback (&CbOne);
Acest apel la MakeCallback este, în esenţă, crearea unuia dintre functorii de specialitate
menționat mai sus. Variabila declarată folosind Callback funcția șablon va fi
să joace rolul functorului generic. Atribuirea unu = MakeCallback (&CbOne) is
cast-ul care convertește functorul specializat cunoscut de apelat într-un functor generic
cunoscut de apelant.
Apoi, mai târziu în program, dacă este nevoie de apel invers, acesta poate fi utilizat după cum urmează:
NS_ASSERT (!one.IsNull ());
// invocă funcția cbOne prin instanța de apel invers
dublu retOne;
retOne = unu (10.0, 20.0);
Cecul pentru IsNull() se asigură că apelul invers nu este nul -- că există o funcție
pentru a suna în spatele acestui apel invers. Atunci, unu() execută genericul operator() ceea ce este cu adevărat
supraîncărcat cu o implementare specifică a operator() și returnează același rezultat ca și cum
CbOne() fusese sunat direct.
Utilizarea il Callback API implementate cu membru funcții
În general, nu veți apela funcții statice, ci în schimb funcții publice membre ale
un obiect. În acest caz, este necesar un argument suplimentar pentru funcția MakeCallback, pentru a
spuneți sistemului pe ce obiect trebuie invocată funcția. Luați în considerare acest exemplu,
tot de la main-callback.cc:
clasa MyCb {
public:
int CbTwo (dublu a) {
std::cout << "invoke cbTwo a=" << a << std::endl;
retur -5;
}
};
intmain()
{
...
// tip returnare: int
// primul tip de argument: dublu
Suna inapoi Două;
MyCb cb;
// construiește o instanță de apel invers care indică MyCb::cbTwo
doi = MakeCallback (&MyCb::CbTwo, &cb);
...
}
Aici, trecem un indicator de obiect suplimentar către MakeCallback<> funcţie. Amintiți-vă din
secțiunea de fundal de mai sus Operator() va folosi indicatorul către sintaxa membru atunci când acesta
execută pe un obiect:
operator virtual int() (ARG arg)
{
(*m_p.*m_pmi)(arg);
}
Și așa a trebuit să furnizăm cele două variabile (m_p și m_pmi) când am făcut specificul
functor. Linia:
doi = MakeCallback (&MyCb::CbTwo, &cb);
face tocmai asta. În acest caz, când Două () este invocat:
int rezultat = doi (1.0);
va avea ca rezultat un apel către CbDoi funcția membru (metodă) pe obiectul indicat de
&cb.
Clădire Null Rambursări
Este posibil ca apelurile inverse să fie nule; prin urmare, ar putea fi înțelept să verificați înainte de a le folosi.
Există o construcție specială pentru un apel invers nul, care este de preferat decât simpla trecere
„0” ca argument; este MakeNullCallback<> construi:
doi = MakeNullCallback ();
NS_ASSERT (două.IsNull ());
Invocarea unui callback nul este la fel ca invocarea unui indicator de funcție nulă: se va bloca la
timpul de rulare.
Legat Rambursări
O extensie foarte utilă a conceptului de functor este cea a unui Bound Callback. Anterior ea
s-a menționat că închiderile erau inițial apeluri de funcție împachetate pentru mai târziu
execuţie. Observați că în toate descrierile de apel invers de mai sus, nu există nicio modalitate de a face acest lucru
împachetați orice parametri pentru a fi utilizați mai târziu -- când Callback se numește via operator().
Toți parametrii sunt furnizați de funcția de apelare.
Ce se întâmplă dacă se dorește să se permită funcției client (cea care oferă apelul înapoi).
oferi unii dintre parametrii? Alexandrescu apelează procesul de a permite unui client să
specificați unul dintre parametri "legare". Unul dintre parametrii operator() a fost
legat (fixat) de client.
Unele dintre codurile noastre de urmărire pcap oferă un exemplu frumos în acest sens. Există o funcție care
trebuie apelat ori de câte ori se primește un pachet. Această funcție numește un obiect care
de fapt, scrie pachetul pe disc în formatul de fișier pcap. Semnătura unuia dintre acestea
functiile vor fi:
static void DefaultSink (Ptr dosar, Ptr p);
Cuvântul cheie static înseamnă că aceasta este o funcție statică care nu are nevoie de a acest indicator, deci
va folosi apeluri inverse în stil C. Nu vrem să știm despre codul de apelare
orice în afară de Pachetul. Ceea ce vrem în codul de apel este doar un apel care arată astfel:
m_promiscSnifferTrace (m_currentPkt);
Ceea ce vrem să facem este să lega il Ptr fişier la apelul invers specific
implementare atunci când este creat și aranjați pentru operator() a apelului la
furnizați acel parametru gratuit.
Noi oferim MakeBoundCallback funcție de șablon în acest scop. Este nevoie la fel
parametri ca MakeCallback funcția șablon, dar ia și parametrii să fie
legat. În cazul exemplului de mai sus:
MakeBoundCallback (&DefaultSink, fișier);
va crea o implementare specifică de apel invers care știe să adauge în limita suplimentară
argumente. Conceptual, extinde functorul specific descris mai sus cu unul sau mai mulți
argumente legate:
șablon
clasa SpecificFunctor : Functor public
{
public:
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
{
m_p = p;
m_pmi = pmi;
m_boundArg = boundArg;
}
operator virtual int() (ARG arg)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
privat:
void (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};
Puteți vedea că atunci când functorul specific este creat, argumentul legat este salvat în
functor / obiectul callback în sine. Cand operator() este invocat cu single-ul
parametru, ca în:
m_promiscSnifferTrace (m_currentPkt);
punerea în aplicare a operator() adaugă parametrul legat în apelul de funcție actual:
(*m_p.*m_pmi)(m_boundArg, arg);
Este posibil să legați și două sau trei argumente. Să presupunem că avem o funcție cu
semnătură:
static void NotifyEvent (Ptr a, Ptr b, MyEventType e);
Se poate crea legat de apel invers legare primele două argumente, cum ar fi:
MakeBoundCallback (&NotifyEvent, a1, b1);
presupunând a1 și b1 sunt obiecte de tip A și B respectiv. La fel pentru trei
argumente pe care le-ar avea funcția cu o semnătură:
static void NotifyEvent (Ptr a, Ptr b, MyEventType e);
Legarea a trei argumente gata cu:
MakeBoundCallback (&NotifyEvent, a1, b1, c1);
din nou presupunând a1, b1 și c1 sunt obiecte de tip A, B și C respectiv.
Acest tip de legare poate fi folosit pentru schimbul de informații între obiecte în simulare;
în special, apelurile inverse legate pot fi folosite ca apeluri urmărite, care vor fi descrise în
următoarea secțiune.
urmărite Rambursări
Înlocuitor subsecțiunea
Callback Locații in ns-3
Unde sunt folosite frecvent apelurile inverse ns-3? Iată câteva dintre cele mai vizibile
utilizatori tipici:
· Socket API
· Layer-2/Layer-3 API
· Subsistem de urmărire
· API între IP și subsistemele de rutare
Punerea în aplicare detalii
Fragmentele de cod de mai sus sunt simpliste și concepute doar pentru a ilustra mecanismul
în sine. Codul de apelare real este destul de complicat și foarte intens de șabloane și a
nu este necesară înțelegerea profundă a codului. Dacă sunt interesați, utilizatorii experți pot găsi
urmatoare utile.
Codul a fost scris inițial pe baza tehnicilor descrise în
http://www.codeproject.com/cpp/TTLFunction.asp. Ulterior a fost rescris pentru a urma
arhitectura conturată în Modern C ++ De proiectare, General Programare și Amenajări Modele
Aplicat, Alexandrescu, capitol 5, generalizat Functorii.
Acest cod folosește:
· parametrii de șablon implicit pentru a scuti utilizatorii de a specifica parametrii goli când
numărul de parametri este mai mic decât numărul maxim acceptat
· limbajul pimpl: clasa Callback este transmisă în jurul valorii și deleagă cheia
lucrarea la indicatorul său pimpl.
· se pot folosi două implementări pimpl care derivă din FunctorCallbackImpl CallbackImpl
cu orice tip de functor în timp ce MemPtrCallbackImpl poate fi folosit cu pointeri către membru
funcții.
· o implementare a listei de referință pentru a implementa semantica valorii Callback-ului.
Acest cod se îndepărtează mai ales de implementarea Alexandrescu prin faptul că nu este
utilizați liste de tipuri pentru a specifica și transmite tipurile de argumente de apel invers. Desigur,
de asemenea, nu folosește semantica de distrugere a copiei și se bazează mai degrabă pe o listă de referințe
autoPtr pentru a menține indicatorul.
Obiect model
ns-3 este fundamental un sistem de obiecte C++. Obiectele pot fi declarate și instanțiate ca
obișnuit, conform regulilor C++. ns-3 adaugă, de asemenea, unele caracteristici la obiectele tradiționale C++, cum ar fi
descrise mai jos, pentru a oferi mai multe funcționalități și caracteristici. Acest capitol manual este
menită să introducă cititorul în ns-3 model de obiect.
Această secțiune descrie designul clasei C++ pentru ns-3 obiecte. Pe scurt, mai multe design
modelele utilizate includ design clasic orientat pe obiecte (interfețe polimorfe și
implementări), separarea interfeței și implementarea, publicul non-virtual
model de proiectare a interfeței, o facilitate de agregare a obiectelor și numărarea referințelor pentru
gestionarea memoriei. Cei familiarizați cu modele de componente precum COM sau Bonobo vor
recunoaște elementele designului în ns-3 modelul de agregare a obiectelor, deși ns-3
designul nu este strict în conformitate cu nici unul.
Orientat pe obiecte comportament
Obiectele C++, în general, oferă capabilități comune orientate pe obiecte (abstracție,
încapsulare, moștenire și polimorfism) care fac parte din clasicul orientat pe obiect
proiecta. ns-3 obiectele folosesc aceste proprietăți; de exemplu:
Adresa clasei
{
public:
Abordare ();
Adresă (uint8_t type, const uint8_t *buffer, uint8_t len);
Adresa (const Adresa si adresa);
Adresă &operator = (const Adresă &adresă);
...
privat:
uint8_t m_type;
uint8_t m_len;
...
};
Obiect de bază clase
Există trei clase de bază speciale utilizate în ns-3. Clase care moștenesc din aceste baze
clasele pot instanția obiecte cu proprietăți speciale. Aceste clase de bază sunt:
· clasa Obiect
· clasa ObjectBase
· clasa SimpleRefCount
Nu se cere ca ns-3 obiectele moștenesc din aceste clase, dar cele care primesc
proprietăți speciale. Clase care derivă din clasă Obiect obține următoarele proprietăți.
· cel ns-3 tip și sistem de atribute (vezi Atribute)
· un sistem de agregare a obiectelor
· un sistem de numărare a referințelor cu indicator inteligent (clasa Ptr)
Clase care derivă din clasă ObjectBase obțineți primele două proprietăți de mai sus, dar nu
obține indicații inteligente. Clase care derivă din clasă SimpleRefCount: obțineți numai
sistem de numărare a referințelor smart-pointer.
În practică, clasă Obiect este varianta celor trei de mai sus pe care ns-3 dezvoltatorul va
întâlnire cel mai frecvent.
Memorie administrare și clasă PTR
Gestionarea memoriei într-un program C++ este un proces complex și este adesea făcut incorect sau
inconsecvent. Ne-am stabilit pe un design de numărare de referință descris după cum urmează.
Toate obiectele care utilizează contorizarea referințelor mențin un număr de referință intern pentru a se determina
când un obiect se poate șterge singur în siguranță. De fiecare dată când se obține un pointer către un
interfață, numărul de referințe al obiectului este incrementat prin apelare Ref(). Este
obligația utilizatorului pointerului de a în mod explicit Unref() indicatorul când ați terminat. Cand
numărul de referințe scade la zero, obiectul este șters.
· Când codul client obține un pointer de la obiectul însuși prin crearea obiectului,
sau prin GetObject, nu trebuie să crească numărul de referințe.
· Atunci când codul client obține un pointer dintr-o altă sursă (de exemplu, copierea unui pointer) trebuie
apel Ref() pentru a crește numărul de referințe.
· Toți utilizatorii indicatorului obiect trebuie să apeleze Unref() pentru a elibera referința.
Povara pentru a suna Unref() este oarecum ușurată prin utilizarea numărării de referință
clasa de indicator inteligent descrisă mai jos.
Utilizatori care utilizează un API de nivel scăzut care doresc să aloce în mod explicit obiecte care nu au fost numărate cu referințe
pe heap, folosind operatorul new, sunt responsabili pentru ștergerea unor astfel de obiecte.
Referinţă socoteală inteligent indicatorul (Ptr)
apel Ref() și Unref() tot timpul ar fi greoi, deci ns-3 oferă un inteligent
clasa pointer PTR similar Boost::intrusive_ptr. Această clasă smart-pointer presupune că
tipul de bază oferă o pereche de Ref și neref metode care se așteaptă să o facă
incrementează și decrementează refcountul intern al instanței obiectului.
Această implementare vă permite să manipulați indicatorul inteligent ca și cum ar fi un normal
pointer: îl puteți compara cu zero, îl puteți compara cu alți pointeri, îi puteți atribui zero
ea etc.
Este posibil să extrageți indicatorul brut din acest indicator inteligent cu ajutorul GetPointer()
și PeekPointer() metode.
Dacă doriți să stocați un obiect nou într-un indicator inteligent, vă recomandăm să utilizați
CreateObject șablon funcționează pentru a crea obiectul și a-l stoca într-un pointer inteligent către
evita scurgerile de memorie. Aceste funcții sunt cu adevărat mici funcții de confort și scopul lor
este doar pentru a vă economisi un pic de tastare.
CreateObject și Crează
Obiectele din C++ pot fi create static, dinamic sau automat. Acest lucru este adevărat
pentru ns-3 de asemenea, dar unele obiecte din sistem au câteva cadre suplimentare disponibile.
Mai exact, obiectele numărate de referință sunt de obicei alocate folosind un model Creare sau
Metoda CreateObject, după cum urmează.
Pentru obiectele care derivă din clasă Obiect:
Ptr dispozitiv = CreateObject ();
Vă rugăm să nu creați astfel de obiecte folosind operator nou; creați-le folosind CreateObject()
in schimb.
Pentru obiectele care derivă din clasă SimpleRefCount, sau alte obiecte care acceptă utilizarea
clasă smart pointer, este disponibilă o funcție de ajutor șablon și se recomandă utilizarea:
Ptr b = Creare ();
Acesta este pur și simplu un înveliș în jurul operatorului nou care gestionează corect numărarea referințelor
sistemului.
Pe scurt, folosiți Crea dacă B nu este un obiect, ci folosește doar numărarea referințelor (de ex
Pachet), si foloseste CreateObject dacă B derivă din ns3::Obiect.
agregare
ns-3 sistemul de agregare a obiectelor este motivat în mare parte de recunoașterea faptului că a
caz comun de utilizare pentru ns-2 a fost utilizarea moștenirii și a polimorfismului pentru a extinde
modele de protocol. De exemplu, derivă versiuni specializate ale TCP, cum ar fi RenoTcpAgent
din (și suprascrie funcții din) clasa TcpAgent.
Cu toate acestea, două probleme care au apărut în ns-2 modelul sunt dezamăgiți și „bază slabă
clasa." Downcasting se referă la procedura de utilizare a unui indicator de clasă de bază către un obiect și
interogând-o în timpul execuției pentru a afla informații de tip, utilizate pentru a arunca în mod explicit pointerul
către un pointer de subclasă, astfel încât API-ul de subclasă să poată fi utilizat. Clasa de bază slabă se referă la
probleme care apar atunci când o clasă nu poate fi reutilizată efectiv (derivată din) deoarece aceasta
lipsește funcționalitatea necesară, ceea ce face ca dezvoltatorul să fie nevoit să modifice clasa de bază și
provocând proliferarea apelurilor API de clasă de bază, dintre care unele pot să nu fie semantic
corect pentru toate subclasele.
ns-3 folosește o versiune a modelului de proiectare a interfeței de interogare pentru a evita aceste probleme.
Acest design se bazează pe elemente ale Component Obiect Modele Usi și GNOME bonobo deși
compatibilitatea completă la nivel binar a componentelor înlocuibile nu este acceptată și avem
a încercat să simplifice sintaxa și impactul asupra dezvoltatorilor de modele.
Exemple
agregare exemplu
Nod este un bun exemplu de utilizare a agregării în ns-3. Rețineți că nu sunt derivate
clase de Noduri în ns-3 precum clasa InternetNode. În schimb, componentele (protocoalele) sunt
agregate la un nod. Să ne uităm la modul în care unele protocoale Ipv4 sunt adăugate la un nod.:
gol static
AddIpv4Stack(Ptr nodul)
{
Ptr ipv4 = CreateObject ();
ipv4->SetNode (nod);
nod->AggregateObject (ipv4);
Ptr ipv4Impl = CreateObject ();
ipv4Impl->SetIpv4 (ipv4);
nod->AggregateObject (ipv4Impl);
}
Rețineți că protocoalele IPv4 sunt create folosind CreateObject(). Apoi, acestea sunt agregate
la nod. În acest mod, clasa de bază Node nu trebuie editată pentru a permite utilizatorilor
cu un indicator Node de clasă de bază pentru a accesa interfața Ipv4; utilizatorii pot cere nodului a
pointer către interfața sa Ipv4 în timpul rulării. Modul în care utilizatorul întreabă nodul este descris în
următoarea subsecțiune.
Rețineți că este o eroare de programare pentru a agrega mai mult de un obiect de același tip
an ns3::Obiect. Deci, de exemplu, agregarea nu este o opțiune pentru stocarea tuturor
prize active ale unui nod.
GetObject exemplu
GetObject este o modalitate sigură de tip de a realiza o reducere sigură și de a permite interfețelor să existe
găsit pe un obiect.
Luați în considerare un pointer de nod m_node care indică un obiect Node care are o implementare a
IPv4 a fost agregat anterior la acesta. Codul clientului dorește să configureze o rută implicită. La
face acest lucru, trebuie să acceseze un obiect din nod care are o interfață la redirecționarea IP
configurație. Ea efectuează următoarele:
Ptr ipv4 = m_node->GetObject ();
Dacă nodul de fapt nu are un obiect Ipv4 agregat la el, atunci metoda va fi
returnează nul. Prin urmare, este o bună practică să verificați valoarea returnată de la o astfel de funcție
apel. Dacă are succes, utilizatorul poate utiliza acum Ptr la obiectul Ipv4 care a fost anterior
agregate la nod.
Un alt exemplu despre cum s-ar putea folosi agregarea este adăugarea unor modele opționale la obiecte. Pentru
de exemplu, un obiect Node existent poate avea un obiect „Model energetic” agregat la el
timpul de rulare (fără a modifica și recompila clasa nodului). Un model existent (cum ar fi un
dispozitiv de rețea fără fir) poate apoi „GetObject” pentru modelul energetic și acționa corespunzător
dacă interfața a fost fie încorporată în obiectul Node de bază, fie agregată la
acesta în timpul rulării. Cu toate acestea, alte noduri nu trebuie să știe nimic despre modelele energetice.
Sperăm că acest mod de programare va necesita mult mai puțină nevoie de modificare pentru dezvoltatori
clasele de bază.
Obiect fabrici
Un caz de utilizare obișnuit este de a crea o mulțime de obiecte configurate similar. Se poate în mod repetat
apel CreateObject() dar există, de asemenea, un model de design din fabrică utilizat în ns-3 sistemului.
Este foarte utilizat în API-ul „helper”.
Clasă ObjectFactory poate fi folosit pentru a instanția obiecte și pentru a configura atributele pe
acele obiecte:
void SetTypeId (TypeId tid);
void Set (std::nume șir, const AttributeValue &value);
Ptr Create (void) const;
Prima metodă permite folosirea ns-3 Sistem TypeId pentru a specifica tipul de obiecte
creată. Al doilea permite setarea atributelor obiectelor care urmează să fie create și
a treia permite să creeze obiectele în sine.
De exemplu:
Fabrica ObjectFactory;
// Faceți ca această fabrică să creeze obiecte de tip FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Faceți ca acest obiect din fabrică să modifice o valoare implicită a unui atribut, pentru
// obiecte create ulterior
factory.Set ("SystemLoss", DoubleValue (2.0));
// Creați un astfel de obiect
Ptr obiect = fabrică.Creează ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Creați un alt obiect cu un SystemLoss diferit
Ptr obiect = fabrică.Creează ();
Doborâre
O întrebare care a apărut de mai multe ori este: „Dacă am un indicator de clasă de bază (Ptr) către an
obiect și vreau indicatorul de clasă derivată, ar trebui să downcast (prin C++ dynamic cast) la
obține indicatorul derivat sau ar trebui să folosesc sistemul de agregare a obiectelor pentru a GetObject<> ()
pentru a găsi un Ptr la interfața cu subclasa API?"
Răspunsul la aceasta este că, în multe situații, ambele tehnici vor funcționa. ns-3 oferă o
funcție șablon pentru a face sintaxa de turnare dinamică a obiectului mult mai utilizator
prietenos:
șablon
Ptr
DynamicCast (Ptr const&p)
{
întoarcere Ptr (dynamic_cast (PeekPointer (p)));
}
DynamicCast funcționează atunci când programatorul are un pointer de tip de bază și testează împotriva unui
indicatorul de subclasă. GetObject funcționează atunci când se caută diferite obiecte agregate, dar și
funcționează cu subclase, în același mod ca DynamicCast. Dacă nu este sigur, programatorul ar trebui
utilizați GetObject, deoarece funcționează în toate cazurile. Dacă programatorul cunoaște ierarhia de clasă a
obiectul luat în considerare, este mai direct să utilizați doar DynamicCast.
Configuraţie și Atribute
In ns-3 simulări, există două aspecte principale ale configurației:
· Topologia simulării și modul în care obiectele sunt conectate.
· Valorile utilizate de modelele instanțiate în topologie.
Acest capitol se concentrează pe al doilea element de mai sus: cât de multe valori sunt utilizate în ns-3 sunt
organizat, documentat și modificabil de ns-3 utilizatori. ns-3 sistemul de atribute este, de asemenea
fundamentarea modului în care urmele și statisticile sunt adunate în simulator.
În cursul acestui capitol vom discuta diferitele modalități de setare sau modificare a valorilor
folosit de ns-3 obiecte model. În ordinea crescătoare a specificității, acestea sunt:
┌─────────────────────────────────┬─────────────── ───────────────────┐
│Metoda │ Domeniul de aplicare │
├─────────────────────────────────┼─────────────── ───────────────────┤
│Valori implicite ale atributelor setate │ Afectează toate instanțele │
│când Atributele sunt definite în │ clasa. │
│GetTypeId (). │ │
└─────────────────────────────────┴─────────────── ───────────────────┘
│Linie de comanda │ Afectează toate cazurile viitoare. │
│Config::SetDefault() │ │
│ConfigStore │ │
├─────────────────────────────────┼─────────────── ───────────────────┤
│ObjectFactory │ Afectează toate instanțele create │
│ │ cu fabrica. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│XHelperSetAttribute () │ Afectează toate instanțele create de │
│ │ ajutorul. │
├─────────────────────────────────┼─────────────── ───────────────────┤
│MyClass::SetX () │ Modifică această instanță specială. │
│Object::SetAttribute () │ În general, aceasta este singura formă │
│Config::Set() │ care poate fi programat să se modifice │
│ │ o instanță odată cu simularea │
│ │ rulează. │
└─────────────────────────────────┴─────────────── ───────────────────┘
Prin „specificitate” înțelegem că metodele din rândurile ulterioare din tabel înlocuiesc valorile setate
prin metodele anterioare și afectează de obicei mai puține cazuri decât metodele anterioare.
Înainte de a aprofunda detaliile sistemului de valori ale atributelor, vă va ajuta să revizuiți câteva
proprietățile de bază ale clasei Obiect.
Obiect Descriere
ns-3 este în esență un sistem C++ bazat pe obiecte. Prin aceasta ne referim la noile clase C++
(tipurile) pot fi declarate, definite și subclasate ca de obicei.
Multe ns-3 obiectele moștenesc de la Obiect clasa de bază. Aceste obiecte au unele suplimentare
proprietăți pe care le exploatăm pentru organizarea sistemului și îmbunătățirea managementului memoriei
dintre obiectele noastre:
· Sistem „Metadate” care leagă numele clasei la o mulțime de meta-informații despre
obiect, inclusiv:
· Clasa de bază a subclasei,
· Setul de constructori accesibili din subclasa,
· Setul de „atribute” ale subclasei,
· Dacă fiecare atribut poate fi setat sau este doar pentru citire,
· Intervalul de valori permis pentru fiecare atribut.
· Implementarea indicatorului inteligent de numărare a referințelor, pentru gestionarea memoriei.
ns-3 obiectele care utilizează sistemul de atribute derivă din oricare Obiect or ObjectBase. Cel mai
ns-3 obiectele despre care vom discuta derivă Obiect, dar câteva care sunt în afara celor deștepți
cadru de management al memoriei pointer derivă din ObjectBase.
Să revizuim câteva proprietăți ale acestor obiecte.
Locker Pointeri
După cum a fost introdus în ns-3 tutorial, ns-3 obiectele sunt memorie gestionată de a referință
socoteală inteligent indicatorul implementarea, clasa PTR.
Indicatoarele inteligente sunt utilizate pe scară largă în ns-3 API, pentru a evita transmiterea referințelor la
obiecte alocate în heap care pot cauza scurgeri de memorie. Pentru utilizarea cea mai de bază (sintaxă), tratați
un indicator inteligent ca un indicator obișnuit:
Ptr nd = ...;
nd->CallSomeFunction ();
// etc.
Deci, cum obțineți un indicator inteligent către un obiect, ca în prima linie a acestui exemplu?
CreateObject
După cum am discutat mai sus în Memory-management-and-class-Ptr, la cel mai jos nivel API, obiectele
de tip Obiect nu sunt instanțiate folosind operator nou ca de obicei, dar în schimb printr-un șablon
funcție numită CreateObject ().
O modalitate tipică de a crea un astfel de obiect este următoarea:
Ptr nd = CreateObject ();
Vă puteți gândi la acest lucru ca fiind echivalent funcțional cu:
WifiNetDevice* nd = nou WifiNetDevice ();
Obiecte care derivă din Obiect trebuie alocate pe heap folosind CreateObject (). Acestea
provenind din ObjectBase, Cum ar fi ns-3 funcții de ajutor și anteturi și trailere de pachete,
pot fi alocate pe stivă.
În unele scripturi, este posibil să nu vedeți prea multe CreateObject () apeluri în cod; aceasta este
deoarece există unele obiecte ajutătoare în vigoare care fac CreateObject () Apeluri
pentru tine.
TypeId
ns-3 clase care derivă din clasă Obiect poate include o clasă de metadate numită TypeId acea
înregistrează metainformații despre clasă, pentru a fi utilizate în agregarea obiectelor și componentă
sisteme manageriale:
· Un șir unic de identificare a clasei.
· Clasa de bază a subclasei, în cadrul sistemului de metadate.
· Setul de constructori accesibili din subclasa.
· O listă de proprietăți accesibile public („atribute”) ale clasei.
Obiect Rezumat
Punând toate aceste concepte împreună, să ne uităm la un exemplu specific: clasă Nod.
Fișierul antet public nod.h are o declarație care include un static GetTypeId ()
apel de funcție:
class Node : obiect public
{
public:
static TypeId GetTypeId (void);
...
Acest lucru este definit în nod.cc fișier după cum urmează:
TypeId
Node::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::Node")
.SetParent ()
.AddConstructor ()
.AddAttribute („DeviceList”,
„Lista dispozitivelor asociate acestui Nod.”,
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker ())
.AddAttribute ("Lista de aplicații",
„Lista aplicațiilor asociate acestui Nod.”
ObjectVectorValue (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker ())
.AddAttribute ("Id",
„Idul (întreg unic) al acestui Nod.”,
TypeId::ATTR_GET, // permiteți doar obținerea acestuia.
UintegerValue (0),
MakeUintegerAccessor (&Node::m_id),
MakeUintegerChecker ())
;
return tid;
}
Considera TypeId a ns-3 Obiect clasa ca o formă extinsă de tip runtime
informații (RTTI). Limbajul C++ include un tip simplu de RTTI pentru a suporta
dinamic_cast și tipid operatori.
SetParent () apel în definiția de mai sus este folosită împreună cu nostru
mecanisme de agregare a obiectelor pentru a permite transformarea în siguranță în sus și în jos în arbori de moștenire
în timpul GetObject (). De asemenea, permite subclaselor să moștenească Atributele părintelui lor
clasă.
AddConstructor () call este folosit împreună cu fabrica noastră de obiecte abstracte
mecanisme care ne permit să construim obiecte C++ fără a forța un utilizator să cunoască
clasa concretă a obiectului pe care îl construiește.
Cele trei apeluri la AdaugăAtribut () asociați un șir dat cu o valoare tastată puternic în
clasa. Observați că trebuie să furnizați un șir de ajutor care poate fi afișat, de exemplu,
de procesoare de linie de comandă. Fiecare Atribut este asociat cu mecanisme de accesare
variabila membru subiacent în obiect (de exemplu, MakeUintegerAccessor () spune
genericul Atribut codificați cum să ajungeți la ID-ul nodului de mai sus). Există și „Checker”
metode care sunt utilizate pentru a valida valorile în raport cu limitările intervalului, cum ar fi maxim și
valori minime admise.
Când utilizatorii doresc să creeze Noduri, de obicei vor apela o formă de CreateObject (),:
Ptr n = CreateObject ();
sau mai abstract, folosind o fabrică de obiecte, puteți crea o Nod obiect fără chiar
cunoscând tipul concret C++:
Fabrica ObjectFactory;
const std::string typeId = "ns3::Node";
factory.SetTypeId (typeId);
Ptr nod = fabrică.Creează ();
Ambele metode au ca rezultat ca atributele complet inițializate să fie disponibile în
rezultând Obiect instanțe.
În continuare vom discuta despre modul în care atributele (valorile asociate cu variabilele membre sau funcțiile lui
clasa) sunt introduse în cele de mai sus TypeId.
Atribute
Scopul sistemului de atribute este de a organiza accesul obiectelor membre interne ale a
simulare. Acest obiectiv apare deoarece, de obicei în simulare, utilizatorii vor tăia și
lipiți/modificați scripturile de simulare existente sau vor folosi constructe de simulare de nivel superior,
dar adesea va fi interesat să studieze sau să urmărească anumite variabile interne. Pentru
de exemplu, cazuri de utilizare precum:
· "I vrea la urmări il pachete on il fără fir interfață on il primul acces punct."
· "I vrea la urmări il valoare of il TCP congestie fereastră (fiecare timp it schimbări) on a
special TCP priză."
· "I vrea a descărca of toate Valorile acea au fost utilizat in my simulare."
În mod similar, utilizatorii pot dori acces precis la variabilele interne din simulare sau
poate doriți să schimbați în general valoarea inițială utilizată pentru un anumit parametru
obiecte create ulterior. În cele din urmă, utilizatorii ar putea dori să știe ce variabile pot fi setate
și recuperabil într-o configurație de simulare. Acest lucru nu este doar pentru simulare directă
interacțiune pe linia de comandă; luați în considerare și o (viitoare) interfață grafică de utilizator care
ar dori să poată oferi o caracteristică prin care un utilizator poate face clic dreapta pe un nod pe
pânză și vedeți o listă ierarhică, organizată de parametri care pot fi setați pe
nodul și obiectele sale membre constitutive și textul de ajutor și valorile implicite pentru fiecare
parametru.
Definire Atribute
Oferim utilizatorilor o modalitate de a accesa valorile în adâncimea sistemului, fără a fi nevoiți să verifice
accesorii (pointerii) prin sistem și parcurgeți lanțurile de indicatori pentru a ajunge la ele. Luați în considerare a
clasă DropTailQueue care are o variabilă membru care este un întreg fără semn m_maxPackets;
această variabilă membru controlează adâncimea cozii.
Dacă ne uităm la declarația de DropTailQueue, vedem următoarele:
clasa DropTailQueue : public Queue {
public:
static TypeId GetTypeId (void);
...
privat:
std::queue > m_pachete;
uint32_t m_maxPackets;
};
Să luăm în considerare lucrurile pe care un utilizator ar putea dori să le facă cu valoarea m_maxPackets:
· Setați o valoare implicită pentru sistem, astfel încât de fiecare dată când este nouă DropTailQueue este creat,
acest membru este inițializat la acea implicită.
· Setați sau obțineți valoarea pe o coadă deja instanțiată.
Lucrurile de mai sus necesită de obicei furnizarea set () și Obține () funcții și un anumit tip de
valoarea implicită globală.
În ns-3 sistem de atribute, aceste definiții de valori și înregistrări ale funcțiilor accesorii
sunt mutate în TypeId clasă; de exemplu,.:
NS_OBJECT_ENSURE_REGISTERED (DropTailQueue);
TypeId
DropTailQueue::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.SetParent ()
.AddConstructor ()
.AddAttribute ("MaxPackets",
„Numărul maxim de pachete acceptate de acest DropTailQueue.”,
UintegerValue (100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MakeUintegerChecker ())
;
return tid;
}
AdaugăAtribut () metoda efectuează o serie de lucruri pentru m_maxPackets valoare:
· Legarea variabilei membru (de obicei private). m_maxPackets la un șir public
„MaxPackets”.
· Furnizarea unei valori implicite (100 de pachete).
· Furnizarea unui text de ajutor care definește semnificația valorii.
· Furnizarea unui „Verificator” (neutilizat în acest exemplu) care poate fi folosit pentru a stabili limite pe
intervalul admisibil de valori.
Punctul cheie este că acum valoarea acestei variabile și valoarea sa implicită sunt accesibile
în spațiul de nume de atribut, care se bazează pe șiruri precum „MaxPackets” și TypeId nume
siruri de caractere. În secțiunea următoare, vom oferi un exemplu de script care arată cum pot utilizatorii
manipula aceste valori.
Rețineți că inițializarea atributului se bazează pe macrocomandă NS_OBJECT_ENSURE_REGISTERED
(DropTailQueue) fiind chemat; dacă nu lăsați acest lucru din noua implementare a clasei dvs., dvs
atributele nu vor fi inițializate corect.
Deși am descris cum să creăm atribute, încă nu am descris cum să accesăm
și gestionați aceste valori. De exemplu, nu există globale.h fișier antet unde sunt acestea
depozitat; atributele sunt stocate cu clasele lor. Întrebările care apar în mod natural sunt cum
utilizatorii învață cu ușurință despre toate atributele modelelor lor și cum învață un utilizator
accesați aceste atribute sau documentați valorile lor ca parte a evidenței lor
simulare?
Documentație detaliată a atributelor reale definite pentru un tip și o listă globală a
toate atributele definite sunt disponibile în documentația API. Pentru restul
document, vom demonstra diferitele modalități de obținere și setare a atributului
valori.
reglaj Mod implicit Valori
Config::SetDefault și Linie de comanda
Să ne uităm la modul în care un script de utilizator ar putea accesa o anumită valoare de atribut. Vom merge
utilizați src/point-to-point/examples/main-attribute-value.cc scenariu pentru ilustrare, cu
unele detalii eliminate. The principal functia incepe:
// Acesta este un exemplu de bază despre cum să utilizați sistemul de atribute pentru
// setați și obțineți o valoare în sistemul de bază; și anume un nesemnat
// întregul numărului maxim de pachete dintr-o coadă
//
int
main (int argc, char *argv[])
{
// Implicit, atributul MaxPackets are o valoare de 100 de pachete
// (acest implicit poate fi observat în funcția DropTailQueue::GetTypeId)
//
// Aici, l-am setat la 80 de pachete. Am putea folosi unul dintre cele două tipuri de valori:
// o valoare bazată pe șir sau o valoare Uinteger
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// Apelul funcției de mai jos este redundant
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));
// Permiteți utilizatorului să înlocuiască oricare dintre valorile implicite și cele de mai sus
// SetDefaults () în timpul rulării, prin argumente din linia de comandă
// De exemplu, prin „--ns3::DropTailQueue::MaxPackets=80”
CommandLine cmd;
// Aceasta oferă încă o modalitate de a seta valoarea din linia de comandă:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);
Principalul lucru de observat în cele de mai sus sunt cele două apeluri echivalente la Config::SetDefault
(). Acesta este modul în care setăm valoarea implicită pentru toate instanțiate ulterior
DropTailQueues. Ilustram ca doua tipuri de Valoare clase, a StringValue și
UintegerValue clasa, poate fi folosit pentru a atribui valoarea atributului numit de
„ns3::DropTailQueue::MaxPackets”.
De asemenea, este posibil să manipulați Atributele folosind Linie de comanda; am vazut cateva exemple
la începutul Tutorialului. În special, este simplu să adăugați un argument scurt
nume, cum ar fi --maxPackets, pentru un Atribut care este deosebit de relevant pentru modelul dvs.,
în acest caz „ns3::DropTailQueue::MaxPackets”. Aceasta are caracteristica suplimentară că
șirul de ajutor pentru Atribut va fi tipărit ca parte a mesajului de utilizare pentru script.
Pentru informaţii suplimentare, consultaţi Linie de comanda Documentația API.
Acum, vom crea câteva obiecte folosind API-ul de nivel scăzut. Cozile noastre nou create vor fi
nu are m_maxPackets inițializat la 100 de pachete, așa cum este definit în
DropTailQueue::GetTypeId () funcție, dar la 80 de pachete, din cauza a ceea ce am făcut mai sus
valori implicite.:
Ptr n0 = CreateObject ();
Ptr net0 = CreateObject ();
n0->AddDevice (net0);
Ptr q = CreateObject ();
net0->AddQueue(q);
În acest moment, am creat un singur Nod (n0) și un singur PointToPointNetDevice
(net0), și a adăugat a DropTailQueue (q) Pentru a net0.
constructori, Ajutoare și ObjectFactory
Combinațiile arbitrare de atribute pot fi setate și preluate de la ajutor și de la nivel scăzut
API-uri; fie de la constructori înșiși:
Ptr p =
CreateObjectWithAttributes
("MinX", DoubleValue (-100.0),
„MinY”, DoubleValue (-100.0),
„DeltaX”, DoubleValue (5.0),
„DeltaY”, DoubleValue (20.0),
„GridWidth”, UintegerValue (20),
„LayoutType”, StringValue („RowFirst”));
sau din API-urile de ajutor de nivel superior, cum ar fi:
mobilitate.SetPositionAllocator
("ns3::GridPositionAllocator",
„MinX”, DoubleValue (-100.0),
„MinY”, DoubleValue (-100.0),
„DeltaX”, DoubleValue (5.0),
„DeltaY”, DoubleValue (20.0),
„GridWidth”, UintegerValue (20),
„LayoutType”, StringValue („RowFirst”));
Nu îl ilustrăm aici, dar puteți configura și un ObjectFactory cu noi valori
pentru atribute specifice. Instanțele create de ObjectFactory va avea acelea
atribute stabilite în timpul construcției. Acest lucru este foarte asemănător cu utilizarea unuia dintre API-urile de ajutor
pentru clasa.
Pentru a revizui, există mai multe moduri de a seta valori pentru atribute pentru instanțe de clasă la be
a creat in il viitor:
· Config::SetDefault ()
· CommandLine::AddValue ()
· CreateObjectWithAttributes<> ()
· Diverse API-uri de ajutor
Dar dacă ați creat deja o instanță și doriți să schimbați valoarea
atribut? În acest exemplu, cum putem manipula m_maxPackets valoarea deja
instanțiat DropTailQueue? Iată diferite moduri de a face asta.
Schimbarea Valori
SmartPointer
Să presupunem că un indicator inteligent (PTR) la un dispozitiv de rețea relevant este în mână; în curent
exemplu, este net0 indicator.
O modalitate de a schimba valoarea este să accesați un pointer către coada de așteptare și să o modificați
atribut.
În primul rând, observăm că putem obține un pointer către (clasa de bază) Coadă de il
PointToPointNetDevice atribute, unde este numit „TxQueue”:
PointerValue tmp;
net0->GetAttribute ("TxQueue", tmp);
Ptr txQueue = tmp.GetObject ();
Utilizarea GetObject () funcție, putem efectua o reducere sigură la a DropTailQueue, În cazul în care
„MaxPackets” este un atribut:
Ptr dtq = txQueue->GetObject ();
NS_ASSERT (dtq != 0);
Apoi, putem obține valoarea unui atribut din această coadă. Am introdus învelișul
Valoare clase pentru tipurile de date subiacente, similare cu pachetele Java din jurul acestor tipuri,
deoarece sistemul de atribute stochează valori serializate în șiruri, și nu tipuri disparate.
Aici, valoarea atributului este atribuită lui a UintegerValue, Şi Obține () metoda pe aceasta
valoarea produce (dezvoltat) uint32_t.:
Limită UintegerValue;
dtq->GetAttribute ("MaxPackets", limită);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " pachete");
Rețineți că declinul de mai sus nu este cu adevărat necesar; am fi putut obține atributul
valoare direct de la txQueue, care este un Obiect:
txQueue->GetAttribute ("MaxPackets", limită);
NS_LOG_INFO ("2. txQueue limit: " << limit.Get () << " pachete");
Acum, să-l setăm la o altă valoare (60 de pachete):
txQueue->SetAttribute("MaxPackets", UintegerValue (60));
txQueue->GetAttribute ("MaxPackets", limită);
NS_LOG_INFO ("3. Limita txQueue schimbată: " << limit.Get () << " pachete");
config Spațiu de nume Cale
O modalitate alternativă de a ajunge la atribut este utilizarea spațiului de nume de configurare. Aici,
acest atribut se află pe o cale cunoscută în acest spațiu de nume; această abordare este utilă dacă una
nu are acces la pointerii de bază și ar dori să configureze un anumit
atribut cu o singură declarație.:
Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
UintegerValue (25));
txQueue->GetAttribute ("MaxPackets", limită);
NS_LOG_INFO ("4. Limita txQueue a fost modificată prin spațiul de nume: "
<< limit.Get () << " pachete ");
Calea de configurare are adesea forma „.../
nume>/ /.../ / " a face referire la o anumită instanță prin indexul unui
obiect în recipient. În acest caz, primul container este lista tuturor Nods; cel
al doilea container este lista tuturor NetDevices pe ales Nod. În cele din urmă,
calea de configurare se termină de obicei cu o succesiune de atribute ale membrilor, în acest caz
„MaxPackets” atribut al „TxQueue” ale alesului NetDevice.
Am fi putut folosi, de asemenea, wildcards pentru a seta această valoare pentru toate nodurile și toate dispozitivele de rețea
(care în acest exemplu simplu are același efect ca și precedentul Config::Set ()):
Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
UintegerValue (15));
txQueue->GetAttribute ("MaxPackets", limită);
NS_LOG_INFO ("5. Limita txQueue a fost modificată prin spațiul de nume cu metacaracterizare: "
<< limit.Get () << " pachete ");
Obiect Nume Service
O altă modalitate de a ajunge la atribut este să utilizați facilitatea de serviciu de nume obiect. The
serviciul de nume obiect ne permite să adăugăm elemente la spațiul de nume de configurare sub
„/Nume/” cale cu un șir de nume definit de utilizator. Această abordare este utilă dacă nu
au acces la indicatorii de bază și este dificil să determinați necesarul
calea spațiului de nume de configurare concretă.
Nume::Add ("server", n0);
Nume::Add ("server/eth0", net0);
...
Config::Set ("/Names/server/eth0/TxQueue/MaxPackets", UintegerValue (25));
Aici am adăugat elementele de cale "Server" și "eth0" în temeiul „/Nume/” spatiu de nume, atunci
a folosit calea de configurare rezultată pentru a seta atributul.
Consultați Nume de obiecte pentru un tratament mai complet al ns-3 spațiu de nume de configurare.
Punerea în aplicare Detalii
Valoare Clase
Cititorii vor nota TypeValue clase care sunt subclase ale AttributeValue de bază
clasă. Acestea pot fi considerate ca clase intermediare care sunt folosite pentru a converti din brut
tipuri la AttributeValues care sunt utilizate de sistemul de atribute. Amintiți-vă că asta
baza de date conține obiecte de mai multe tipuri serializate în șiruri. Conversii la acest tip
poate fi realizat fie folosind o clasă intermediară (cum ar fi Valoare intreaga, DoubleValue pentru
numere în virgulă mobilă) sau de siruri de caractere. Conversia implicită directă a tipurilor în
AttributeValue nu este chiar practic. Deci, în cele de mai sus, utilizatorii au posibilitatea de a alege
șiruri de caractere sau valori:
p->Set ("cwnd", StringValue ("100")); // setter bazat pe șiruri
p->Set ("cwnd", IntegerValue (100)); // setter pe bază de întregi
Sistemul oferă câteva macrocomenzi care ajută utilizatorii să declare și să definească noua valoare AttributeValue
subclase pentru tipuri noi pe care doresc să le introducă în sistemul de atribute:
· ATTRIBUTE_HELPER_HEADER
· ATTRIBUTE_HELPER_CPP
Consultați documentația API pentru aceste construcții pentru mai multe informații.
Inițializarea Order
Atributele din sistem nu trebuie să depindă de starea oricărui alt Atribut din acesta
sistem. Acest lucru se datorează faptului că nu este specificată nici o ordonare a inițializării atributelor
aplicate, de către sistem. Un exemplu specific în acest sens poate fi văzut în configurația automată
programe precum ConfigStore. Deși un model dat îl poate aranja astfel încât Atribute
sunt inițializate într-o anumită ordine, un alt configurator automat poate decide
independent pentru a schimba Atributele, de exemplu, în ordine alfabetică.
Din cauza acestei ordonări nespecifice, niciun Atribut din sistem nu poate avea vreo dependență
pe orice alt Atribut. Ca un corolar, stabilitorii de atribute nu trebuie să eșueze niciodată din cauza stării
a altui Atribut. Niciun setter de atribute nu poate modifica (seta) orice altă valoare de atribut ca a
rezultat al modificării valorii sale.
Aceasta este o restricție foarte puternică și există cazuri în care Atributele trebuie setate
consecvent pentru a permite funcționarea corectă. În acest scop, permitem verificarea coerenței
cand il atribut is utilizat (cf. NS_ASSERT_MSG or NS_ABORT_MSG).
În general, codul de atribut pentru a atribui valori variabilelor membre ale clasei de bază
este executat după ce un obiect este construit. Dar dacă aveți nevoie de valorile atribuite
înainte ca corpul constructorului să se execute, deoarece aveți nevoie de ele în logica
constructor? Există o modalitate de a face acest lucru, folosită de exemplu în clasă ConfigStore: apel
ObjectBase::ConstructSelf () după cum urmează:
ConfigStore::ConfigStore ()
{
ObjectBase::ConstructSelf (AttributeConstructionList ());
// continuă cu constructorul.
}
Atenție că obiectul și toate clasele sale derivate trebuie să implementeze și a GetInstanceTypeId
() metodă. Altfel cel ObjectBase::ConstructSelf () nu va putea citi
atribute.
Adăugare Atribute
ns-3 sistem va plasa un număr de valori interne sub sistemul de atribute, dar
fără îndoială, utilizatorii vor dori să extindă acest lucru pentru a le ridica pe cele pe care le-am ratat sau pentru a le adăuga
propriile clase la sistem.
Există trei cazuri tipice de utilizare:
· Facerea unui membru existent de date de clasă accesibil ca Atribut, atunci când nu este deja.
· Crearea unei clase noi capabile să expună unii membri de date ca Atribute, dându-i un TypeId.
· Crearea unui AttributeValue subclasă pentru o nouă clasă, astfel încât să poată fi accesată ca un
Atribut.
Existent Membru Variabil
Luați în considerare această variabilă în TcpSocket:
uint32_t m_cWnd; // Fereastra de congestionare
Să presupunem că cineva care lucrează cu TCP a vrut să obțină sau să stabilească valoarea acelei variabile
folosind sistemul de metadate. Dacă nu ar fi fost deja furnizat de ns-3, ar putea declara utilizatorul
următoarea adăugare în sistemul de metadate de rulare (la GetTypeId() definiţie pentru
TcpSocket):
.AddAttribute („Fereastra de congestie”,
„Fereastra de congestie Tcp (octeți)”,
UintegerValue (1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MakeUintegerChecker ())
Acum, utilizatorul cu un pointer către a TcpSocket instanța poate efectua operațiuni precum
setarea și obținerea valorii, fără a fi nevoie să adăugați aceste funcții în mod explicit.
În plus, pot fi aplicate controale de acces, cum ar fi permiterea citirii parametrului și
nu este scris, sau se poate aplica verificarea limitelor la valorile admise.
Nou Clasă TypeId
Aici, discutăm impactul asupra unui utilizator care dorește să-i adauge o nouă clasă ns-3. Ce
trebuie făcute lucruri suplimentare pentru a-i permite să dețină atribute?
Să presupunem noua noastră clasă, numită ns3::MyMobility, este un tip de model de mobilitate. Primul,
clasa ar trebui să moștenească de la clasa părinte, ns3::Model de mobilitate. În mobilitatea mea.h
fișier antet:
spatiu de nume ns3 {
clasa MyClass : public MobilityModel
{
Acest lucru necesită să declarăm GetTypeId () funcţie. Aceasta este o funcție publică cu o singură linie
declaraţie:
public:
/ **
* Înregistrați acest tip.
* \return Obiectul TypeId.
*/
static TypeId GetTypeId (void);
Am introdus deja ceea ce a TypeId definiția va arăta ca în mobilitatea mea.cc
fisier de implementare:
NS_OBJECT_ENSURE_REGISTERED (MyMobility);
TypeId
MyMobility::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.SetParent ()
.SetGroupName ("Mobilitate")
.AddConstructor ()
.AddAttribute ("Margini",
„Delimitarea zonei de croazieră.”
RectangleValue (Dreptunghi (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
MakeRectangleChecker ())
.AddAttribute ("Timp",
„Schimbați direcția curentă și viteza după deplasare pentru această întârziere.”,
TimeValue (Secunde (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// etc (mai mulți parametri).
;
return tid;
}
Dacă nu dorim să subclasăm dintr-o clasă existentă, în fișierul antet doar moștenim
de la ns3::Obiect, iar în fișierul obiect am setat clasa părinte la ns3::Obiect implementate cu
.SetParent ().
Greșelile tipice aici implică:
· Nu sună NS_OBJECT_ENSURE_REGISTERED ()
· Nu se apelează la SetParent () metoda sau apelând-o cu tipul greșit.
· Nu se apelează la AddConstructor () metoda sau apelând-o cu tipul greșit.
· Introducerea unei erori tipografice în numele TypeId în constructorul său.
· Neutilizarea numelui de tip C++ complet calificat al clasei C++ care include ca nume
TypeId. Rețineți că "ns3::" este necesară.
Niciuna dintre aceste greșeli nu poate fi detectată de ns-3 baza de cod, astfel încât utilizatorii sunt sfătuiți să verifice
cu atenție de mai multe ori că le-au înțeles corect.
Nou AttributeValue Tip
Din perspectiva utilizatorului care scrie o nouă clasă în sistem și dorește să fie
accesibil ca atribut, există în principal problema scrierii conversiilor către/de la
șiruri de caractere și valori ale atributelor. Cele mai multe dintre acestea pot fi copiate/lipite cu cod macro-izat. Pentru
de exemplu, luați în considerare o declarație de clasă pentru Dreptunghi în src/mobility/model director:
Antet Fișier
/ **
* \brief un dreptunghi 2d
*/
clasa dreptunghi
{
...
xMin dublu;
xMax dublu;
dublu yMin;
dublu yMax;
};
Un apel macro și doi operatori, trebuie adăugate sub declarația de clasă pentru a
transforma un dreptunghi într-o valoare utilizabilă de către Atribut Sistemul de:
std::ostream &operator << (std::ostream &os, const Dreptunghi &dreptunghi);
std::istream &operator >> (std::istream &is, Dreptunghi &dreptunghi);
ATTRIBUTE_HELPER_HEADER (Dreptunghi);
Punerea în aplicare Fișier
În definiția clasei (. Cc fișier), codul arată astfel:
ATTRIBUTE_HELPER_CPP (Dreptunghi);
std::ostream &
operator << (std::ostream &os, const Dreptunghi &dreptunghi)
{
os << dreptunghi.xMin << "|" << rectangle.xMax << "|" << dreptunghi.yMin << "|"
<< dreptunghi.yMax;
return os;
}
std::istream &
operator >> (std::istream &is, dreptunghi & dreptunghi)
{
char c1, c2, c3;
este >> dreptunghi.xMin >> c1 >> dreptunghi.xMax >> c2 >> dreptunghi.yMin >> c3
>> dreptunghi.yMax;
dacă (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
întoarcerea este;
}
Acești operatori de flux pur și simplu convertesc dintr-o reprezentare șir a dreptunghiului
(„xMin|xMax|yMin|yMax”) la dreptunghiul de bază. Modelatorul trebuie să le specifice
operatori și reprezentarea sintactică șir a unei instanțe a noii clase.
ConfigStore
Valori pentru ns-3 atributele pot fi stocate într-un fișier text ASCII sau XML și încărcate într-un
viitoarea rulare de simulare. Această caracteristică este cunoscută sub numele de ns-3 ConfigStore. The ConfigStore is
o bază de date specializată pentru valorile atributelor și valorile implicite.
Deși este un modul întreținut separat în src/config-store/ director, noi
documentați-l aici din cauza dependenței sale de ns-3 modul de bază și atribute.
Putem explora acest sistem folosind un exemplu de la
src/config-store/examples/config-store-save.cc.
În primul rând, toți utilizatorii ConfigStore trebuie să includă următoarea declarație:
#include „ns3/config-store-module.h”
Apoi, acest program adaugă un obiect eșantion ConfigExample pentru a arăta cum este extins sistemul:
clasa ConfigExample : obiect public
{
public:
static TypeId GetTypeId (void) {
static TypeId tid = TypeId ("ns3::A")
.SetParent ()
.AddAttribute ("TestInt16", "text de ajutor",
IntegerValue (-2),
MakeIntegerAccessor (&A::m_int16),
MakeIntegerChecker ())
;
return tid;
}
int16_t m_int16;
};
NS_OBJECT_ENSURE_REGISTERED (ConfigExample);
Apoi, folosim subsistemul Config pentru a suprascrie valorile implicite în câteva moduri:
Config::SetDefault ("ns3::ConfigExample::TestInt16", IntegerValue (-5));
Ptr a_obj = CreateObject ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
„Nu se poate seta atributul întreg al lui ConfigExample prin Config::SetDefault”);
Ptr a2_obj = CreateObject ();
a2_obj->SetAttribute ("TestInt16", IntegerValue (-3));
IntegerValue iv;
a2_obj->GetAttribute ("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv.Obțineți () == -3,
„Nu se poate seta atributul întreg al lui ConfigExample prin SetAttribute”);
Următoarea declarație este necesară pentru a vă asigura că (unul dintre) obiectele create este înrădăcinat
în spațiul de nume de configurare ca o instanță de obiect. Acest lucru se întâmplă în mod normal când dvs
agregați obiecte la a ns3::Node or ns3::Canal exemplu, dar aici, din moment ce lucrăm
la nivelul de bază, trebuie să creăm un nou obiect de spațiu de nume rădăcină:
Config::RegisterRootNamespaceObject (a2_obj);
Scris
Apoi, dorim să scoatem magazinul de configurare. Exemplele arată cum să o faci în două
formate, XML și text brut. În practică, ar trebui să efectuați acest pas chiar înainte de a suna
Simulator::Run () pentru a salva configurația finală chiar înainte de rularea simulării.
Există trei atribute care guvernează comportamentul ConfigStore: "Modul",
"Nume de fișier" și "Tipul fisierului". Modul (implicit "Nici unul") configurează dacă ns-3 să
încărcați configurația dintr-un fișier salvat anterior (specificați „Mode=Încărcare”) sau salvați-l într-un fișier
(specifica „Mode=Salvare”). Numele fișierului (implicit "") este locul unde ar trebui să citească ConfigStore sau
scrie datele acestuia. Formatul fișierului (implicit „RawText”) guvernează dacă formatul ConfigStore
este text simplu sau XML („FileFormat=Xml”)
Exemplul arată:
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Salvare"));
ConfigStore outputConfig;
outputConfig.ConfigureDefaults ();
outputConfig.ConfigureAttributes ();
// Ieșiți magazinul de configurare în format txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Salvare"));
ConfigStore outputConfig2;
outputConfig2.ConfigureDefaults ();
outputConfig2.ConfigureAttributes ();
Simulator::Run ();
Simulator::Destroy ();
Rețineți plasarea acestor declarații chiar înainte de Simulator::Run () afirmație.
Această ieșire înregistrează toate valorile existente chiar înainte de începerea simulării (de exemplu.
după ce a avut loc toată configurația).
După rulare, puteți deschide output-attributes.txt fisier si vezi:
implicit ns3::RealtimeSimulatorImpl::SynchronizationMode „BestEffort”
implicit ns3::RealtimeSimulatorImpl::HardLimit „+100000000.0ns”
implicit ns3::PcapFileWrapper::CaptureSize „65535”
implicit ns3::PacketSocket::RcvBufSize „131072”
implicit ns3::ErrorModel::IsEnabled „adevărat”
implicit ns3::RateErrorModel::ErrorUnit „EU_BYTE”
implicit ns3::RateErrorModel::ErrorRate „0”
implicit ns3::RateErrorModel::RanVar „Uniform:0:1”
implicit ns3::DropTailQueue::Mod „Pachete”
implicit ns3::DropTailQueue::MaxPackets „100”
implicit ns3::DropTailQueue::MaxBytes „6553500”
implicit ns3::Application::StartTime „+0.0ns”
implicit ns3::Application::StopTime „+0.0ns”
implicit ns3::ConfigStore::Mod „Salvare”
implicit ns3::ConfigStore::Nume fișier „output-attributes.txt”
implicit ns3::ConfigStore::FileFormat „RawText”
implicit ns3::ConfigExample::TestInt16 „-5”
global RngSeed „1”
global RngRun „1”
global SimulatorImplementationType „ns3::DefaultSimulatorImpl”
global SchedulerType „ns3::MapScheduler”
Sumă de verificare globală Activată „fals”
valoare /$ns3::ConfigExample/TestInt16 „-3”
În cele de mai sus, sunt afișate toate valorile implicite pentru atributele pentru modulul de bază.
Apoi, toate valorile pentru ns-3 se înregistrează valorile globale. În cele din urmă, valoarea
instanță de ConfigExample care a fost înrădăcinat în spațiul de nume de configurare este afișat. Într-o
real ns-3 programului, vor fi afișate mai multe modele, atribute și valori implicite.
Există și o versiune XML în output-attributes.xml:
Acest fișier poate fi arhivat cu scriptul de simulare și datele de ieșire.
Citind
În continuare, vom discuta despre configurarea simulărilor de un fișier de configurare de intrare stocat. Sunt
câteva diferențe cheie în comparație cu scrierea configurației finale de simulare.
În primul rând, trebuie să plasăm declarații ca acestea la începutul programului, înainte
instrucțiunile de configurare de simulare sunt scrise (deci valorile sunt înregistrate înainte de a fi
folosit în construcţia de obiecte).
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Încărcare"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore inputConfig;
inputConfig.ConfigureDefaults ();
În continuare, rețineți că încărcarea datelor de configurare de intrare este limitată la atributul implicit (de exemplu.
nu exemplu) valori și valori globale. Valorile instanțelor de atribut nu sunt acceptate
pentru că în această etapă a simulării, înainte ca orice obiect să fie construit, nu există
asemenea obiecte în jur. (Rețineți că îmbunătățirile viitoare ale magazinului de configurare se pot schimba
acest comportament).
În al doilea rând, în timp ce producția de ConfigStore stat va lista totul din baza de date, the
fișierul de intrare trebuie să conțină doar valorile specifice care trebuie înlocuite. Deci, o modalitate de utilizare
această clasă pentru configurarea fișierului de intrare este de a genera o configurație inițială folosind
ieșire ("Salvați") "Modul" descris mai sus, extrageți din acel fișier de configurare doar fișierul
elementele pe care doriți să le schimbați și mutați aceste elemente minime într-un nou fișier de configurare
care poate fi apoi editat și încărcat în siguranță într-o rulare de simulare ulterioară.
Cand ConfigStore obiectul este instanțiat, atributele sale "Nume de fișier", "Modul" și
"Tipul fisierului" trebuie setat, fie de linia de comandă sau de instrucțiuni de program.
Citind, scriind Exemplu
Ca exemplu mai complicat, să presupunem că vrem să citim într-o configurație de
valorile implicite dintr-un fișier de intrare numit input-defaults.xml, și scrieți rezultatul
atribute unui fișier separat numit output-attributes.xml.:
#include „ns3/config-store-module.h”
...
int main (...)
{
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Încărcare"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigStore inputConfig;
inputConfig.ConfigureDefaults ();
//
// Permite utilizatorului să suprascrie oricare dintre valorile implicite și Bind () de mai sus la
// timpul de rulare, prin argumentele liniei de comandă
//
CommandLine cmd;
cmd.Parse (argc, argv);
// setează topologia
...
// Invocă chiar înainte de a intra în Simulator::Run ()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Salvare"));
ConfigStore outputConfig;
outputConfig.ConfigureAttributes ();
Simulator::Run ();
}
ConfigStore GUI
Există un front-end bazat pe GTK pentru ConfigStore. Acest lucru permite utilizatorilor să folosească o interfață grafică pentru
accesați și modificați variabile. Capturile de ecran ale acestei funcții sunt disponibile în |ns3|
Descriere prezentare.
Pentru a utiliza această caracteristică, trebuie să instalați libgtk și libgtk-dev; un exemplu Ubuntu
comanda de instalare este:
$ sudo apt-get install libgtk2.0-0 libgtk2.0-dev
Pentru a verifica dacă este configurat sau nu, verificați rezultatul pasului:
$ ./waf configure --enable-examples --enable-tests
---- Rezumatul caracteristicilor opționale NS-3:
Legături Python: activat
Suport pentru scanare Python API: activat
NS-3 Faceți clic pe Integrare : activat
GtkConfigStore: nu este activat (biblioteca „gtk+-2.0 >= 2.12” nu a fost găsită)
În exemplul de mai sus, nu a fost activat, deci nu poate fi utilizat până când nu este o versiune adecvată
instalat și:
$ ./waf configure --enable-examples --enable-tests
$ ./waf
este reluat.
Utilizarea este aproape aceeași cu versiunea care nu este bazată pe GTK, dar nu există ConfigStore
atribute implicate:
// Invocă chiar înainte de a intra în Simulator::Run ()
Configurare GtkConfigStore;
config.ConfigureDefaults ();
config.ConfigureAttributes ();
Acum, când rulați scriptul, ar trebui să apară o interfață grafică, permițându-vă să deschideți meniuri de
atribute pe diferite noduri/obiecte și apoi lansați execuția simulării când dvs
sunt gata.
Viitor muncă
Există câteva posibile îmbunătățiri:
· Salvați un număr unic de versiune cu data și ora la începutul fișierului.
· Salvați sămânța inițială rng undeva.
· Faceți ca fiecare RandomVariable să-și serializeze propria sămânță inițială și să o recitiți mai târziu.
Obiect nume
Înlocuitor capitol
Exploatari forestiere
ns-3 Facilitatea de înregistrare poate fi utilizată pentru a monitoriza sau a depana progresul simulării
programe. Ieșirea în jurnal poate fi activată de instrucțiunile programului din dvs main () program sau
prin utilizarea NS_LOG variabilă de mediu.
Declarațiile de înregistrare nu sunt compilate în versiuni optimizate ale ns-3. Pentru a utiliza înregistrarea, unul
trebuie să construiască versiunea (implicit) de depanare a ns-3.
Proiectul nu oferă nicio garanție cu privire la faptul că rezultatul înregistrat va rămâne neschimbat
timp. Utilizatorii sunt atenționați să nu construiască cadre de ieșire de simulare pe lângă înregistrare
cod, deoarece ieșirea și modul în care este activată se pot schimba în timp.
Descriere
ns-3 Instrucțiunile de înregistrare sunt de obicei folosite pentru a înregistra diverse evenimente de execuție a programului, cum ar fi
ca apariția unor evenimente de simulare sau utilizarea unei anumite funcții.
De exemplu, acest fragment de cod este de la Ipv4L3Protocol::IsDestinationAddress():
if (adresa == iaddr.GetBroadcast ())
{
NS_LOG_LOGIC ("Pentru mine (adresa de difuzare a interfeței)");
return true;
}
Dacă înregistrarea în jurnal a fost activată pentru Ipv4L3Protocol componentă la o severitate de LOGICĂ or
mai sus (vezi mai jos despre severitatea jurnalului), declarația va fi tipărită; altfel, ea
va fi suprimat.
Activarea producție
Există două moduri prin care utilizatorii controlează de obicei ieşirea jurnalului. Primul este prin setarea
NS_LOG variabilă de mediu; de exemplu:
$ NS_LOG="*" ./waf --rulați primul
va rula primul program tutorial cu toate rezultatele de înregistrare. (Specificiul NS_LOG
formatul va fi discutat mai jos.)
Acest lucru poate fi detaliat prin selectarea componentelor individuale:
$ NS_LOG="Ipv4L3Protocol" ./waf --rulați primul
Ieșirea poate fi adaptată în continuare cu opțiuni de prefix.
A doua modalitate de a activa înregistrarea este să utilizați instrucțiuni explicite în programul dvs., cum ar fi in
il primul program tutorial:
int
main (int argc, char *argv[])
{
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
...
(Înțelesul lui LOG_LEVEL_INFO, și alte valori posibile, vor fi discutate mai jos.)
NS_LOG Sintaxă
NS_LOG variabila de mediu conține o listă de componente și opțiuni de jurnal. Buturuga
componentele sunt separate prin caractere `:':
$ NS_LOG=" : ..."
Opțiunile pentru fiecare componentă de jurnal sunt date sub formă de steaguri după fiecare componentă de jurnal:
$ NS_LOG=" = | ...: ..."
Opțiunile controlează severitatea și nivelul pentru acea componentă și dacă sunt opționale
ar trebui incluse informații, cum ar fi timpul de simulare, nodul de simulare, funcția
numele și severitatea simbolică.
Log Componente
În general, o componentă de jurnal se referă la un singur cod sursă . Cc fișier și cuprinde fișierul
întregul dosar.
Unii ajutoare au metode speciale pentru a permite înregistrarea tuturor componentelor dintr-un modul,
care cuprinde diferite unități de compilare, dar grupate logic împreună, cum ar fi ns-3
cod wifi:
WifiHelper wifiHelper;
wifiHelper.EnableLogComponents ();
NS_LOG componenta jurnal wildcard `*' va activa toate componentele.
Pentru a vedea ce componente de jurnal sunt definite, oricare dintre acestea va funcționa:
$ NS_LOG="print-list" ./waf --run ...
$ NS_LOG="foo" # un token care nu se potrivește cu nicio componentă de jurnal
Primul formular va tipări numele și steagurile activate pentru toate componentele de jurnal care sunt
legat în; incearca cu zgârietură-simulator. Al doilea formular tipărește toate jurnalele înregistrate
componente, apoi ieșiți cu o eroare.
Severitate și Nivel Opţiuni
Mesajele individuale aparțin unei singure „clase de severitate”, stabilită de macrocomanda care creează
mesaj. În exemplul de mai sus, NS_LOG_LOGIC(..) creează mesajul în LOG_LOGIC
clasa de severitate.
Următoarele clase de severitate sunt definite ca enumerare constante:
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┐
│Clasa de severitate │ Semnificație │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_NONE │ Implicit, fără înregistrare │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_ERROR │ Numai mesaje de eroare grave │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_WARN │ Mesaje de avertizare │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_DEBUG │ Pentru utilizare în depanare │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_INFO │ Informațional │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_FUNCTION │ Urmărirea funcției │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┤
│LOG_LOGIC │ Controlați urmărirea fluxului în │
│ │ funcții │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─┘
De obicei, cineva dorește să vadă mesajele la o anumită clasă de severitate și superior. Acest lucru este realizat de
definirea „nivelurilor” de jurnalizare inclusivă:
┌───────────────────┬───────────────────────────── ─────┐
│Nivel │ Semnificație │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_ERROR │ Numai LOG_ERROR clasa de severitate │
│ │ mesaje. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_WARN │ LOG_WARN Si mai sus. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_DEBUG │ LOG_DEBUG Si mai sus. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_INFO │ LOG_INFO Si mai sus. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_FUNCTION │ LOG_FUNCTION Si mai sus. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_LOGIC │ LOG_LOGIC Si mai sus. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_LEVEL_ALL │ Toate clasele de severitate. │
├───────────────────┼───────────────────────────── ─────┤
│LOG_ALL │ Sinonim pentru LOG_LEVEL_ALL │
└───────────────────┴───────────────────────────── ─────┘
Clasa de severitate și opțiunile de nivel pot fi date în NS_LOG variabila de mediu prin
aceste jetoane:
┌─────────┬────────────────┐
│Clasă │ Nivel │
├─────────┼────────────────┤
│eroare │ level_error │
├─────────┼────────────────┤
│avertiza │ level_warn │
├─────────┼────────────────┤
│depana │ level_debug │
├─────────┼────────────────┤
│info │ level_info │
├─────────┼────────────────┤
│funcţie │ level_function │
├─────────┼────────────────┤
│logică │ level_logic │
├─────────┼────────────────┤
│ │ level_all │
│ │ toate │
│ │ * │
└─────────┴────────────────┘
Utilizarea unui simbol de clasă de severitate activează mesajele de jurnal numai la acea severitate. De exemplu,
NS_LOG="*=avertisment" nu va scoate mesaje cu severitate eroare. NS_LOG="*=level_debug" voi
trimite mesaje la niveluri de severitate depana Si mai sus.
Clasele și nivelurile de severitate pot fi combinate cu `|' operator:
NS_LOG="*=level_warn|logica" va afișa mesaje la niveluri de severitate eroare, avertiza și logică.
NS_LOG wildcard la nivel de severitate `*' și toate sunt sinonime pentru level_all.
Pentru componentele jurnalului menționate doar în NS_LOG
$ NS_LOG=" :..."
severitatea implicită este LOG_LEVEL_ALL.
Prefix Opţiuni
O serie de prefixe pot ajuta la identificarea unde și când a provenit un mesaj și la ce
severitate.
Opțiunile de prefix disponibile (cum ar fi enumerare constante) sunt
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┐
│Simbol prefix │ Sens │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│LOG_PREFIX_FUNC │ Prefix numele apelantului │
│ │ funcţia. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│LOG_PREFIX_TIME │ Prefixați ora de simulare. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│LOG_PREFIX_NODE │ Prefix id-ul nodului. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│LOG_PREFIX_LEVEL │ Prefixați nivelul de severitate. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│LOG_PREFIX_ALL │ Activați toate prefixele. │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┘
Opțiunile de prefix sunt descrise pe scurt mai jos.
Opțiunile pot fi date în NS_LOG variabilă de mediu prin aceste indicative:
┌─────────────┬───────────┐
│Jeton │ Alternativ │
├─────────────┼───────────┤
│prefix_func │ FUNC │
├─────────────┼───────────┤
│prefix_time │ timp │
└─────────────┴───────────┘
│nod_prefix │ nod │
├─────────────┼───────────┤
│nivel_prefix │ nivel │
├─────────────┼───────────┤
│prefix_all │ toate │
│ │ * │
└─────────────┴───────────┘
Pentru componentele jurnalului menționate doar în NS_LOG
$ NS_LOG=" :..."
opțiunile implicite de prefix sunt LOG_PREFIX_ALL.
Severitate Prefix
Clasa de severitate a unui mesaj poate fi inclusă în opțiuni nivel_prefix or nivel.
De exemplu, această valoare a NS_LOG permite înregistrarea pentru toate componentele jurnalului (`*') și toate
clase de severitate (=toate), și prefixează mesajul cu clasa de severitate (|nivel_prefix).
$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
Simulator de zgârieturi
[EROARE] mesaj de eroare
[WARN] mesaj de avertizare
[DEBUG] mesaj de depanare
[INFO] mesaj informativ
mesajul funcției [FUNCT].
[LOGIC] mesaj logic
Timp Prefix
Timpul de simulare poate fi inclus cu opțiunile prefix_time or timp. Aceasta imprimă
timpul de simulare în secunde.
Nod Prefix
ID-ul nodului de simulare poate fi inclus cu opțiunile nod_prefix or nod.
Funcţie Prefix
Numele funcției de apelare poate fi inclus cu opțiunile prefix_func or FUNC.
NS_LOG metacaractere
Caracterul joker „*” al componentei jurnal va activa toate componentele. Pentru a activa toate componentele la a
utilizarea nivelului de severitate specific *=.
Opțiunea de nivel de severitate wildcard „*” este un sinonim pentru toate. Acest lucru trebuie să se întâmple înainte de orice
`|' opțiuni de separare a caracterelor. Pentru a activa toate clasele de severitate, utilizați =*,
or =*|.
Opțiunea wildcard `*' sau token toate activează toate opțiunile de prefix, dar trebuie să apară după a
`|' caracter. Pentru a activa o anumită clasă sau nivel de severitate și toate prefixele, utilizați
= |*.
Caracterul joker al opțiunii combinate ** activează toate severitățile și toate prefixele; de exemplu,
=**.
Uber-wildcard *** activează toate severitățile și toate prefixele pentru toate componentele jurnalului.
Toate acestea sunt echivalente:
$ NS_LOG="***" ... $ NS_LOG="*=toate|*" ... $ NS_LOG="*=*|toate" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$ NS_LOG="*=*|*"...
Fiți atenți: chiar și banalul zgârietură-simulator produce peste 46K linii de ieșire cu
NS_LOG="***"!
Cum la adăuga logare la ta cod
Adăugarea de logare la codul dvs. este foarte simplă:
1. Invocați NS_LOG_COMPONENT_DEFINE (...); macro în interiorul Spațiu de nume ns3.
Creați un identificator de șir unic (de obicei bazat pe numele fișierului și/sau al clasei
definit în fișier) și înregistrați-l cu un apel macro, după cum urmează:
spatiu de nume ns3 {
NS_LOG_COMPONENT_DEFINE ("Protocol Ipv4L3");
...
Aceasta se înregistrează Ipv4L3Protocol ca componentă a jurnalului.
(Macrocomandă a fost scrisă cu atenție pentru a permite includerea fie în interiorul, fie în afara acestuia
Spațiu de nume ns3, iar utilizarea va varia de la baza de cod, dar intenția inițială a fost aceea
inregistreaza asta exterior a spațiului de nume ns3 la domeniul global al fișierului.)
2. Adăugați instrucțiuni de înregistrare (apeluri macro) la funcțiile și corpurile dvs. de funcții.
Exploatari forestiere Macrocomenzi
Macrocomenzile de înregistrare și nivelurile de severitate asociate sunt
┌───────────────┬──────────────────────────
│Clasa de severitate │ Macro │
├───────────────┼──────────────────────────────
│LOG_NONE │ (nu este necesar) │
├───────────────┼──────────────────────────────
│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 (...); │
└───────────────┴────────────────────────────
Macrocomenzile funcționează ca fluxuri de ieșire, deci orice la care puteți trimite std::out, s-a alăturat
by << operatori, este permis:
void MyClass::Check (valoare int, caracter * element)
{
NS_LOG_FUNCTION (acest articol << arg <<);
dacă (arg > 10)
{
NS_LOG_ERROR ("s-a întâlnit o valoare greșită " << valoare <
" în timp ce bifați " << nume << "!");
}
...
}
Rețineți că NS_LOG_FUNCTION inserează automat un `,' (virgulă-spațiu) separator între
fiecare dintre argumentele sale. Aceasta simplifică înregistrarea argumentelor funcției; doar concatenează
ei cu << ca în exemplul de mai sus.
Necondiţionat Exploatari forestiere
Ca o comoditate, NS_LOG_UNCOND (...); macro va înregistra întotdeauna argumentele sale, chiar dacă
componenta jurnal asociată nu este activată la nicio severitate. Această macrocomandă nu folosește niciuna
a opțiunilor de prefix. Rețineți că înregistrarea este activată numai în versiunile de depanare; această macro
nu va produce rezultate în versiuni optimizate.
Instrucțiuni
· Începeți fiecare metodă de clasă cu NS_LOG_FUNCTION (acest << argumente...); Acest lucru permite ușor
urmărirea apelurilor de funcție.
· Cu excepția: nu înregistrați operatorii sau constructorii de copiere explicită, deoarece acestea vor cauza
recursivitate infinită și depășire a stivei.
· Pentru metodele fără argumente utilizați aceeași formă: NS_LOG_FUNCTION (acest);
· Pentru funcții statice:
· Folosirea cu argumente NS_LOG_FUNCTION (...); la fel de normal.
· Folosire fără argumente NS_LOG_FUNCTION_NOARGS ,
· Utilizare NS_LOG_ERROR pentru condiții de eroare grave care probabil invalidează simularea
execuţie.
· Utilizare NS_LOG_WARN pentru condiții neobișnuite care pot fi corectabile. Vă rog să dați câteva indicii
în ceea ce privește natura problemei și cum ar putea fi corectată.
· NS_LOG_DEBUG este de obicei folosit într-o ad hoc mod de a înțelege execuția unui model.
· Utilizare NS_LOG_INFO pentru informații suplimentare despre execuție, cum ar fi dimensiunea unui
structura datelor la adăugarea/eliminarea din aceasta.
· Utilizare NS_LOG_LOGIC pentru a urmări ramuri logice importante în cadrul unei funcții.
· Testați că modificările dvs. de înregistrare nu încalcă codul. Rulați câteva exemple de programe cu
toate componentele jurnalului sunt activate (de ex NS_LOG="***").
calc
Subsistemul de urmărire este unul dintre cele mai importante mecanisme de înțeles ns-3. În
cele mai multe cazuri, ns-3 utilizatorii vor avea o idee genială pentru unele rețele noi și îmbunătățite
caracteristică. Pentru a verifica dacă această idee funcționează, cercetătorul va face modificări unui
sistemul existent și apoi executați experimente pentru a vedea cum se comportă noua caracteristică prin colectare
statistici care surprind comportamentul caracteristicii.
Cu alte cuvinte, scopul rulării unei simulări este de a genera rezultate pentru mai departe
studiu. În ns-3, subsistemul care permite unui cercetător să facă acest lucru este urmărirea
subsistem.
calc motivaţia
Există multe modalități de a obține informații dintr-un program. Cea mai simplă cale este
pentru a imprima direct informațiile la ieșirea standard, ca în,
#include
...
intmain()
{
...
std::cout << "Valoarea lui x este " << x << std::endl;
...
}
Acest lucru este realizabil în medii mici, dar pe măsură ce simulările dvs. devin din ce în ce mai multe
complicat, ajungi cu tot mai multe printuri și sarcina de a analiza și a performa
calculele la ieșire încep să devină din ce în ce mai dificile.
Un alt lucru de luat în considerare este că de fiecare dată când este nevoie de o nouă informație, nucleul software
trebuie editat și introdus un alt tipar. Nu există o modalitate standardizată de a controla totul
din această ieșire, astfel încât cantitatea de ieșire tinde să crească fără limite. În cele din urmă, cel
lățimea de bandă necesară pur și simplu pentru ieșirea acestor informații începe să limiteze timpul de rulare
a simulării. Fișierele de ieșire cresc la dimensiuni enorme și analizarea lor devine a
problemă.
ns-3 oferă un mecanism simplu pentru înregistrare și oferirea unui anumit control asupra ieșirii prin
Log Componente, dar nivelul de control nu este deloc foarte fin. Taierea
modulul este un instrument relativ contondent.
Este de dorit să existe o facilitate care să permită accesul în sistemul de bază și numai
obțineți informațiile necesare fără a fi nevoie să schimbați și să recompilați sistemul de bază. Chiar
mai bine ar fi un sistem care să notifice utilizatorul atunci când un articol de interes s-a schimbat sau un
s-a întâmplat un eveniment interesant.
ns-3 sistemul de urmărire este proiectat să funcționeze de-a lungul acestor linii și este bine integrat cu
substemele Atribut și Config permițând scenarii de utilizare relativ simple.
Descriere
Subsistemul de urmărire se bazează în mare măsură pe ns-3 Mecanisme de apel invers și de atribute. Tu
ar trebui să citească și să înțeleagă secțiunile corespunzătoare ale manualului înainte de a încerca
înțelege sistemul de urmărire.
ns-3 sistemul de urmărire este construit pe conceptele de surse independente de urmărire și
trasarea chiuvetelor; împreună cu un mecanism uniform pentru conectarea surselor la chiuvete.
Sursele de urmărire sunt entități care pot semnala evenimente care au loc într-o simulare și pot furniza
acces la date interesante de bază. De exemplu, o sursă de urmărire ar putea indica când a
pachetul este primit de un dispozitiv de rețea și oferă acces la conținutul pachetului pentru
cufundă urme interesate. O sursă de urmărire ar putea indica, de asemenea, când o stare interesantă
schimbarea are loc într-un model. De exemplu, fereastra de congestie a unui model TCP este un prim
candidat pentru o sursă de urmărire.
Sursele de urmărire nu sunt utile în sine; acestea trebuie să fie conectate la alte bucăți de cod
care fac de fapt ceva util cu informațiile furnizate de sursă. The
entitățile care consumă informații despre urme sunt numite canale de urmărire. Sursele de urme sunt
generatorii de evenimente și canalele de urmărire sunt consumatori.
Această împărțire explicită permite împrăștierea unui număr mare de surse de urme
sistemul în locuri pe care autorii modelelor cred că ar putea fi util. Cu excepția cazului în care un utilizator se conectează la un
Urmărirea la una dintre aceste surse, nu este ieșit nimic. Acest aranjament permite relativ
utilizatorii nesofisticați să atașeze noi tipuri de chiuvete la sursele de urmărire existente, fără
necesitând editarea și recompilarea nucleului sau modelelor simulatorului.
Pot exista zero sau mai mulți consumatori de evenimente de urmărire generate de o sursă de urmărire. Se poate
Gândiți-vă la o sursă de urmărire ca la un fel de legătură de informații punct-la-multipunct.
„Protocolul de transport” pentru această legătură conceptuală punct-la-multipunct este un ns-3 Callback.
Reamintim din secțiunea de apel invers că facilitatea de apel invers este o modalitate de a permite intrarea a două module
sistemul să comunice prin apeluri de funcții în timp ce, în același timp, decuplează apelul
funcția completă din clasa apelată. Aceasta este aceeași cerință ca cea prezentată mai sus
pentru sistemul de urmărire.
Practic, o sursă de urme is un apel invers la care pot fi înregistrate mai multe funcții.
Când un receptor de urmărire își exprimă interesul pentru a primi evenimente de urmărire, adaugă un apel invers la a
lista de apeluri inverse deținute de sursa de urmărire. Când se întâmplă un eveniment interesant, urmă
sursa îl invocă operator() oferind zero sau mai mulți parametri. Aceasta îi spune sursei să
parcurgeți lista de apeluri inverse, invocând pe fiecare pe rând. În acest fel, parametrul(i)
sunt comunicate chiuvetelor de urme, care sunt doar funcții.
Cel mai simplu Exemplu
Va fi util să mergem cu un exemplu rapid doar pentru a întări ceea ce am spus.:
#include „ns3/object.h”
#include „ns3/uinteger.h”
#include „ns3/traced-value.h””
#include „ns3/trace-source-accessor.h”
#include
folosind namespace ns3;
Primul lucru de făcut este să includeți fișierele necesare. După cum am menționat mai sus, sistemul de urmărire
folosește intens sistemele de obiecte și atribute. Primele două includ aducerea în
declarații pentru acele sisteme. Fișierul, valoare urmărită.h aduce necesarul
declarații pentru urmărirea datelor care respectă semantica valorii.
În general, semantica valorii înseamnă doar că puteți trece obiectul în jur, nu un
abordare. Pentru a utiliza semantica valorii, trebuie să aveți un obiect cu un
disponibil constructor de copiere asociat și operator de atribuire. Extindem cerințele
pentru a vorbi despre setul de operatori care sunt predefiniti pentru tipurile de date simple-vechi (POD).
Operator=, operator++, operator--, operator+, operator== etc.
Ceea ce înseamnă toate acestea este că veți putea urmări modificările unui obiect făcute folosind
acei operatori.:
clasa MyObject : obiect public
{
public:
static TypeId GetTypeId (void)
{
static TypeId tid = TypeId ("MyObject")
.SetParent (Object::GetTypeId ())
.AddConstructor ()
.AddTraceSource ("MyInteger",
„O valoare întreagă de urmărit.”
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
return tid;
}
MyObject () {}
TracedValue m_myInt;
};
Deoarece sistemul de urmărire este integrat cu Atribute, iar Atributele funcționează cu Obiecte,
trebuie să existe o ns-3 Obiect pentru ca sursa urmei să locuiască. Cele două linii importante ale
codul sunt .AddTraceSource si TracedValue declaraţie.
.AddTraceSource furnizează „cârligele” utilizate pentru conectarea sursei de urmărire la
lumea de afara. The TracedValue declarația oferă infrastructura care supraîncărcă
operatorii menționați mai sus și conduce procesul de apel invers.:
anula
IntTrace (Int oldValue, Int newValue)
{
std::cout << "Traced" << oldValue << " la " << newValue << std::endl;
}
Aceasta este definiția chiuvetei de urme. Corespunde direct unei funcții de apel invers.
Această funcție va fi apelată ori de câte ori unul dintre operatorii TracedValue is
executat.:
int
main (int argc, char *argv[])
{
Ptr myObject = CreateObject ();
myObject->TraceConnectWithoutContext ("MyInteger", MakeCallback(&IntTrace));
myObject->m_myInt = 1234;
}
În acest fragment, primul lucru care trebuie făcut este să creați obiectul în care
sursa urmei trăiește.
Următorul pas, cel TraceConnectWithoutContext, formează legătura între urmă
sursă și chiuvetă de urme. Observați MakeCallback funcția șablon. Amintiți-vă din
Secțiunea de apel invers care creează functorul specializat responsabil pentru furnizarea
supraîncărcat operator() folosit pentru a „trage” apelul invers. Operatorii supraîncărcați (++, --, etc.)
va folosi asta operator() pentru a invoca efectiv apelul invers. The TraceConnectWithoutContext,
ia un parametru șir care furnizează numele Atributului atribuit urmei
sursă. Să ignorăm puțin despre context pentru moment, deoarece nu este încă important.
În sfârșit, linia:
myObject->m_myInt = 1234;
ar trebui interpretat ca o invocare a operator= pe variabila membru m_myInt implementate cu
întregul 1234 a trecut ca parametru. Se pare că acest operator este definit (de
TracedValue) pentru a executa un apel invers care returnează void și ia două valori întregi ca
parametri -- o valoare veche și o valoare nouă pentru întregul în cauză. Exact asta
semnătura funcției pentru funcția de apel invers oferită -- IntTrace.
Pentru a rezuma, o sursă de urmărire este, în esență, o variabilă care conține o listă de apeluri inverse. A
trace sink este o funcție folosită ca țintă a unui apel invers. Atributul și tipul de obiect
sistemele informaționale sunt folosite pentru a oferi o modalitate de a conecta sursele de urmărire la chiuvete de urmărire. The
actul de a „lovi” o sursă de urmărire este executarea unui operator pe sursa de urmărire care se declanșează
apeluri inverse. Acest lucru are ca rezultat înregistrarea de apeluri inverse de urmărire care înregistrează interes pentru sursă
fiind apelat cu parametrii furnizaţi de sursă.
Utilizarea il config Subsistem la Connect la Urmă Surse
TraceConnectWithoutContext apelul prezentat mai sus în exemplul simplu este de fapt foarte
rar folosit în sistem. Mai tipic, cel config subsistemul este utilizat pentru a permite selectarea
o sursă de urmărire în sistem folosind ceea ce se numește a config cale.
De exemplu, s-ar putea găsi ceva care arată ca următorul în sistem (luat
de la exemple/tcp-large-transfer.cc):
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
Config::ConnectWithoutContext (
„/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow”,
MakeCallback (&CwndTracer));
Acest lucru ar trebui să pară foarte familiar. Este același lucru ca exemplul precedent, cu excepția faptului că
o funcție membru statică a clasei config este apelată în loc de o metodă activată Obiect;
iar în loc de o Atribut nume, este furnizată o cale.
Primul lucru de făcut este să citiți calea înapoi. Ultimul segment al drumului trebuie să fie
an Atribut unui Obiect. De fapt, dacă ai avea un indicator către Obiect care are
„Fereastră de congestionare” Atribut la îndemână (numiți obiectul), ai putea scrie asta la fel ca
exemplu anterior:
void CwndTracer (uint32_t oldval, uint32_t newval) {}
...
theObject->TraceConnectWithoutContext („CongestionWindow”, MakeCallback (&CwndTracer));
Se pare că codul pentru Config::ConnectWithoutContext face exact asta. Acest
funcția ia o cale care reprezintă un lanț de Obiect indică și le urmează până când acesta
ajunge la capătul căii și interpretează ultimul segment ca un Atribut pe ultima
obiect. Să trecem prin ceea ce se întâmplă.
Caracterul principal „/” din cale se referă la un așa-numit spațiu de nume. Unul dintre
spațiile de nume predefinite în sistemul de configurare este „NodeList”, care este o listă a tuturor
noduri în simulare. Elementele din listă sunt menționate prin indici în listă, deci
„/NodeList/0” se referă la nodul zero din lista de noduri create de simulare.
Acest nod este de fapt un Ptr la fel și o subclasă a lui an ns3::Obiect.
După cum este descris în secțiunea Model-obiect, ns-3 acceptă un model de agregare a obiectelor. The
următorul segment de cale începe cu caracterul „$” care indică a GetObject apelul ar trebui să fie
făcut să caute tipul care urmează. Când un nod este inițializat de un
InternetStackHelper un număr de interfețe sunt agregate la nod. Una dintre acestea este
Protocolul TCP de nivel patru. Tipul de rulare al acestui obiect de protocol este ns3::TcpL4Protocol''.
Cand il ``GetObject este executat, returnează un pointer către obiectul de acest tip.
TcpL4Protocol clasa definește un Atribut numit "SocketList" care este o listă de
prize. Fiecare priză este de fapt un ns3::Obiect cu ale sale Atribute. Elementele din
lista de socketuri este menționată prin index la fel ca în NodeList, deci „SocketList/0”
se referă la socket-ul zero din lista de prize de pe nodul zero din NodeList --
primul nod construit în simulare.
Această priză, al cărei tip se dovedește a fi un ns3::TcpSocketImpl definește un atribut
numită „CongestionWindow” care este a TracedValue.
Config::ConnectWithoutContext acum face un,:
obiect->TraceConnectWithoutContext ("CongestionWindow", MakeCallback (&CwndTracer));
folosind indicatorul obiect din „SocketList/0” care face conexiunea între urmă
sursa definită în soclu pentru apel invers -- CwndTracer.
Acum, ori de câte ori se face o modificare la TracedValue reprezentand aglomeratia
fereastră în socket-ul TCP, apelul invers înregistrat va fi executat și funcția
CwndTracer va fi numit tipărirea valorilor vechi și noi ale congestiei TCP
fereastră.
Utilizarea il calc API
Există trei niveluri de interacțiune cu sistemul de urmărire:
· Utilizatorul începător poate controla cu ușurință ce obiecte participă la urmărire;
· Utilizatorii intermediari pot extinde sistemul de urmărire pentru a modifica formatul de ieșire generat
sau utilizați sursele de urme existente în moduri diferite, fără a modifica nucleul
simulator;
· Utilizatorii avansați pot modifica nucleul simulatorului pentru a adăuga noi surse de urmărire și chiuvete.
Utilizarea Urmă Ajutoare
ns-3 Ajutoarele de urmărire oferă un mediu bogat pentru configurarea și selectarea diferitelor
urmăriți evenimentele și scrieți-le în fișiere. În secțiunile anterioare, în primul rând „Clădire
Topologii", am văzut mai multe varietăți de metode de ajutor de urmărire concepute pentru utilizare
în interiorul altor ajutoare (dispozitive).
Poate vă amintiți că ați văzut unele dintre aceste variații:
pointToPoint.EnablePcapAll ("al doilea");
pointToPoint.EnablePcap ("al doilea", p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap ("al treilea", csmaDevices.Get (0), true);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream ("myfirst.tr"));
Ceea ce poate să nu fie evident, însă, este că există un model consistent pentru toate
metode legate de urme găsite în sistem. Acum vom lua puțin timp și vom arunca o privire
la „imaginea de ansamblu”.
În prezent, există două cazuri de utilizare principale ale ajutoarelor de urmărire în ns-3: Ajutoare pentru dispozitiv
și ajutoare de protocol. Ajutoarele de dispozitiv analizează problema de a specifica ce urme ar trebui
să fie activat printr-un nod, pereche de dispozitive. De exemplu, poate doriți să specificați acel pcap
urmărirea ar trebui să fie activată pe un anumit dispozitiv pe un anumit nod. Aceasta rezultă din
ns-3 modelul conceptual al dispozitivului, precum și modelele conceptuale ale diferitelor dispozitive
ajutoare. Urmând în mod firesc de aici, fișierele create urmează a
- - convenție de numire.
Ajutoarele de protocol analizează problema specificării prin care urme ar trebui activate
o pereche de protocol și interfață. Aceasta rezultă din ns-3 modelul conceptual al stivei de protocol,
precum și modelele conceptuale ale ajutoarelor de stivă de internet. Desigur, fișierele de urmărire
ar trebui să urmeze a - - convenție de numire.
Prin urmare, ajutoarele de urmărire se încadrează în mod natural într-o taxonomie bidimensională. Sunt
subtilități care împiedică toate cele patru clase să se comporte identic, dar ne străduim să o facem
fă-le pe toate să funcționeze cât mai similar posibil; iar ori de câte ori este posibil există analogi pentru
toate metodele din toate clasele.
┌────────────────┬──────┬───────
│ │ pcap │ ascii │
├────────────────┼──────┼───────
│Device Helper │ │ │
├────────────────┼──────┼───────
│Protocol Helper │ │ │
└────────────────┴──────┴──────────
Folosim o abordare numită a mixin pentru a adăuga funcționalitate de urmărire la clasele noastre de ajutor. A
mixin este o clasă care oferă funcționalitate moștenită de o subclasă.
Moștenirea de la un mixin nu este considerată o formă de specializare, ci este într-adevăr o modalitate de a face acest lucru
colectează funcționalitatea.
Să aruncăm o privire rapidă asupra tuturor acestor patru cazuri și a acestora mixine.
Pcap calc Dispozitiv Ajutoare
Scopul acestor ajutoare este de a facilita adăugarea unei facilitati de urmărire pcap consistente la un
ns-3 dispozitiv. Dorim ca toate diversele arome ale urmăririi pcap să funcționeze la fel
toate dispozitivele, astfel încât metodele acestor ajutoare sunt moștenite de ajutoarele de dispozitiv. Aruncă o privire
at src/network/helper/trace-helper.h dacă vrei să urmărești discuția în timp ce te uiți la
cod real.
Clasa PcapHelperForDevice este mixin oferă funcționalitate de nivel înalt pentru utilizare
pcap urmărire într-un ns-3 dispozitiv. Fiecare dispozitiv trebuie să implementeze o singură metodă virtuală
moștenit din această clasă.:
virtual void EnablePcapInternal (prefix std::string, Ptr nd, bool promiscuu) = 0;
Semnătura acestei metode reflectă viziunea centrată pe dispozitiv a situației la aceasta
nivel. Toate metodele publice moștenite de la clasă PcapUserHelperForDevice A se reduce la
apelând această metodă de implementare unică dependentă de dispozitiv. De exemplu, cel mai jos nivel
metoda pcap,:
void EnablePcap (prefix std::string, Ptr nd, bool promiscuous = fals, bool explicitFilename = fals);
va apela implementarea dispozitivului de EnablePcapInternal direct. Toate celelalte pcap publice
metodele de urmărire se bazează pe această implementare pentru a oferi un nivel suplimentar de utilizator
funcţionalitate. Ceea ce înseamnă acest lucru pentru utilizator este că toți asistența dispozitivului din sistem o vor face
au toate metodele de urmărire pcap disponibile; iar aceste metode vor funcționa toate la fel
drum peste dispozitive dacă dispozitivul implementează EnablePcapInternal corect.
Pcap calc Dispozitiv Ajutor Aplicate
void EnablePcap (prefix std::string, Ptr nd,
bool promiscuous = fals, bool explicitFilename = fals);
void EnablePcap (prefix std::string, std::string ndName,
bool promiscuous = fals, bool explicitFilename = fals);
void EnablePcap (prefix std::string, NetDeviceContainer d,
bool promiscuu = fals);
void EnablePcap (prefix std::string, NodeContainer n,
bool promiscuu = fals);
void EnablePcap (std::prefix șir, uint32_t nodeid, uint32_t deviceid,
bool promiscuu = fals);
void EnablePcapAll (prefix std::string, bool promiscuous = false);
În fiecare dintre metodele prezentate mai sus, există un parametru implicit numit promiscuu acea
implicit la false. Acest parametru indică faptul că urma nu trebuie adunată
modul promiscuu. Dacă doriți ca urmele dvs. să includă tot traficul văzut de dispozitiv
(și dacă dispozitivul acceptă un mod promiscuu), adăugați pur și simplu un parametru adevărat la oricare dintre
apelurile de mai sus. De exemplu,:
Ptr nd;
...
helper.EnablePcap ("prefix", nd, true);
va permite capturi în mod promiscuu pe NetDevice specificat de nd.
Primele două metode includ și un parametru implicit numit explicitFilename asta va fi
fi discutat mai jos.
Sunteți încurajat să citiți Doxygen pentru curs PcapHelperForDevice pentru a afla detaliile
a acestor metode; dar pentru a rezuma...
Puteți activa urmărirea pcap pe o anumită pereche nod/net-dispozitiv furnizând un
Ptr la un EnablePcap metodă. Ptr este implicit din moment ce dispozitivul net
trebuie să aparțină exact unuia Nod. De exemplu,:
Ptr nd;
...
helper.EnablePcap ("prefix", nd);
Puteți activa urmărirea pcap pe o anumită pereche nod/net-dispozitiv furnizând un
std::string reprezentând un șir de servicii de nume de obiect la un EnablePcap metodă.
Ptr este căutat din șirul de nume. Din nou, este implicită din moment ce
dispozitivul net numit trebuie să aparțină exact unuia Nod. De exemplu,:
Nume::Add ("server" ...);
Nume::Add ("server/eth0" ...);
...
helper.EnablePcap ("prefix", "server/ath0");
Puteți activa urmărirea pcap pe o colecție de perechi nod/net-dispozitiv furnizând a
NetDeviceContainer. Pentru fiecare NetDevice în container se verifică tipul. Pentru fiecare
dispozitiv de tipul adecvat (același tip cu cel gestionat de dispozitivul de ajutor), urmărirea este
activat. Din nou, este implicit deoarece dispozitivul net găsit trebuie să aparțină exact
unu Nod. De exemplu,:
NetDeviceContainer d = ...;
...
helper.EnablePcap ("prefix", d);
Puteți activa urmărirea pcap pe o colecție de perechi nod/net-dispozitiv furnizând a
NodeContainer. Pentru fiecare Nod în NodeContainer este atașat NetDevices sunt iterate.
Pentru fiecare NetDevice atașat fiecărui nod din container, tipul dispozitivului respectiv este
verificat. Pentru fiecare dispozitiv de tipul adecvat (același tip cu cel gestionat de dispozitiv
helper), urmărirea este activată.:
NodeContainer n;
...
helper.EnablePcap ("prefix", n);
Puteți activa urmărirea pcap pe baza ID-ului nodului și ID-ului dispozitivului, precum și cu explicit
PTR. Fiecare Nod în sistem are un ID de nod întreg și fiecare dispozitiv conectat la un nod
are un ID de dispozitiv întreg.:
helper.EnablePcap ("prefix", 21, 1);
În cele din urmă, puteți activa urmărirea pcap pentru toate dispozitivele din sistem, cu același tip ca
care este gestionat de asistentul dispozitivului.:
helper.EnablePcapAll ("prefix");
Pcap calc Dispozitiv Ajutor Filename Selecţie
În descrierile metodelor de mai sus este implicită construcția unui nume de fișier complet de către
metoda de implementare. Prin convenție, pcap urmărește în ns-3 sistem sunt de forma
- id>- id>.pcap
După cum sa menționat anterior, fiecare nod din sistem va avea un ID de nod atribuit de sistem; și
fiecare dispozitiv va avea un index de interfață (numit și id de dispozitiv) relativ la nodul său.
Prin urmare, implicit, un fișier de urmărire pcap creat ca urmare a activării urmăririi pe primul
dispozitivul nodului 21 care utilizează prefixul „prefix” ar fi prefix-21-1.pcap.
Puteți utiliza întotdeauna ns-3 serviciu de nume obiect pentru a face acest lucru mai clar. De exemplu, dacă
utilizați serviciul de nume obiect pentru a atribui numele „server” nodului 21, pcap rezultat
numele fișierului de urmărire va deveni automat, prefix-server-1.pcap iar dacă atribuiți și
numele „eth0” pe dispozitiv, numele fișierului dumneavoastră pcap îl va prelua automat și va fi
denumit prefix-server-eth0.pcap.
În cele din urmă, două dintre metodele prezentate mai sus:
void EnablePcap (prefix std::string, Ptr nd, bool promiscuous = fals, bool explicitFilename = fals);
void EnablePcap (std::string prefix, std::string ndName, bool promiscuous = false, bool explicitFilename = fals);
au un parametru implicit numit explicitFilename. Când este setat la adevărat, acest parametru
dezactivează mecanismul automat de completare a numelui de fișier și vă permite să creați un fișier explicit
nume de fișier. Această opțiune este disponibilă numai în metodele care permit urmărirea pcap pe a
dispozitiv unic.
De exemplu, pentru a aranja ca un asistent de dispozitiv să creeze un singur pccap promiscuu
fișier de captură cu un anumit nume (my-pcap-file.pcap) pe un dispozitiv dat, se poate:
Ptr nd;
...
helper.EnablePcap ("my-pcap-file.pcap", nd, true, true);
Prima adevărat parametrul activează urmele modului promiscuu, iar al doilea îi spune ajutorului
a interpreta prefix parametru ca nume complet de fișier.
ascii calc Dispozitiv Ajutoare
Comportamentul asistentului de urmărire ascii mixin este substanțial similar cu versiunea pcap.
Aruncati o privire la src/network/helper/trace-helper.h daca vrei sa urmaresti discutia
în timp ce se uită la codul real.
Clasa AsciiTraceHelperForDevice adaugă funcționalitatea de nivel înalt pentru utilizarea ascii
urmărirea la o clasă de ajutor de dispozitiv. Ca și în cazul pcap, fiecare dispozitiv trebuie să implementeze a
o singură metodă virtuală moștenită din urma ascii mixin.:
virtual void EnableAsciiInternal (Ptr stream, std::string prefix, Ptr nd) = 0;
Semnătura acestei metode reflectă viziunea centrată pe dispozitiv a situației la aceasta
nivel; și, de asemenea, faptul că asistentul poate scrie într-un flux de ieșire partajat. Toate
metodele publice legate de urmările ascii moștenite de la clasă AsciiTraceHelperForDevice
reduceți la apelarea acestei metode de implementare unică dependentă de dispozitiv. De exemplu, cel
metode de urmărire ascii de cel mai scăzut nivel,:
void EnableAscii (prefix std::string, Ptr nd);
void EnableAscii (Ptr pârâu, Ptr nd);
va apela implementarea dispozitivului de EnableAsciiInternal direct, oferind fie a
prefix sau flux valid. Toate celelalte metode publice de urmărire ASCII se vor baza pe acestea
funcții de nivel scăzut pentru a oferi funcționalități suplimentare la nivel de utilizator. Ce înseamnă asta pentru
utilizatorul este că toți ajutoarele de dispozitiv din sistem vor avea toate metodele de urmărire ASCII
disponibil; și aceste metode vor funcționa toate în același mod pe toate dispozitivele dacă dispozitivele
punerea în aplicare a EnablAsciiInternal corect.
ascii calc Dispozitiv Ajutor Aplicate
void EnableAscii (prefix std::string, Ptr nd);
void EnableAscii (Ptr pârâu, Ptr nd);
void EnableAscii (std::string prefix, std::string ndName);
void EnableAscii (Ptr stream, std::string ndName);
void EnableAscii (prefix std::string, NetDeviceContainer d);
void EnableAscii (Ptr flux, NetDeviceContainer d);
void EnableAscii (prefix std::string, NodeContainer n);
void EnableAscii (Ptr flux, NodeContainer n);
void EnableAscii (prefix std::string, uint32_t nodeid, uint32_t deviceid);
void EnableAscii (Ptr flux, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiAll (prefix std::string);
void EnableAsciiAll (Ptr curent);
Sunteți încurajat să citiți Doxygen pentru curs TraceHelperForDevice pentru a găsi
detalii despre aceste metode; dar pentru a rezuma...
Există de două ori mai multe metode disponibile pentru urmărirea ascii decât pentru pcap
trasarea. Acest lucru se datorează faptului că, în plus față de modelul în stil pcap unde urme din fiecare
perechea unică nod/dispozitiv sunt scrise într-un fișier unic, acceptăm un model în care urmărește
informațiile pentru multe perechi noduri/dispozitive sunt scrise într-un fișier comun. Aceasta înseamnă că
- - mecanismul de generare a numelui de fișier este înlocuit cu un mecanism de
se referă la un fișier comun; iar numărul de metode API este dublat pentru a permite toate
combinații.
La fel ca în urmărirea pcap, puteți activa urmărirea ascii pe o anumită pereche nod/net-dispozitiv
prin furnizarea unui Ptr la un EnableAscii metodă. Ptr este implicit din moment ce
dispozitivul net trebuie să aparțină exact unuia Nod. De exemplu,:
Ptr nd;
...
helper.EnableAscii („prefix”, nd);
În acest caz, nu sunt scrise contexte de urmărire în fișierul de urmărire ASCII, deoarece ar fi
redundant. Sistemul va alege numele fișierului care urmează să fie creat folosind aceleași reguli ca
descrise în secțiunea pcap, cu excepția faptului că fișierul va avea sufixul „.tr” în loc de
„.pcap”.
Dacă doriți să activați urmărirea ASCII pe mai mult de un dispozitiv net și să trimiteți toate urmele
la un singur fișier, puteți face asta și folosind un obiect pentru a face referire la un singur fișier:
Ptr nd1;
Ptr nd2;
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAscii (stream, nd1);
helper.EnableAscii (stream, nd2);
În acest caz, contextele de urmărire sunt scrise în fișierul de urmărire ASCII, deoarece sunt necesare
pentru a dezambiguiza urmele de la cele două dispozitive. Rețineți că, deoarece utilizatorul este complet
specificând numele fișierului, șirul ar trebui să includă „.tr” pentru consecvență.
Puteți activa urmărirea ascii pe o anumită pereche nod/net-dispozitiv furnizând un
std::string reprezentând un șir de servicii de nume de obiect la un EnablePcap metodă.
Ptr este căutat din șirul de nume. Din nou, este implicită din moment ce
dispozitivul net numit trebuie să aparțină exact unuia Nod. De exemplu,:
Nume::Adăugați ("client" ...);
Nume::Add ("client/eth0" ...);
Nume::Add ("server" ...);
Nume::Add ("server/eth0" ...);
...
helper.EnableAscii ("prefix", "client/eth0");
helper.EnableAscii ("prefix", "server/eth0");
Acest lucru ar avea ca rezultat două fișiere numite prefix-client-eth0.tr și prefix-server-eth0.tr implementate cu
urme pentru fiecare dispozitiv din fișierul de urmărire respectiv. Din moment ce toate EnableAscii
funcțiile sunt supraîncărcate pentru a prelua un stream wrapper, puteți utiliza și acea formă:
Nume::Adăugați ("client" ...);
Nume::Add ("client/eth0" ...);
Nume::Add ("server" ...);
Nume::Add ("server/eth0" ...);
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAscii (stream, "client/eth0");
helper.EnableAscii (stream, "server/eth0");
Acest lucru ar avea ca rezultat un singur fișier de urmărire numit trace-file-name.tr care contine toate
evenimentele de urmărire pentru ambele dispozitive. Evenimentele ar fi dezambiguate de contextul urmelor
siruri de caractere.
Puteți activa urmărirea ascii pe o colecție de perechi nod/net-dispozitiv furnizând un
NetDeviceContainer. Pentru fiecare NetDevice în container se verifică tipul. Pentru fiecare
dispozitiv de tipul adecvat (același tip cu cel gestionat de dispozitivul de ajutor), urmărirea este
activat. Din nou, este implicit deoarece dispozitivul net găsit trebuie să aparțină exact
unu Nod. De exemplu,:
NetDeviceContainer d = ...;
...
helper.EnableAscii („prefix”, d);
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, fiecare dintre ele urmează
cel - - .tr conventie. Combinând toate urmele într-un
un singur fișier se realizează în mod similar cu exemplele de mai sus:
NetDeviceContainer d = ...;
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAscii (stream, d);
Puteți activa urmărirea ascii pe o colecție de perechi nod/net-dispozitiv furnizând un
NodeContainer. Pentru fiecare Nod în NodeContainer este atașat NetDevices sunt iterate.
Pentru fiecare NetDevice atașat fiecărui nod din container, tipul dispozitivului respectiv este
verificat. Pentru fiecare dispozitiv de tipul adecvat (același tip cu cel gestionat de dispozitiv
helper), urmărirea este activată.:
NodeContainer n;
...
helper.EnableAscii („prefix”, n);
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, fiecare dintre ele urmează
cel - - .tr conventie. Combinând toate urmele într-un
un singur fișier se realizează în mod similar cu exemplele de mai sus:
Puteți activa urmărirea pcap pe baza ID-ului nodului și ID-ului dispozitivului, precum și cu explicit
PTR. Fiecare Nod în sistem are un ID de nod întreg și fiecare dispozitiv conectat la un nod
are un ID de dispozitiv întreg.:
helper.EnableAscii („prefix”, 21, 1);
Desigur, urmele pot fi combinate într-un singur fișier, așa cum se arată mai sus.
În cele din urmă, puteți activa urmărirea pcap pentru toate dispozitivele din sistem, cu același tip ca
care este gestionat de asistentul dispozitivului.:
helper.EnableAsciiAll ("prefix");
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, unul pentru fiecare dispozitiv din interior
sistemul de tipul gestionat de ajutor. Toate aceste fișiere vor urma
- - .tr conventie. Combinând toate urmele într-un singur
fișierul se realizează în mod similar cu exemplele de mai sus.
ascii calc Dispozitiv Ajutor Filename Selecţie
Implicită în descrierile metodei în stil prefix de mai sus este construcția completului
nume de fișiere prin metoda de implementare. Prin convenție, urmele ascii în ns-3 sistem sunt
a formei - id>- id>.tr.
După cum sa menționat anterior, fiecare nod din sistem va avea un ID de nod atribuit de sistem; și
fiecare dispozitiv va avea un index de interfață (numit și id de dispozitiv) relativ la nodul său.
Prin urmare, implicit, un fișier de urmărire ASCII creat ca urmare a activării urmăririi pe primul
dispozitivul nodului 21, folosind prefixul „prefix”, ar fi prefix-21-1.tr.
Puteți utiliza întotdeauna ns-3 serviciu de nume obiect pentru a face acest lucru mai clar. De exemplu, dacă
utilizați serviciul de nume obiect pentru a atribui numele „server” nodului 21, rezultatul
numele fișierului de urmărire ascii va deveni automat, prefix-server-1.tr iar dacă de asemenea atribui
numele „eth0” pe dispozitiv, numele fișierului de urmărire ASCII va prelua automat acest lucru
și fii chemat prefix-server-eth0.tr.
Pcap calc Protocol Ajutoare
Scopul acestora mixine este de a face mai ușor să adăugați o facilitate de urmărire pcap consistentă la
protocoale. Dorim ca toate diversele arome ale urmăririi pcap să funcționeze la fel în toate
protocoale, astfel încât metodele acestor ajutoare sunt moștenite de ajutoarele de stivă. Aruncăm o privire la
src/network/helper/trace-helper.h dacă vrei să urmărești discuția în timp ce te uiți la
cod real.
În această secțiune vom ilustra metodele aplicate protocolului IPv4. Pentru
specificați urme în protocoale similare, înlocuiți doar tipul corespunzător. De exemplu,
folosește o Ptr în loc de a Ptr și sunați Activați PcapIpv6 în loc de Activați PcapIpv4.
Clasa PcapHelperForIpv4 oferă funcționalitatea de nivel înalt pentru utilizarea urmăririi pcap
în IPv4 protocol. Fiecare asistent de protocol care permite aceste metode trebuie să implementeze unul singur
metoda virtuala mostenita din aceasta clasa. Va exista o implementare separată pentru
IPv6, de exemplu, dar singura diferență va fi în numele metodelor și semnăturile.
Sunt necesare nume de metode diferite pentru a dezambigua clasa IPv4 de la IPv6 care sunt ambele
derivat din clasă Obiect, și metode care au aceeași semnătură.:
virtual void EnablePcapIpv4Internal (prefix std::string, Ptr ipv4, interfață uint4_t) = 32;
Semnătura acestei metode reflectă protocolul și vizualizarea centrată pe interfață a
situatie la acest nivel. Toate metodele publice moștenite de la clasă PcapHelperForIpv4
reduceți la apelarea acestei metode de implementare unică dependentă de dispozitiv. De exemplu, cel
metoda pcap de cel mai jos nivel,:
void EnablePcapIpv4 (prefix std::string, Ptr ipv4, interfață uint4_t);
va apela implementarea dispozitivului de Activați PcapIpv4Internal direct. Toate celelalte publice
Metodele de urmărire pcap se bazează pe această implementare pentru a oferi un nivel suplimentar de utilizator
funcţionalitate. Ce înseamnă acest lucru pentru utilizator este că toți ajutoarele de protocol din sistem o vor face
au toate metodele de urmărire pcap disponibile; iar aceste metode vor funcționa toate la fel
de-a lungul protocoalelor dacă helper-ul implementează Activați PcapIpv4Internal corect.
Pcap calc Protocol Ajutor Aplicate
Aceste metode sunt concepute pentru a fi în corespondență unu-la-unu cu Nod- și
NetDevice- versiuni centrate ale versiunilor de dispozitiv. In loc de Nod și NetDevice pereche
constrângeri, folosim constrângeri de protocol și interfață.
Rețineți că, la fel ca în versiunea dispozitivului, există șase metode:
void EnablePcapIpv4 (prefix std::string, Ptr ipv4, interfață uint4_t);
void EnablePcapIpv4 (std::string prefix, std::string ipv4Name, interfață uint32_t);
void EnablePcapIpv4 (prefix std::string, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (std::prefix șir, NodeContainer n);
void EnablePcapIpv4 (std::prefix șir, uint32_t nodeid, uint32_t interfață);
void EnablePcapIpv4All (prefix std::string);
Sunteți încurajat să citiți Doxygen pentru curs PcapHelperForIpv4 pentru a afla detaliile
a acestor metode; dar pentru a rezuma...
Puteți activa urmărirea pcap pe o anumită pereche de protocol/interfață furnizând un
Ptr și interfață la un EnablePcap metodă. De exemplu,:
Ptr ipv4 = nod->GetObject ();
...
helper.EnablePcapIpv4 („prefix”, ipv4, 0);
Puteți activa urmărirea pcap pe o anumită pereche nod/net-dispozitiv furnizând un
std::string reprezentând un șir de servicii de nume de obiect la un EnablePcap metodă.
Ptr este căutat din șirul de nume. De exemplu,:
Names::Add ("serverIPv4" ...);
...
helper.EnablePcapIpv4 ("prefix", "serverIpv4", 1);
Puteți activa urmărirea pcap pe o colecție de perechi de protocol/interfață furnizând un
IPv4InterfaceContainer. Pentru fiecare IPv4 / perechea de interfață în container tipul de protocol
este bifat. Pentru fiecare protocol de tipul adecvat (același tip cu cel gestionat de
dispozitiv de ajutor), urmărirea este activată pentru interfața corespunzătoare. De exemplu,:
Nodurile NodeContainer;
...
NetDeviceContainer devices = deviceHelper.Install (noduri);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfețe Ipv4InterfaceContainer = ipv4.Assign (dispozitive);
...
helper.EnablePcapIpv4 („prefix”, interfețe);
Puteți activa urmărirea pcap pe o colecție de perechi de protocol/interfață furnizând un
NodeContainer. Pentru fiecare Nod în NodeContainer se găsește protocolul corespunzător. Pentru
fiecare protocol, interfețele sale sunt enumerate și urmărirea este activată pe rezultat
perechi. De exemplu,:
NodeContainer n;
...
helper.EnablePcapIpv4 ("prefix", n);
Puteți activa urmărirea pcap și pe baza ID-ului nodului și a interfeței. În acest caz,
ID-ul nodului este tradus în a Ptr iar protocolul corespunzător este căutat în
nodul. Protocolul rezultat și interfața sunt utilizate pentru a specifica urma rezultată
sursă.:
helper.EnablePcapIpv4 ("prefix", 21, 1);
În cele din urmă, puteți activa urmărirea pcap pentru toate interfețele din sistem, cu asociate
protocolul fiind de același tip cu cel gestionat de asistentul dispozitivului.:
helper.EnablePcapIpv4All ("prefix");
Pcap calc Protocol Ajutor Filename Selecţie
Implicită în toate descrierile metodelor de mai sus este construcția întregului
nume de fișiere prin metoda de implementare. Prin convenție, urmele pcap luate pentru dispozitive în
il ns-3 sistem sunt de forma - id>- id>.pcap. În cazul în care
urme de protocol, există o corespondență unu-la-unu între protocoale și Nodurile. Acest lucru este
deoarece protocolul Obiecte sunt agregate la Nod Obiecte. Din moment ce nu există un protocol global
id în sistem, folosim id-ul nodului corespunzător în denumirea fișierelor. Prin urmare, există o
posibilitatea de coliziuni de nume de fișiere în numele fișierelor de urmărire alese automat. Pentru asta
motiv, convenția numelui fișierului este schimbată pentru urmele protocolului.
După cum sa menționat anterior, fiecare nod din sistem va avea un ID de nod atribuit de sistem.
Deoarece există o corespondență unu-la-unu între instanțe de protocol și instanțe de nod
folosim id-ul nodului. Fiecare interfață are un ID de interfață în raport cu protocolul său. Folosim
convenția" -n -i .pcap" pentru denumirea fișierelor de urmărire în
ajutoare de protocol.
Prin urmare, în mod implicit, un fișier de urmărire pcap creat ca urmare a activării urmăririi
interfața 1 a protocolului Ipv4 al nodului 21 folosind prefixul „prefix” ar fi
„prefix-n21-i1.pcap”.
Puteți utiliza întotdeauna ns-3 serviciu de nume obiect pentru a face acest lucru mai clar. De exemplu, dacă
utilizați serviciul de nume obiect pentru a atribui numele „serverIpv4” la Ptr pe nod
21, numele fișierului de urmărire pcap rezultat va deveni automat,
„prefix-nserverIpv4-i1.pcap”.
ascii calc Protocol Ajutoare
Comportamentul ajutoarelor de urmărire ascii este substanțial similar cu cazul pcap. Ia o
privi la src/network/helper/trace-helper.h dacă vrei să urmărești discuția în timp ce
privind codul real.
În această secțiune vom ilustra metodele aplicate protocolului IPv4. Pentru
specificați urme în protocoale similare, înlocuiți doar tipul corespunzător. De exemplu,
folosește o Ptr în loc de a Ptr și sunați Activați AsciiIpv6 în loc de
Activați AsciiIpv4.
Clasa AsciiTraceHelperForIpv4 adaugă funcționalitatea de nivel înalt pentru utilizarea ascii
urmărirea la un asistent de protocol. Fiecare protocol care permite aceste metode trebuie să implementeze a
o singură metodă virtuală moștenită din această clasă.:
virtual void EnableAsciiIpv4Internal (Ptr flux, std::prefix șir,
Ptr ipv4, interfață uint4_t) = 32;
Semnătura acestei metode reflectă viziunea centrată pe protocol și interfață a
situația la acest nivel; și, de asemenea, faptul că asistentul poate scrie la un shared
flux de ieșire. Toate metodele publice moștenite de la clasă
PcapAndAsciiTraceHelperForIpv4 reduceți la apelarea acestui singur dispozitiv dependent
metoda de implementare. De exemplu, metodele de urmărire ascii de cel mai scăzut nivel,:
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interfață uint4_t);
void EnableAsciiIpv4 (Ptr pârâu, Ptr ipv4, interfață uint4_t);
va apela implementarea dispozitivului de EnableAsciiIpv4Internal direct, oferind fie
prefixul sau fluxul. Toate celelalte metode publice de urmărire ASCII se vor baza pe acestea
funcții de nivel scăzut pentru a oferi funcționalități suplimentare la nivel de utilizator. Ce înseamnă asta pentru
utilizatorul este că toți ajutoarele de dispozitiv din sistem vor avea toate metodele de urmărire ASCII
disponibil; și aceste metode vor funcționa toate în același mod peste protocoale dacă
implementarea protocoalelor EnablAsciiIpv4Internal corect.
ascii calc Dispozitiv Ajutor Aplicate
void EnableAsciiIpv4 (std::string prefix, Ptr ipv4, interfață uint4_t);
void EnableAsciiIpv4 (Ptr pârâu, Ptr ipv4, interfață uint4_t);
void EnableAsciiIpv4 (std::string prefix, std::string ipv4Name, interfață uint32_t);
void EnableAsciiIpv4 (Ptr flux, std::string ipv4Name, interfață uint32_t);
void EnableAsciiIpv4 (prefix std::string, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (Ptr flux, Ipv4InterfaceContainer c);
void EnableAsciiIpv4 (std::string prefix, NodeContainer n);
void EnableAsciiIpv4 (Ptr flux, NodeContainer n);
void EnableAsciiIpv4 (std::string prefix, uint32_t nodeid, uint32_t deviceid);
void EnableAsciiIpv4 (Ptr flux, uint32_t nodeid, interfață uint32_t);
void EnableAsciiIpv4All (prefix std::string);
void EnableAsciiIpv4All (Ptr curent);
Sunteți încurajat să citiți Doxygen pentru curs PcapAndAsciiHelperForIpv4 pentru a găsi
detalii despre aceste metode; dar pentru a rezuma...
Există de două ori mai multe metode disponibile pentru urmărirea ascii decât pentru pcap
trasarea. Acest lucru se datorează faptului că, în plus față de modelul în stil pcap unde urme din fiecare
perechea unică de protocol/interfață sunt scrise într-un fișier unic, acceptăm un model în care
informațiile de urmărire pentru multe perechi de protocol/interfață sunt scrise într-un fișier comun. Acest
înseamnă că -n - mecanismul de generare a numelui fișierului este înlocuit
printr-un mecanism de referire la un dosar comun; iar numărul de metode API este dublat la
permite toate combinațiile.
La fel ca în urmărirea pcap, puteți activa urmărirea ascii pe un anumit protocol/interfață
pereche prin furnizarea unui Ptr si un interfață la un EnableAscii metodă. De exemplu,:
Ptr ipv4;
...
helper.EnableAsciiIpv4 („prefix”, ipv4, 1);
În acest caz, nu sunt scrise contexte de urmărire în fișierul de urmărire ASCII, deoarece ar fi
redundant. Sistemul va alege numele fișierului care urmează să fie creat folosind aceleași reguli ca
descrise în secțiunea pcap, cu excepția faptului că fișierul va avea sufixul „.tr” în loc de
„.pcap”.
Dacă doriți să activați urmărirea ASCII pe mai multe interfețe și să primiți toate urmele către
un singur fișier, puteți face asta și folosind un obiect pentru a face referire la un singur fișier. Noi
au deja ceva similar cu asta în exemplul „cwnd” de mai sus:
Ptr protocol4 = node1->GetObject ();
Ptr protocol4 = node2->GetObject ();
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAsciiIpv4 (stream, protocol1, 1);
helper.EnableAsciiIpv4 (stream, protocol2, 1);
În acest caz, contextele de urmărire sunt scrise în fișierul de urmărire ASCII, deoarece sunt necesare
pentru a dezambiguiza urmele din cele două interfețe. Rețineți că, deoarece utilizatorul este complet
specificând numele fișierului, șirul ar trebui să includă „.tr” pentru consecvență.
Puteți activa urmărirea ascii pe un anumit protocol furnizând un std::string
reprezentând un șir de servicii de nume de obiect la un EnablePcap metodă. Ptr is
ridică privirea din șirul numelui. The în numele de fişiere rezultate este implicită deoarece
există o corespondență unu-la-unu între instanțe de protocol și noduri, De exemplu:
Nume::Adăugați ("nod1Ipv4" ...);
Nume::Adăugați ("nod2Ipv4" ...);
...
helper.EnableAsciiIpv4 ("prefix", "node1Ipv4", 1);
helper.EnableAsciiIpv4 ("prefix", "node2Ipv4", 1);
Acest lucru ar avea ca rezultat două fișiere numite „prefix-nnode1Ipv4-i1.tr” și
„prefix-nnode2Ipv4-i1.tr” cu urme pentru fiecare interfață din fișierul de urmărire respectiv.
Deoarece toate funcțiile EnableAscii sunt supraîncărcate pentru a prelua un stream wrapper, puteți
foloseste si acea forma:
Nume::Adăugați ("nod1Ipv4" ...);
Nume::Adăugați ("nod2Ipv4" ...);
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAsciiIpv4 (stream, „node1Ipv4”, 1);
helper.EnableAsciiIpv4 (stream, „node2Ipv4”, 1);
Acest lucru ar avea ca rezultat un singur fișier de urmărire numit „trace-file-name.tr” care conține toate
evenimentele de urmărire pentru ambele interfețe. Evenimentele ar fi dezambiguate de contextul urmelor
siruri de caractere.
Puteți activa urmărirea ascii pe o colecție de perechi de protocol/interfață furnizând un
IPv4InterfaceContainer. Pentru fiecare protocol de tipul adecvat (același tip cu cel gestionat
de către asistentul dispozitivului), urmărirea este activată pentru interfața corespunzătoare. Din nou,
este implicită deoarece între fiecare protocol există o corespondență unu-la-unu și
nodul acestuia. De exemplu,:
Nodurile NodeContainer;
...
NetDeviceContainer devices = deviceHelper.Install (noduri);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfețe Ipv4InterfaceContainer = ipv4.Assign (dispozitive);
...
...
helper.EnableAsciiIpv4 („prefix”, interfețe);
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, fiecare dintre ele urmează
cel -n -i .tr conventie. Combinând toate urmele într-un
un singur fișier se realizează în mod similar cu exemplele de mai sus:
Nodurile NodeContainer;
...
NetDeviceContainer devices = deviceHelper.Install (noduri);
...
Ipv4AddressHelper ipv4;
ipv4.SetBase ("10.1.1.0", "255.255.255.0");
Interfețe Ipv4InterfaceContainer = ipv4.Assign (dispozitive);
...
Ptr flux = asciiTraceHelper.CreateFileStream ("nume-fișier-urmă.tr");
...
helper.EnableAsciiIpv4 (stream, interfețe);
Puteți activa urmărirea ascii pe o colecție de perechi de protocol/interfață furnizând un
NodeContainer. Pentru fiecare Nod în NodeContainer se găsește protocolul corespunzător. Pentru
fiecare protocol, interfețele sale sunt enumerate și urmărirea este activată pe rezultat
perechi. De exemplu,:
NodeContainer n;
...
helper.EnableAsciiIpv4 ("prefix", n);
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, fiecare dintre ele urmează
cel - - .tr conventie. Combinând toate urmele într-un
un singur fișier se realizează în mod similar cu exemplele de mai sus:
Puteți activa urmărirea pcap și pe baza ID-ului nodului și a ID-ului dispozitivului. În acest caz,
ID-ul nodului este tradus în a Ptr iar protocolul corespunzător este căutat în
nodul. Protocolul rezultat și interfața sunt utilizate pentru a specifica urma rezultată
sursă.:
helper.EnableAsciiIpv4 („prefix”, 21, 1);
Desigur, urmele pot fi combinate într-un singur fișier, așa cum se arată mai sus.
În cele din urmă, puteți activa urmărirea ASCII pentru toate interfețele din sistem, cu asociate
protocolul fiind de același tip cu cel gestionat de asistentul dispozitivului.:
helper.EnableAsciiIpv4All ("prefix");
Acest lucru ar duce la crearea unui număr de fișiere de urmărire ASCII, unul pentru fiecare interfață
în sistemul legat de un protocol de tipul gestionat de helper. Toate aceste fișiere
va urma -n -i
într-un singur fișier se realizează în mod similar cu exemplele de mai sus.
ascii calc Dispozitiv Ajutor Filename Selecţie
Implicită în descrierile metodei în stil prefix de mai sus este construcția completului
nume de fișiere prin metoda de implementare. Prin convenție, urmele ascii în ns-3 sistem sunt
de forma " - - .tr."
După cum sa menționat anterior, fiecare nod din sistem va avea un ID de nod atribuit de sistem.
Deoarece există o corespondență unu-la-unu între protocoale și noduri, folosim pentru a node-id
pentru a identifica identitatea protocolului. Fiecare interfață dintr-un anumit protocol va avea un
index de interfață (numit și simplu interfață) în raport cu protocolul său. În mod implicit,
apoi, un fișier de urmărire ASCII creat ca urmare a activării urmăririi pe primul dispozitiv al
nodul 21, folosind prefixul „prefix”, ar fi „prefix-n21-i1.tr”. Utilizați prefixul pentru
dezambiguizează mai multe protocoale per nod.
Puteți utiliza întotdeauna ns-3 serviciu de nume obiect pentru a face acest lucru mai clar. De exemplu, dacă
utilizați serviciul de nume obiect pentru a atribui numele „serverIpv4” protocolului de pe nod
21 și, de asemenea, specificați interfața unu, numele fișierului de urmărire ascii rezultat va fi automat
deveni, „prefix-nserverIpv4-1.tr”.
calc implementarea detalii
Date Colectie
Acest capitol descrie ns-3 Data Collection Framework (DCF), care oferă
capabilități de a obține date generate de modele în simulator, de a efectua on-line
reducerea și prelucrarea datelor și pentru a distribui datele brute sau transformate în diverse rezultate
formate.
Cadrul acceptă în prezent rulări autonome ns-3 care nu se bazează pe niciun extern
controlul execuției programului. Obiectele furnizate de DCF pot fi agățate ns-3 urmări
surse pentru a permite prelucrarea datelor.
Codul sursă pentru clase se află în director src/stats.
Acest capitol este organizat după cum urmează. În primul rând, o privire de ansamblu asupra arhitecturii este
prezentat. În continuare, sunt prezentați ajutoarele pentru aceste clase; acest tratament inițial
ar trebui să permită utilizarea de bază a cadrului de colectare a datelor pentru multe cazuri de utilizare. Utilizatorii care
doresc să producă rezultate în afara domeniului de asistență actuali sau care doresc să creeze
propriile obiecte de colectare a datelor, ar trebui să citească restul capitolului, care continuă
în detaliu despre toate tipurile de obiecte DCF de bază și oferă codare de nivel scăzut
exemple.
Amenajări
DCF constă din trei clase de bază:
· Sondă este un mecanism de instrumentare și control a ieșirii datelor de simulare, adică
folosit pentru a monitoriza evenimente interesante. Produce rezultate sub formă de unul sau mai multe ns-3
surse de urme. Obiectele sondei sunt conectate la una sau mai multe urme chiuvete (numite
Colecționari), care procesează probe on-line și le pregătesc pentru ieșire.
· Colector consumă datele generate de unul sau mai multe obiecte Probe. Funcționează
transformări ale datelor, cum ar fi normalizarea, reducerea și calculul
statistici de bază. Obiectele colectoare nu produc date care sunt scoase direct de către
ns-3 alergare; în schimb, scot date în aval către un alt tip de obiect, numit
Agregator, care îndeplinește acea funcție. De obicei, colectorii își scot datele în
de asemenea, sub formă de surse de urme, permițând colectorilor să fie înlănțuite în serie.
· Agregator este punctul final al datelor colectate de o rețea de Sonde și Colectori.
Responsabilitatea principală a agregatorului este să trimită datele și corespunzătoare acestora
metadate, în diferite formate de ieșire, cum ar fi fișiere text simplu, fișiere de foi de calcul sau
baze de date.
Toate aceste trei clase oferă capacitatea de a se activa sau dezactiva dinamic
pe parcursul unei simulări.
Orice de sine stătător ns-3 rularea de simulare care utilizează DCF va crea de obicei cel puțin unul
exemplu pentru fiecare dintre cele trei clase de mai sus.
[imagine] Prezentare generală a cadrului de colectare a datelor.UNINDENT
Fluxul general de prelucrare a datelor este descris în Date Colectie Cadru Prezentare generală.
În partea stângă, o alergare ns-3 este descrisă simularea. În cursul rulării
simulare, datele sunt puse la dispoziție prin modele prin surse de urmărire sau prin alte mijloace.
Diagrama ilustrează că sondele pot fi conectate la aceste surse de urmărire pentru a primi date
asincron, sau sondele pot sonda date. Datele sunt apoi transmise unui obiect colector
care transformă datele. În cele din urmă, un agregator poate fi conectat la ieșirile lui
colector, pentru a genera diagrame, fișiere sau baze de date.
[imagine] Agregarea cadrului de colectare a datelor.UNINDENT
O variație a cifrei de mai sus este oferită în Date Colectie Cadru agregare.
Această a doua figură ilustrează faptul că obiectele DCF pot fi înlănțuite într-un fel
că obiectele din aval preia intrări de la mai multe obiecte din amonte. Figura
conceptual arată că mai multe sonde pot genera ieșire care este alimentată într-o singură
colector; de exemplu, un colector care scoate un raport de două contoare ar
în mod obișnuit, achiziționează fiecare contor de date de la sonde separate. Mai mulți colecționari pot, de asemenea
furnizează într-un singur agregator, care (după cum sugerează și numele) poate colecta o serie de date
fluxuri pentru includerea într-o singură parcelă, fișier sau bază de date.
Date Colectie Ajutoare
Flexibilitatea deplină a cadrului de colectare a datelor este asigurată de interconectare
de sonde, colectoare și agregatoare. Efectuarea tuturor acestor interconexiuni duce la
multe instrucțiuni de configurare în programele utilizatorului. Pentru ușurință în utilizare, unele dintre cele mai comune
operațiunile pot fi combinate și încapsulate în funcții de ajutor. În plus, unii
declaraţii care implică ns-3 Sursele de urmărire nu au legături Python, din cauza limitărilor în
legăturile.
Date Colectie Ajutoare Descriere
În această secțiune, oferim o prezentare generală a unor clase de ajutor care au fost create pentru
ușurează configurarea cadrului de colectare a datelor pentru unele cazuri de utilizare obișnuite. The
ajutoarele permit utilizatorilor să formeze operațiuni comune cu doar câteva instrucțiuni în C++ sau
Programe Python. Însă, această ușurință în utilizare vine cu costul mult mai puțin
flexibilitate decât o poate oferi configurația de nivel scăzut și necesitatea de a codifica în mod explicit
suport pentru noile tipuri de sonde în ajutoare (pentru a rezolva o problemă descrisă mai jos).
Accentul pus pe ajutoarele actuale este de a extrage datele ns-3 urme surse în
diagrame gnuplot sau fișiere text, fără un grad ridicat de personalizare a ieșirii sau statistice
prelucrare (inițial). De asemenea, utilizarea este limitată la tipurile de sonde disponibile în
ns-3. Secțiunile ulterioare ale acestei documentații vor intra în mai multe detalii despre crearea de noi
Tipuri de sonde, precum și detalii despre conectarea împreună a sondelor, colectoarelor și agregatorilor
în aranjamente personalizate.
Până în prezent, au fost implementați doi asistență pentru colectarea datelor:
· GnuplotHelper
· FileHelper
GnuplotHelper
GnuplotHelper este o clasă de ajutor pentru producerea fișierelor de ieșire utilizate pentru a face gnuplots. The
Scopul general este de a oferi utilizatorilor posibilitatea de a face rapid diagrame din datele exportate
in ns-3 surse de urme. În mod implicit, se realizează o cantitate minimă de transformare a datelor;
obiectivul este de a genera diagrame cu cât mai puține instrucțiuni de configurare (implicit).
posibil.
GnuplotHelper Descriere
GnuplotHelper va crea 3 fișiere diferite la sfârșitul simulării:
· Un fișier de date gnuplot separat de spațiu
· Un fișier de control gnuplot
· Un script shell pentru a genera gnuplot
Există două instrucțiuni de configurare care sunt necesare pentru a produce diagrame. Primul
instrucțiunea configurează diagrama (nume fișier, titlu, legende și tip de ieșire, unde este rezultatul
tipul este implicit PNG dacă nu este specificat):
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &title,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
A doua afirmație agăță urmele sursei de interes:
void PlotProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource,
const std::string &title);
Argumentele sunt următoarele:
· typeId: The ns-3 TypeId-ul sondei
· cale: Calea în ns-3 spațiu de nume de configurare la una sau mai multe surse de urmărire
· probeTraceSource: ce ieșire a sondei (în sine o sursă de urmărire) ar trebui să fie reprezentată grafic
· titlu: titlul de asociat cu setul (seturile) de date (în legenda gnuplot)
O variantă a PlotProbe de mai sus este de a specifica un al cincilea argument opțional care controlează
unde în complot este plasată cheia (legenda).
Un exemplu pe deplin lucrat (din al şaptelea.cc) este prezentat mai jos:
// Creați ajutorul gnuplot.
GnuplotHelper plotHelper;
// Configurați plotul.
// Configurați plotul. Primul argument este prefixul numelui fișierului
// pentru fișierele de ieșire generate. Al doilea, al treilea și al patrulea
// argumentele sunt, respectiv, titlul diagramei, axa x și etichetele axei y
plotHelper.ConfigurePlot ("număr-al șaptelea-pachet-octet",
„Număr de octeți de pachete față de timp”,
„Timp (secunde)”,
„Număr de octeți de pachet”,
„png”);
// Specificați tipul sondei, calea sursei de urmărire (în spațiul de nume de configurare) și
// Sondați sursa de urmărire a ieșirii ("OutputBytes") pentru a reprezenta grafic. Al patrulea argument
// specifică numele etichetei seriei de date de pe grafic. Ultimul
// argumentul formatează graficul specificând unde ar trebui plasată cheia.
plotHelper.PlotProbe (probeType,
tracePath,
„OutputBytes”,
„Număr de octeți de pachet”,
GnuplotAggregator::KEY_BELOW);
În acest exemplu, probeType și tracePath sunt după cum urmează (pentru IPv4):
probeType = "ns3::Ipv4PacketProbe";
tracePath = "/NodeList/*/$ns3::Ipv4L3Protocol/Tx";
probeType este un parametru cheie pentru ca acest ajutor să funcționeze. Acest TypeId trebuie să fie înregistrat
în sistem, iar semnătura de pe chiuveta de urmărire a sondei trebuie să se potrivească cu cea a urmei
sursa la care este conectat. Tipurile de sonde sunt predefinite pentru un număr de tipuri de date
corespunzătoare ns-3 valorile urmărite și pentru alte câteva semnături surse de urmărire, cum ar fi
sursa de urmărire „Tx” a ns3::Ipv4L3Protocol clasă.
Rețineți că calea sursă de urmărire specificată poate conține caractere joker. În acest caz, multiple
seturile de date sunt reprezentate pe un grafic; câte unul pentru fiecare cale potrivită.
Rezultatul principal produs va fi trei fișiere:
al șaptelea-pachet-octet-count.dat
al șaptelea-pachet-octet-count.plt
al șaptelea-pachet-octet-count.sh
În acest moment, utilizatorii pot fie edita manual fișierul .plt pentru personalizări suplimentare, fie
rulați-l prin gnuplot. Alergare sh al șaptelea-pachet-octet-count.sh pur și simplu rulează complotul
prin gnuplot, așa cum se arată mai jos.
[imagine] 2-D Gnuplot Creat de seventh.cc Exemplu..UNINDENT
Se poate observa că elementele cheie (legendă, titlu, plasare legendă, xlabel, ylabel,
și calea pentru date) sunt toate plasate pe parcelă. Din moment ce au fost două meciuri la
calea de configurare furnizată, sunt afișate cele două serii de date:
· Numărul de octeți de pachete-0 corespunde cu /NodeList/0/$ns3::Ipv4L3Protocol/Tx
· Numărul de octeți de pachete-1 corespunde cu /NodeList/1/$ns3::Ipv4L3Protocol/Tx
GnuplotHelper ConfigurePlot
De la GnuplotHelper's ConfigurePlot() funcția poate fi utilizată pentru a configura diagrame.
Are urmatorul prototip:
void ConfigurePlot (const std::string &outputFileNameWithoutExtension,
const std::string &title,
const std::string &xLegend,
const std::string &yLegend,
const std::string &terminalType = ".png");
Are urmatoarele argumente:
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┐
│Argument │ Descriere │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│outputFileNameWithoutExtension │ Numele fișierelor legate de gnuplot către │
│ │ scrie fără extensie. │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│titlu │ Trasează șirul de titlu de utilizat pentru │
│ │ acest complot. │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│xLegend │ Legenda pentru x orizontală │
│ │ axa. │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│yLegend │ Legenda pentru verticala y │
│ │ axa. │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┘
│terminalType │ șir de setare tip terminal pentru │
│ │ ieşire. Terminalul implicit │
│ │ tipul este „png”. │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┘
De la GnuplotHelper's ConfigurePlot() funcția configurează parametrii legați de diagramă pentru aceasta
gnuplot helper, astfel încât să creeze un fișier de date gnuplot separat de spațiu numit
outputFileNameWithoutExtension + „.dat”, un fișier de control gnuplot numit
outputFileNameWithoutExtension + „.plt” și un script shell pentru a genera gnuplot-ul numit
outputFileNameWithoutExtension + „.sh”.
Un exemplu de utilizare a acestei funcții poate fi văzut în al şaptelea.cc codul descris mai sus
unde a fost folosit după cum urmează:
plotHelper.ConfigurePlot ("număr-al șaptelea-pachet-octet",
„Număr de octeți de pachete față de timp”,
„Timp (secunde)”,
„Număr de octeți de pachet”,
„png”);
GnuplotHelper PlotProbe
De la GnuplotHelper's PlotProbe() funcția poate fi utilizată pentru a reprezenta grafic valorile generate de sonde.
Are urmatorul prototip:
void PlotProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource,
const std::string &title,
enumerare GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);
Are urmatoarele argumente:
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┐
│Argument │ Descriere │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│typeId │ ID-ul tipului pentru sondă │
│ │ creat de acest ajutor. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│cale │ Calea de configurare pentru a accesa traseul │
│ │ sursă. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│probeTraceSource │ Sursa de urmărire a sondei către │
│ │ acces. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│titlu │ Titlul care trebuie asociat cu │
│ │ acest set de date │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│keyLocation │ Locația cheii în │
│ │ complot. Locația implicită este │
│ │ înăuntru. │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┘
De la GnuplotHelper's PlotProbe() funcția trasează un set de date generat prin conectarea ns-3
Urmărirea sursei cu o sondă creată de asistent și apoi trasarea valorilor din
probeTraceSource. Setul de date va avea titlul furnizat și va consta din
„newValue” la fiecare marcaj temporal.
Dacă calea de configurare are mai multe potriviri în sistem, deoarece există un wildcard, atunci
va fi reprezentat un set de date pentru fiecare potrivire. Titlurile setului de date vor fi sufixate cu
caractere potrivite pentru fiecare dintre caracterele metalice din calea de configurare, separate prin spații. Pentru
de exemplu, dacă titlul setului de date propus este șirul „octeți” și există două metacaractere
în cale, atunci titlurile setului de date precum „bytes-0 0” sau „bytes-12 9” vor fi posibile ca
etichete pentru seturile de date care sunt reprezentate grafic.
Un exemplu de utilizare a acestei funcții poate fi văzut în al şaptelea.cc codul descris mai sus
unde a fost folosit (cu substituție variabilă) după cum urmează:
plotHelper.PlotProbe ("ns3::Ipv4PacketProbe",
„/NodeList/*/$ns3::Ipv4L3Protocol/Tx”,
„OutputBytes”,
„Număr de octeți de pachet”,
GnuplotAggregator::KEY_BELOW);
Altele Exemple
gnuplot Ajutor Exemplu
Un exemplu ceva mai simplu decât cel al şaptelea.cc exemplu poate fi găsit în
src/stats/examples/gnuplot-helper-example.cc. Următorul gnuplot 2-D a fost creat folosind
exemplul.
[imagine] Gnuplot 2-D Creat de gnuplot-helper-example.cc Exemplu..UNINDENT
În acest exemplu, există un obiect Emitter care își crește contorul conform a
procesează Poisson și apoi emite valoarea contorului ca sursă de urmărire.
Ptr emitter = CreateObject ();
Names::Add ("/Names/Emitter", emitter);
Rețineți că, deoarece nu există caractere joker în calea utilizată mai jos, a fost doar 1 flux de date
desenat în complot. Acest singur flux de date din diagramă este pur și simplu etichetat „Număr de emițători”,
fără sufixe suplimentare, așa cum s-ar vedea dacă ar exista metacaractere în cale.
// Creați ajutorul gnuplot.
GnuplotHelper plotHelper;
// Configurați plotul.
plotHelper.ConfigurePlot ("gnuplot-helper-example",
„Numărul emițătorului față de timp”,
„Timp (secunde)”,
„Numărul de emițători”,
„png”);
// Trasează valorile generate de sondă. Calea pe care o oferim
// ajută la dezambiguizarea sursei urmei.
plotHelper.PlotProbe ("ns3::Uinteger32Probe",
„/Nume/Emițător/Contor”,
„Ieșire”,
„Numărul de emițători”,
GnuplotAggregator::KEY_INSIDE);
FileHelper
FileHelper este o clasă de ajutor folosită pentru a pune valorile datelor într-un fișier. Scopul general este
pentru a oferi utilizatorilor posibilitatea de a realiza rapid fișiere text formatate din datele exportate
in ns-3 surse de urme. În mod implicit, se realizează o cantitate minimă de transformare a datelor;
obiectivul este de a genera fișiere cu cât mai puține instrucțiuni de configurare (implicit).
posibil.
FileHelper Descriere
FileHelper va crea 1 sau mai multe fișiere text la sfârșitul simulării.
FileHelper poate crea 4 tipuri diferite de fișiere text:
· Formatate
· Spațiu separat (prestabilit)
· Separate prin virgulă
· Tab separate
Fișierele formatate folosesc șiruri de format în stil C și funcția sprintf() pentru a le imprima
valorile din fișierul care se scrie.
Următorul fișier text cu 2 coloane de valori formatate numit
al șaptelea-pachet-octet-count-0.txt a fost creat folosind mai mult cod nou care a fost adăugat la
original ns-3 Cod exemplu de tutorial. Sunt afișate doar primele 10 rânduri ale acestui fișier
aici pentru concizie.
Timp (secunde) = 1.000e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.004e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.004e+00 Număr de octeți de pachete = 576
Timp (secunde) = 1.009e+00 Număr de octeți de pachete = 576
Timp (secunde) = 1.009e+00 Număr de octeți de pachete = 576
Timp (secunde) = 1.015e+00 Număr de octeți de pachete = 512
Timp (secunde) = 1.017e+00 Număr de octeți de pachete = 576
Timp (secunde) = 1.017e+00 Număr de octeți de pachete = 544
Timp (secunde) = 1.025e+00 Număr de octeți de pachete = 576
Timp (secunde) = 1.025e+00 Număr de octeți de pachete = 544
...
Următorul fișier text diferit cu 2 coloane de valori formatate numit
al șaptelea-pachet-octet-count-1.txt a fost, de asemenea, creat folosind același cod nou care a fost adăugat la
originală ns-3 Cod exemplu de tutorial. Sunt afișate doar primele 10 rânduri ale acestui fișier
aici pentru concizie.
Timp (secunde) = 1.002e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.007e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.013e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.020e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.028e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.036e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.045e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.053e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.061e+00 Număr de octeți de pachete = 40
Timp (secunde) = 1.069e+00 Număr de octeți de pachete = 40
...
Noul cod care a fost adăugat pentru a produce cele două fișiere text este mai jos. Mai multe detalii despre
acest API va fi tratat într-o secțiune ulterioară.
Rețineți că, deoarece au existat 2 potriviri pentru wildcard în cale, 2 fișiere text separate
Au fost create. Primul fișier text, care se numește „seventh-packet-byte-count-0.txt”,
corespunde potrivirii wildcard-ului cu „*” înlocuit cu „0”. Al doilea fișier text,
care se numește „seventh-packet-byte-count-1.txt”, corespunde potrivirii wildcard cu
„*” înlocuit cu „1”. De asemenea, rețineți că funcția apel la WriteProbe() va da o
mesaj de eroare dacă nu există potriviri pentru o cale care conține caractere joker.
// Creați ajutorul fișierului.
FileHelper fileHelper;
// Configurați fișierul care urmează să fie scris.
fileHelper.ConfigureFile ("numărul de octeți de pachete al șaptelea",
FileAggregator::FORMATED);
// Setați etichetele pentru acest fișier de ieșire formatat.
fileHelper.Set2dFormat ("Timp (secunde) = %.3e\tNumăr de octeți de pachete = %.0f");
// Scrieți valorile generate de sondă.
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
„/NodeList/*/$ns3::Ipv4L3Protocol/Tx”,
"OutputBytes");
FileHelper ConfigureFile
De la FileHelper ConfigureFile() funcția poate fi utilizată pentru a configura fișiere text.
Are urmatorul prototip:
void ConfigureFile (const std::string &outputFileNameWithoutExtension,
enum FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);
Are urmatoarele argumente:
┌─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┐
│Argument │ Descriere │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│outputFileNameWithoutExtension │ Numele fișierului de ieșire de scris │
│ │ fără prelungire. │
├─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┤
│fileType │ Tipul de fișier de scris. │
│ │ tipul implicit de fișier este spațiu │
│ │ separate. │
└─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ─────────────────┘
De la FileHelper ConfigureFile() funcția configurează parametrii legați de fișierul text pentru
asistent de fișier, astfel încât să creeze un fișier numit outputFileNameWithoutExtension plus
posibile informații suplimentare din potrivirile wildcard plus „.txt” cu valori tipărite ca
specificat de fileType. Tipul de fișier implicit este separat prin spațiu.
Un exemplu de utilizare a acestei funcții poate fi văzut în al şaptelea.cc codul descris mai sus
unde a fost folosit după cum urmează:
fileHelper.ConfigureFile ("numărul de octeți de pachete al șaptelea",
FileAggregator::FORMATED);
FileHelper WriteProbe
De la FileHelper WriteProbe() funcția poate fi folosită pentru a scrie valorile generate de sonde în
fișiere text.
Are urmatorul prototip:
void WriteProbe (const std::string &typeId,
const std::string &path,
const std::string &probeTraceSource);
Are urmatoarele argumente:
┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┐
│Argument │ Descriere │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│typeId │ ID-ul tipului pentru sondă care urmează să fie │
│ │ creat. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│cale │ Calea de configurare pentru a accesa traseul │
│ │ sursă. │
├────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┤
│probeTraceSource │ Sursa de urmărire a sondei către │
│ │ acces. │
└────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────- ───┘
De la FileHelper WriteProbe() funcția creează fișiere text de ieșire generate prin conectarea
sursa de urmărire ns-3 cu o sondă creată de asistent și apoi scrierea valorilor din
probeTraceSource. Numele fișierelor de ieșire vor avea textul stocat în variabila membru
m_outputFileNameWithoutExtension plus „.txt” și va consta din „newValue” la fiecare
timestamp-ul.
Dacă calea de configurare are mai multe potriviri în sistem, deoarece există un wildcard, atunci
va fi creat un fișier de ieșire pentru fiecare potrivire. Numele fișierelor de ieșire vor conține
text în m_outputFileNameWithoutExtension plus caracterele potrivite pentru fiecare dintre
wildcards în calea de configurare, separate prin liniuțe, plus „.txt”. De exemplu, dacă valoarea
în m_outputFileNameWithoutExtension este șirul „packet-byte-count” și există două
wildcards în cale, apoi scoateți nume de fișiere precum „packet-byte-count-0-0.txt” sau
„packet-byte-count-12-9.txt” va fi posibil ca nume pentru fișierele care vor fi create.
Un exemplu de utilizare a acestei funcții poate fi văzut în al şaptelea.cc codul descris mai sus
unde a fost folosit după cum urmează:
fileHelper.WriteProbe ("ns3::Ipv4PacketProbe",
„/NodeList/*/$ns3::Ipv4L3Protocol/Tx”,
"OutputBytes");
Altele Exemple
Fișier Ajutor Exemplu
Un exemplu ceva mai simplu decât cel al şaptelea.cc exemplu poate fi găsit în
src/stats/examples/file-helper-example.cc. Acest exemplu folosește numai FileHelper.
Următorul fișier text cu 2 coloane de valori formatate numit file-helper-example.txt
a fost creat folosind exemplul. Doar primele 10 rânduri ale acestui fișier sunt afișate aici pentru
concizie.
Timp (secunde) = 0.203 Număr = 1
Timp (secunde) = 0.702 Număr = 2
Timp (secunde) = 1.404 Număr = 3
Timp (secunde) = 2.368 Număr = 4
Timp (secunde) = 3.364 Număr = 5
Timp (secunde) = 3.579 Număr = 6
Timp (secunde) = 5.873 Număr = 7
Timp (secunde) = 6.410 Număr = 8
Timp (secunde) = 6.472 Număr = 9
...
În acest exemplu, există un obiect Emitter care își crește contorul conform a
procesează Poisson și apoi emite valoarea contorului ca sursă de urmărire.
Ptr emitter = CreateObject ();
Names::Add ("/Names/Emitter", emitter);
Rețineți că, deoarece nu există caractere joker în calea utilizată mai jos, a fost doar 1 fișier text
creată. Acest fișier text unic este pur și simplu numit „file-helper-example.txt”, fără suplimentar
sufixe așa cum ați vedea dacă ar exista metacaractere în cale.
// Creați ajutorul fișierului.
FileHelper fileHelper;
// Configurați fișierul care urmează să fie scris.
fileHelper.ConfigureFile ("exemplu-ajutor-fișier",
FileAggregator::FORMATED);
// Setați etichetele pentru acest fișier de ieșire formatat.
fileHelper.Set2dFormat ("Timp (Secunde) = %.3e\tCount = %.0f");
// Scrieți valorile generate de sondă. Calea pe care noi
// oferă ajutoare pentru dezambiguizarea sursei urmei.
fileHelper.WriteProbe ("ns3::Uinteger32Probe",
„/Nume/Emițător/Contor”,
"Ieșire");
domeniu și Limitări
În prezent, numai aceste Probe au fost implementate și conectate la GnuplotHelper și
către FileHelper:
· BooleanProbe
· DoubleProbe
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· PacketProbe
· ApplicationPacketProbe
· Ipv4PacketProbe
Prin urmare, aceste sonde sunt singurele TypeId-uri disponibile pentru a fi utilizate PlotProbe() și
WriteProbe().
În următoarele câteva secțiuni, vom acoperi fiecare dintre tipurile fundamentale de obiect (sondă, colector,
și Aggregator) mai detaliat și arată cum pot fi conectate împreună folosind
API de nivel inferior.
Sonde
Această secțiune detaliază funcționalitățile oferite de clasa Probe unui ns-3
simulare și oferă exemple despre cum să le codificați într-un program. Această secțiune este destinată
utilizatorii interesați să dezvolte simulări cu ns-3 instrumente și utilizarea Datelor
Cadrul de colectare, din care face parte clasa Probe, pentru a genera date de ieșire
rezultatele simulării lor.
Sondă Descriere
Un obiect Probe se presupune a fi conectat la o variabilă din simulare ale cărei valori
pe parcursul experimentului sunt relevante pentru utilizator. Sonda va înregistra ce au fost
valorile asumate de variabilă pe parcursul simulării și transmit astfel de date altuia
membru al cadrului de colectare a datelor. Deși este în afara domeniului de aplicare al acestei secțiuni să
discutați ce se întâmplă după ce Sonda își produce rezultatul, este suficient să spunem că, prin
la sfârșitul simulării, utilizatorul va avea informații detaliate despre ce valori au fost
stocate în interiorul variabilei care este sondată în timpul simulării.
De obicei, o sondă este conectată la un ns-3 sursa de urma. În acest mod, ori de câte ori
sursa de urmărire exportă o nouă valoare, Sonda consumă valoarea (și o exportă în aval
către un alt obiect prin propria sursă de urmărire).
Sonda poate fi considerată ca un fel de filtru asupra surselor de urme. Principalele motive pentru
posibila conectare la o sondă, mai degrabă decât direct la o sursă de urmărire, sunt următoarele:
· Sondele pot fi activate și oprite dinamic în timpul simulării cu apeluri către Permite()
și Dezactivați(). De exemplu, ieșirea datelor poate fi oprită în timpul
faza de încălzire de simulare.
· Sondele pot efectua operații asupra datelor pentru a extrage valori din mai complicate
structuri; de exemplu, ieșirea valorii dimensiunii pachetului dintr-un pachet ns3:: primit.
· Sondele înregistrează un nume în spațiul de nume ns3::Config (folosind Nume::Adăugați ()) astfel încât celălalt
obiectele se pot referi la ele.
· Sondele oferă o metodă statică care permite manipularea unei Sonde după nume, cum ar fi
ce se face în ns2measure [Cic06]
Stat::put („metric_mea”, ID, eșantion);
Echivalentul ns-3 al codului ns2measure de mai sus este, de ex
DoubleProbe::SetValueByPath ("/path/to/probe", exemplu);
Creare
Rețineți că un obiect al clasei de bază Probe nu poate fi creat deoarece este o bază abstractă
clasa, adică are funcții virtuale pure care nu au fost implementate. Un obiect al
tipul DoubleProbe, care este o subclasă a clasei Probe, va fi creat aici pentru a fi afișat
ce trebuie făcut.
Se declară un DoubleProbe în memoria dinamică utilizând clasa smart pointer (Ptr ). La
creați un DoubleProbe în memoria dinamică cu pointeri inteligente, trebuie doar să apelați
ns-3 metodă CreateObject():
Ptr myprobe = CreateObject ();
Declarația de mai sus creează DoubleProbes folosind valorile implicite pentru atributele sale.
Există patru atribute în clasa DoubleProbe; două în obiectul clasei de bază
DataCollectionObject și două în clasa de bază Probe:
· „Nume” (DataCollectionObject), o StringValue
· „Activat” (DataCollectionObject), o valoare BooleanValue
· „Start” (Probe), o valoare TimeValue
· „Stop” (Probe), o valoare TimeValue
Se pot seta astfel de atribute la crearea obiectului folosind următoarea metodă:
Ptr myprobe = CreateObjectWithAttributes (
„Nume”, StringValue („sonda mea”),
„Activat”, BooleanValue (fals),
„Start”, TimeValue (Secunde (100.0)),
„Stop”, TimeValue (Secunde (1000.0)));
Start și Stop sunt variabile de timp care determină intervalul de acțiune al sondei. The
Sonda va scoate date numai dacă ora curentă a simulării este în interiorul acesteia
interval. Valoarea specială a timpului de 0 secunde pentru Stop va dezactiva acest atribut (de ex
mențineți Sonda aprinsă pentru întreaga simulare). Activat este un indicator care pornește Sonda sau
off și trebuie setat la true pentru ca Sonda să exporte date. Numele este numele obiectului
în cadrul DCF.
Importarea și exportator de date
ns-3 sursele de urme sunt puternic tipizate, astfel încât mecanismele de agățare a sondelor la o urmă
sursă și pentru exportul datelor aparțin subclaselor sale. De exemplu, implicit
distribuirea de ns-3 oferă o clasă DoubleProbe care este concepută pentru a se conecta la o urmă
sursă exportând o valoare dublă. În continuare vom detalia funcționarea DoubleProbe și
apoi discutați cum pot fi definite alte clase Probe de către utilizator.
DoubleProbe Descriere
DoubleProbe se conectează la o valoare dublă ns-3 sursă de urmărire și ea însăși exportă a
diferite cu valoare dublă ns-3 sursa de urma.
Următorul cod, extras din src/stats/examples/double-probe-example.cc, arată elementele de bază
operațiunile de conectare a DoubleProbe într-o simulare, în care sondează un contor
exportat de un obiect emitor (clasa Emitter).
Ptr emitter = CreateObject ();
Names::Add ("/Names/Emitter", emitter);
...
Ptr probe1 = CreateObject ();
// Conectați sonda la contorul emițătorului
bool conectat = probe1->ConnectByObject ("Contor", emitator);
Următorul cod verifică același contor exportat de același obiect emitor. Acest
DoubleProbe, totuși, folosește o cale în spațiul de nume de configurare pentru a crea
conexiune. Rețineți că emițătorul sa înregistrat în spațiul de nume de configurare după
a fost creat; în caz contrar, ConnectByPath nu ar funcționa.
Ptr probe2 = CreateObject ();
// Notă, nicio valoare returnată nu este verificată aici
probe2->ConnectByPath ("/Names/Emitter/Counter");
Următorul DoubleProbe afișat mai jos va avea valoarea setată folosind calea în
spațiul de nume de configurare. Rețineți că de data aceasta DoubleProbe s-a înregistrat în fișierul
spațiu de nume de configurare după ce a fost creat.
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// Trebuie să-l adăugăm la baza de date de configurare
Names::Add ("/Names/Probes", probe3->GetName (), probe3);
Funcția Count() a emițătorului este acum capabilă să seteze valoarea pentru acest DoubleProbe ca
urmează:
anula
Emițător::Număr (nulat)
{
...
m_counter += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
...
}
Exemplul de mai sus arată cum codul care apelează Probe nu trebuie să aibă un explicit
referință la Sondă, dar poate direcționa setarea valorii prin spațiul de nume Config.
Aceasta este similară ca funcționalitate cu Stat::Pune metoda introdusa de hartia ns2measure
[Cic06] și permite utilizatorilor să introducă temporar instrucțiuni Probe precum printf Declarații
în cadrul existentei ns-3 modele. Rețineți că pentru a putea folosi DoubleProbe în acest sens
exemplu ca acesta, au fost necesare 2 lucruri:
1. fișierul antet al modulului statistici a fost inclus în fișierul exemplu .cc
2. exemplul a fost făcut dependent de modulul statistici din fișierul său wscript.
Lucruri similare trebuie făcute pentru a adăuga alte Sonde în alte locuri din ns-3
baza codului.
Valorile pentru DoubleProbe pot fi setate și folosind funcția DoubleProbe::SetValue(),
în timp ce valorile pentru DoubleProbe pot fi obținute folosind funcția
DoubleProbe::GetValue().
DoubleProbe exportă valori duble în sursa de urmărire „Ieșire”; un obiect din aval
poate conecta o chiuvetă de urmărire (NotifyViaProbe) la aceasta, după cum urmează:
conectat = probe1->TraceConnect ("Ieșire", probe1->GetName (), MakeCallback (&NotifyViaProbe));
Altele sonde
Pe lângă DoubleProbe, sunt disponibile și următoarele Sonde:
· Uinteger8Probe se conectează la un ns-3 sursă de urmărire care exportă un uint8_t.
· Uinteger16Probe se conectează la un ns-3 sursă de urmărire care exportă un uint16_t.
· Uinteger32Probe se conectează la un ns-3 sursă de urmărire care exportă un uint32_t.
· PacketProbe se conectează la un ns-3 sursă de urmărire care exportă un pachet.
· ApplicationPacketProbe se conectează la un ns-3 sursă de urmărire care exportă un pachet și un socket
adresa.
· Ipv4PacketProbe se conectează la un ns-3 sursă de urmărire care exportă un pachet, un obiect IPv4 și
o interfață.
Crearea nou Sondă Tipuri
Pentru a crea un nou tip de sondă, trebuie să efectuați următorii pași:
· Asigurați-vă că noua dvs. clasă Probe este derivată din clasa de bază Probe.
· Asigurați-vă că funcțiile virtuale pure pe care noua dvs. clasă Probe le moștenește de la
Clasa de bază a sondei sunt implementate.
· Găsiți o clasă Probe existentă care utilizează o sursă de urmărire care este cel mai apropiată ca tip de
tipul de sursă de urmărire pe care o va folosi Sonda dvs.
· Copiați fișierul antet al clasei Probe existente (.h) și fișierul de implementare (.cc) în două
fișiere noi cu nume care se potrivesc cu noua dvs. Sondă.
· Înlocuiți tipurile, argumentele și variabilele din fișierele copiate cu cele corespunzătoare
tastați pentru Sonda dvs.
· Faceți modificările necesare pentru a face compilarea codului și pentru a-l face să se comporte așa cum ați face dvs
ca.
Exemple
Două exemple vor fi discutate în detaliu aici:
· Exemplu de sondă dublă
· Exemplu de diagramă de pachete IPv4
dublu Sondă Exemplu
Exemplul sondei duble a fost discutat anterior. Programul exemplu poate fi găsit
in src/stats/examples/double-probe-example.cc. Pentru a rezuma ceea ce se întâmplă în acest program,
există un emițător care exportă un numărător care crește conform unui proces Poisson.
În special, sunt prezentate două moduri de a emite date:
1. printr-o variabilă urmărită agățată de o Sondă:
TracedValue m_counter; // în mod normal, acesta ar fi de tip întreg
2. printr-un contor a cărui valoare este postată la o a doua Sondă, referită prin numele său în
sistemul de configurare:
anula
Emițător::Număr (nulat)
{
NS_LOG_FUNCTION (aceasta);
NS_LOG_DEBUG ("Numărarea la " << Simulator::Now ().GetSeconds ());
m_counter += 1.0;
DoubleProbe::SetValueByPath ("/Names/StaticallyAccessedProbe", m_counter);
Simulator::Schedule (Secunde (m_var->GetValue ()), &Emitter::Count, this);
}
Să ne uităm la Sondă mai atent. Sondele își pot primi valorile într-un multiplu
moduri:
1. de către Sonda care accesează direct sursa de urmărire și conectează la aceasta o chiuvetă de urmărire
2. de către Probe accesând sursa de urmărire prin spațiul de nume de configurare și conectând a
urma chiuveta la ea
3. prin codul de apel apelând explicit la Sonda SetValue() metodă
4. prin codul de apelare în mod explicit SetValueByPath
("/path/through/Config/namespace", ...)
Se așteaptă ca primele două tehnici să fie cele mai comune. De asemenea, în exemplu, the
este afișată conectarea unei funcții de apel invers normal, așa cum se face de obicei în ns-3. Acest
Funcția de apel invers nu este asociată cu un obiect Probe. Vom numi acest caz 0) mai jos.
// Aceasta este o funcție pentru a testa conectarea unei funcții brute la sursa de urmărire
anula
NotifyViaTraceSource (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " vechi " << oldVal << " new " << newVal);
}
În primul rând, emițătorul trebuie configurat:
Ptr emitter = CreateObject ();
Names::Add ("/Names/Emitter", emitter);
// Obiectul Emitter nu este asociat cu un nod ns-3, deci
// nu va începe automat, așa că trebuie să facem asta noi înșine
Simulator::Schedule (Secunde (0.0), &Emitter::Start, emitter);
Diferitele DoubleProbes interacționează cu emițătorul din exemplu, așa cum se arată mai jos.
Cazul 0):
// Mai jos arată funcționalitatea tipică fără sondă
// (conectați o funcție sink la o sursă de urmărire)
//
conectat = emitter->TraceConnect („Contor”, „context eșantion”, MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (conectat, „Sursa de urmărire nu este conectată”);
cazul 1):
//
// Probe1 va fi conectat direct la obiectul sursă de urmărire Emitter
//
// sonda1 va fi conectată la sursa de urmărire a Emițătorului
Ptr probe1 = CreateObject ();
// numele sondei poate servi ca context în urmărire
probe1->SetName ("ObjectProbe");
// Conectați sonda la contorul emițătorului
conectat = probe1->ConnectByObject ("Contor", emitator);
NS_ASSERT_MSG (conectat, „Sursa de urmărire nu este conectată la sonda1”);
cazul 2):
//
// Probe2 va fi conectat la obiectul sursă de urmărire Emitter prin
// accesând-o după numele căii în baza de date Config
//
// Creați o altă sondă similară; aceasta se va conecta printr-o cale de configurare
Ptr probe2 = CreateObject ();
probe2->SetName ("PathProbe");
// Notă, nicio valoare returnată nu este verificată aici
probe2->ConnectByPath ("/Names/Emitter/Counter");
cazul 4) (cazul 3 nu este prezentat în acest exemplu):
//
// Probe3 va fi apelat de emițător direct prin intermediul
// metoda statică SetValueByPath().
//
Ptr probe3 = CreateObject ();
probe3->SetName ("StaticallyAccessedProbe");
// Trebuie să-l adăugăm la baza de date de configurare
Names::Add ("/Names/Probes", probe3->GetName (), probe3);
Și, în sfârșit, exemplul arată cum pot fi conectate sondele pentru a genera ieșire:
// Sonda în sine ar trebui să genereze rezultate. Contextul pe care îl oferim
// la această sondă (în acest caz, numele sondei) va ajuta la dezambiguizare
// sursa urmei
conectat = probe3->TraceConnect ("Ieșire",
„/Names/Probes/StaticallyAccessedProbe/Output”,
MakeCallback (&NotifyViaProbe));
NS_ASSERT_MSG (conectat, „Sursa de urmărire nu .. conectată la ieșirea probe3”);
Următorul apel invers este conectat la Sondă în acest exemplu pentru scopuri ilustrative;
în mod normal, Sonda ar fi conectată la un obiect Collector.
// Aceasta este o funcție pentru a testa conectarea acesteia la ieșirea sondei
anula
NotifyViaProbe (std::string context, double oldVal, double newVal)
{
NS_LOG_DEBUG ("context: " << context << " vechi " << oldVal << " new " << newVal);
}
IPv4 Pachet intrigă Exemplu
Exemplul de diagramă de pachete IPv4 se bazează pe exemplul fifth.cc din ns-3 Tutorial. Aceasta
pot fi găsite în src/stats/examples/ipv4-packet-plot-example.cc.
nodul 0 nodul 1
+----------------+ +----------------+
| ns-3 TCP | | ns-3 TCP |
+----------------+ +----------------+
| 10.1.1.1 | | 10.1.1.2 |
+----------------+ +----------------+
| punct la punct | | punct la punct |
+----------------+ +----------------+
| |
+---------------------+
Ne vom uita doar la Probe, deoarece ilustrează faptul că Probes pot, de asemenea, să despacheteze valori din
structuri (în acest caz, pachete) și raportați acele valori ca ieșiri surse de urmărire, mai degrabă
decât trecerea prin același tip de date.
Există și alte aspecte ale acestui exemplu care vor fi explicate mai târziu în documentație.
Cele două tipuri de date care sunt exportate sunt pachetul însuși (producție) și un număr de
numărul de octeți din pachet (OutputBytes).
TypeId
Ipv4PacketProbe::GetTypeId ()
{
static TypeId tid = TypeId ("ns3::Ipv4PacketProbe")
.SetParent ()
.AddConstructor ()
.AddTraceSource ("Ieșire",
„Pachetul plus obiectul său IPv4 și interfața care servesc ca ieșire pentru această sondă”,
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource ("OutputBytes",
„Numărul de octeți din pachet”,
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
return tid;
}
Când receptorul de urmărire al sondei primește un pachet, dacă Sonda este activată, va ieși
pachetul de pe el producție sursa de urmărire, dar va scoate, de asemenea, numărul de octeți pe
OutputBytes sursa de urma.
anula
Ipv4PacketProbe::TraceSink (Ptr pachet, Ptr ipv4, interfață uint4_t)
{
NS_LOG_FUNCTION (acest << pachet << ipv4 << interfață);
dacă (este activat ())
{
m_packet = pachet;
m_ipv4 = ipv4;
m_interfață = interfață;
m_output (pachet, ipv4, interfață);
uint32_t packetSizeNew = pachet->GetSize ();
m_outputBytes (m_packetSizeOld, packetSizeNew);
m_packetSizeOld = packetSizeNew;
}
}
Referinte
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, „Un cadru integrat pentru
Activarea colectării eficiente a datelor și a analizei statistice cu ns2, Workshop on
ns-2 (WNS2), Pisa, Italia, octombrie 2006.
Colecționari
Această secțiune este un substituent pentru a detalia funcționalitățile oferite de Colector
clasa la o ns-3 simulare și oferă exemple despre cum să le codificați într-un program.
Notă: Începând cu ns-3.18, Colectorii sunt încă în dezvoltare și nu sunt încă furnizați ca parte
a cadrului.
Agregatoare
Această secțiune detaliază funcționalitățile oferite de clasa Aggregator unui ns-3
simulare. Această secțiune este destinată utilizatorilor interesați să dezvolte simulări cu
ns-3 instrumente și folosind Cadrul de colectare a datelor, din care clasa Aggregator este a
parte, pentru a genera date de ieșire cu rezultatele simulării lor.
Agregator Descriere
Un obiect Aggregator ar trebui să fie conectat la una sau mai multe surse de urmărire pentru a
primiți intrare. Agregatoarele sunt punctul final al datelor colectate de rețeaua de
Sonde și colectoare în timpul simulării. Este treaba Agregatorului să preia acestea
valorile și transformați-le în formatul final de ieșire, cum ar fi fișierele text simplu,
fișiere de foi de calcul, diagrame sau baze de date.
De obicei, un agregator este conectat la unul sau mai mulți colectori. În acest fel, oricând
Sursele de urmărire ale Colectorilor exportă noi valori, Agregatorul poate procesa astfel valoarea
că poate fi utilizat în formatul final de ieșire unde valorile datelor vor locui după
simulare.
Rețineți următoarele despre agregatori:
· Agregatoarele pot fi activate și dezactivate dinamic în timpul simulării cu apeluri către
Permite() și Dezactivați(). De exemplu, agregarea datelor poate fi dezactivată în timpul
faza de încălzire a simulării, ceea ce înseamnă că acele valori nu vor fi incluse în finală
mediu de ieșire.
· Agregatorii primesc date de la colectori prin apeluri inverse. Când un Colector este asociat
la un agregator, se face un apel către TraceConnect pentru a stabili urmărirea agregatorului
metoda sink ca apel invers.
Până în prezent, au fost implementați doi agregatori:
· GnuplotAggregator
· FileAggregator
GnuplotAggregator
GnuplotAggregator produce fișiere de ieșire folosite pentru a face gnuplots.
GnuplotAggregator va crea 3 fișiere diferite la sfârșitul simulării:
· Un fișier de date gnuplot separat de spațiu
· Un fișier de control gnuplot
· Un script shell pentru a genera gnuplot
Creare
Aici va fi creat un obiect de tip GnuplotAggregator pentru a arăta ce trebuie făcut.
Se declară un GnuplotAggregator în memoria dinamică utilizând clasa smart pointer
(Ptr ). Pentru a crea un GnuplotAggregator în memoria dinamică cu pointeri inteligente, doar unul
trebuie să sune la ns-3 metodă CreateObject(). Următorul cod de la
src/stats/examples/gnuplot-aggregator-example.cc arată cum să faci asta:
string fileNameWithoutExtension = "gnuplot-aggregator";
// Creați un agregator.
Ptr agregator =
CreateObject (NumeFișierFărăExtensie);
Primul argument pentru constructor, fileNameWithoutExtension, este numele
fișiere legate de gnuplot pentru a le scrie fără extensie. Acest GnuplotAggregator va crea un
fișier de date gnuplot separat de spațiu numit „gnuplot-aggregator.dat”, un fișier de control gnuplot
numit „gnuplot-aggregator.plt”, și un script shell pentru a genera gnuplot numit +
„gnuplot-aggregator.sh”.
Gnuplotul care este creat poate avea cheia în 4 locații diferite:
· Fară cheie
· Cheie în interiorul complotului (implicit)
· Tasta deasupra parcelei
· Tasta sub intriga
Următoarele valori de enumerare a locației cheii gnuplot sunt permise pentru a specifica poziția cheii:
enumerare KeyLocation {
FARĂ CHEIE,
KEY_INSIDE,
KEY_SUPS,
KEY_BELOOW
};
Dacă s-a dorit să aibă cheia de mai jos, mai degrabă decât poziția implicită din interior, atunci
ai putea face urmatoarele.
agregator->SetKeyLocation(GnuplotAggregator::KEY_BELOW);
Exemple
Un exemplu va fi discutat în detaliu aici:
· Exemplu de agregare Gnuplot
gnuplot Agregator Exemplu
Un exemplu care exersează GnuplotAggregator poate fi găsit în
src/stats/examples/gnuplot-aggregator-example.cc.
Următorul gnuplot 2-D a fost creat folosind exemplul.
[imagine] Gnuplot 2-D Creat de gnuplot-aggregator-example.cc Exemplu..UNINDENT
Acest cod din exemplu arată cum să construiți GnuplotAggregator așa cum sa discutat
de mai sus.
void Create2dPlot ()
{
folosirea spațiului de nume std;
string fileNameWithoutExtension = "gnuplot-aggregator";
string plotTitle = "Plot agregator Gnuplot";
string plotXAxisHeading = "Timp (secunde)";
string plotYAxisHeading = "Valori duble";
string plotDatasetLabel = "Valorile datelor";
string datasetContext = "Set de date/Context/String";
// Creați un agregator.
Ptr agregator =
CreateObject (NumeFișierFărăExtensie);
Sunt setate diverse atribute GnuplotAggregator, inclusiv setul de date 2-D care va fi
complotată.
// Setați proprietățile agregatorului.
agregator->SetTerminal ("png");
agregator->SetTitle (plotTitle);
agregator->SetLegend (plotXAxisHeading, plotYAxisHeading);
// Adăugați un set de date la agregator.
agregator->Add2dDataset (datasetContext, plotDatasetLabel);
// agregatorul trebuie să fie pornit
agregator->Activare ();
Apoi, valorile 2-D sunt calculate și fiecare este scrisă individual în
GnuplotAggregator folosind Scrie2d() Funcția.
timp dublu;
valoare dublă;
// Creați setul de date 2-D.
pentru (timp = -5.0; timp <= +5.0; timp += 1.0)
{
// Calculați curba 2-D
//
// 2
// valoare = timp .
//
valoare = timp * timp;
// Adăugați acest punct la complot.
agregator->Write2d (datasetContext, time, value);
}
// Dezactivează înregistrarea datelor pentru agregator.
agregator->Dezactivare ();
}
FileAggregator
FileAggregator trimite valorile pe care le primește la un fișier.
FileAggregator poate crea 4 tipuri diferite de fișiere:
· Formatate
· Spațiu separat (prestabilit)
· Separate prin virgulă
· Tab separate
Fișierele formatate folosesc șiruri de format în stil C și funcția sprintf() pentru a le imprima
valorile din fișierul care se scrie.
Creare
Aici va fi creat un obiect de tip FileAggregator pentru a arăta ce trebuie făcut.
Se declară un FileAggregator în memoria dinamică utilizând clasa smart pointer (Ptr ).
Pentru a crea un FileAggregator în memoria dinamică cu pointeri inteligente, trebuie doar să apelați
il ns-3 metoda CreateObject. Următorul cod de la
src/stats/examples/file-aggregator-example.cc arată cum să faci asta:
string fileName = "fișier-agregator-format-values.txt";
// Creați un agregator care va avea valori formatate.
Ptr agregator =
CreateObject (fileName, FileAggregator::FORMATTED);
Primul argument pentru constructor, nume de fișier, este numele fișierului de scris; cel
al doilea argument, fileType, este tipul de fișier de scris. Acest FileAggregator va crea un
fișier numit „file-aggregator-formatted-values.txt” cu valorile sale tipărite așa cum este specificat de
fileType, adică formatat în acest caz.
Sunt permise următoarele valori de enumerare a tipului de fișier:
enumerație FileType {
FORMATAT,
SPACE_SEPARATED,
SEPARATE PRIN VIRGULĂ,
TAB_SEPARATED
};
Exemple
Un exemplu va fi discutat în detaliu aici:
· Exemplu de agregare de fișiere
Fișier Agregator Exemplu
Un exemplu care exersează FileAggregator poate fi găsit în
src/stats/examples/file-aggregator-example.cc.
Următorul fișier text cu 2 coloane de valori separate prin virgule a fost creat folosind
exemplu.
-5,25
-4,16
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9
4,16
5,25
Acest cod din exemplu arată cum să construiți FileAggregator așa cum sa discutat
de mai sus.
void CreateCommaSeparatedFile ()
{
folosirea spațiului de nume std;
string fileName = "fișier-agregator-separat prin virgulă.txt";
string datasetContext = "Set de date/Context/String";
// Creați un agregator.
Ptr agregator =
CreateObject (fileName, FileAggregator::COMMA_SEPARATED);
Atributele FileAggregator sunt setate.
// agregatorul trebuie să fie pornit
agregator->Activare ();
Apoi, valorile 2-D sunt calculate și fiecare este scrisă individual în
FileAggregator folosind Scrie2d() Funcția.
timp dublu;
valoare dublă;
// Creați setul de date 2-D.
pentru (timp = -5.0; timp <= +5.0; timp += 1.0)
{
// Calculați curba 2-D
//
// 2
// valoare = timp .
//
valoare = timp * timp;
// Adăugați acest punct la complot.
agregator->Write2d (datasetContext, time, value);
}
// Dezactivează înregistrarea datelor pentru agregator.
agregator->Dezactivare ();
}
Următorul fișier text cu 2 coloane de valori formatate a fost, de asemenea, creat folosind
exemplu.
Timp = -5.000e+00 Valoare = 25
Timp = -4.000e+00 Valoare = 16
Timp = -3.000e+00 Valoare = 9
Timp = -2.000e+00 Valoare = 4
Timp = -1.000e+00 Valoare = 1
Timp = 0.000e+00 Valoare = 0
Timp = 1.000e+00 Valoare = 1
Timp = 2.000e+00 Valoare = 4
Timp = 3.000e+00 Valoare = 9
Timp = 4.000e+00 Valoare = 16
Timp = 5.000e+00 Valoare = 25
Acest cod din exemplu arată cum să construiți FileAggregator așa cum sa discutat
de mai sus.
void CreateFormattedFile ()
{
folosirea spațiului de nume std;
string fileName = "fișier-agregator-format-values.txt";
string datasetContext = "Set de date/Context/String";
// Creați un agregator care va avea valori formatate.
Ptr agregator =
CreateObject (fileName, FileAggregator::FORMATTED);
Atributele FileAggregator sunt setate, inclusiv șirul de format în stil C de utilizat.
// Setați formatul pentru valori.
agregator->Set2dFormat ("Timp = %.3e\tValue = %.0f");
// agregatorul trebuie să fie pornit
agregator->Activare ();
Apoi, valorile 2-D sunt calculate și fiecare este scrisă individual în
FileAggregator folosind Scrie2d() Funcția.
timp dublu;
valoare dublă;
// Creați setul de date 2-D.
pentru (timp = -5.0; timp <= +5.0; timp += 1.0)
{
// Calculați curba 2-D
//
// 2
// valoare = timp .
//
valoare = timp * timp;
// Adăugați acest punct la complot.
agregator->Write2d (datasetContext, time, value);
}
// Dezactivează înregistrarea datelor pentru agregator.
agregator->Dezactivare ();
}
Adaptoare
Această secțiune detaliază funcționalitățile oferite de clasa Adaptor la un ns-3
simulare. Această secțiune este destinată utilizatorilor interesați să dezvolte simulări cu
ns-3 instrumente și folosind Cadrul de colectare a datelor, din care face parte clasa Adaptor,
pentru a genera date de ieșire cu rezultatele simulării lor.
Notă: termenul „adaptator” poate fi scris și „adaptor”; am ales ortografia aliniată
cu standardul C++.
Cleme pentru cască Descriere
Un adaptor este folosit pentru a realiza conexiuni între diferite tipuri de obiecte DCF.
Până în prezent, a fost implementat un singur adaptor:
· TimeSeriesAdaptor
Timp serie Cleme pentru cască
TimeSeriesAdaptor permite Sondelor să se conecteze direct la agregatoare fără a avea nevoie de niciuna
Colectionar intre ele.
Ambii ajutoare DCF implementate utilizează TimeSeriesAdaptors pentru a prelua probe
valori de diferite tipuri și scoateți ora curentă plus valoarea cu ambele convertite
la duble.
Rolul clasei TimeSeriesAdaptor este acela de adaptor, care ia valori brute
sondați date de diferite tipuri și emite un tuplu de două valori duble. Prima este a
marca temporală, care poate fi setată la rezoluții diferite (de exemplu, secunde, milisecunde etc.) în
viitorul, dar care este în prezent codificat în secunde. Al doilea este conversia lui a
valoare non-dublă la o valoare dublă (posibil cu pierderea preciziei).
Domeniu/Limitări
Această secțiune discută domeniul de aplicare și limitările Cadrului de colectare a datelor.
În prezent, numai aceste Sonde au fost implementate în DCF:
· BooleanProbe
· DoubleProbe
· Uinteger8Probe
· Uinteger16Probe
· Uinteger32Probe
· TimeProbe
· PacketProbe
· ApplicationPacketProbe
· Ipv4PacketProbe
În prezent, nu sunt disponibili Colectori în DCF, deși există un BasicStatsCollector
de dezvoltare.
În prezent, numai acești agregatori au fost implementați în DCF:
· GnuplotAggregator
· FileAggregator
În prezent, doar acest adaptor a fost implementat în DCF:
Adaptor pentru serii temporale.
Viitor Apartamente
Această secțiune discută lucrările viitoare care vor fi realizate în cadrul Cadrului de colectare a datelor.
Iată câteva lucruri care mai trebuie făcute:
· Conectați mai multe surse de urme în ns-3 cod pentru a obține mai multe valori din simulator.
· Implementați mai multe tipuri de Sonde decât există în prezent.
· Implementați mai mult decât un singur colector curent 2-D, BasicStatsCollector.
· Implementați mai mulți agregatori.
· Implementați mai mult decât adaptoare.
Statistic Cadru
Acest capitol evidențiază activitatea de colectare a datelor de simulare și cadrul statistic pentru
ns-3.
Codul sursă pentru cadrul statistic se află în director src/stats.
Goluri
Obiectivele principale ale acestui efort sunt următoarele:
· Oferiți funcționalitate pentru a înregistra, calcula și prezenta date și statistici pentru analiză
a simulărilor de rețea.
· Îmbunătățiți performanța simulării prin reducerea necesității de a genera log-uri de urmărire extinse
pentru a colecta date.
· Activați controlul simulării prin statistici online, de exemplu, terminarea simulărilor sau
repetarea încercărilor.
Sub-obiectivele derivate și alte caracteristici țintă includ următoarele:
· Integrarea cu sistemul de urmărire ns-3 existent ca cadru de instrumentare de bază
ale motorului intern de simulare, de exemplu stive de rețea, dispozitive de rețea și canale.
· Permite utilizatorilor să utilizeze cadrul de statistici fără a necesita utilizarea urmăririi
sistemului.
· Ajutați utilizatorii să creeze, să agreeze și să analizeze date în mai multe încercări.
· Suport pentru instrumente create de utilizator, de exemplu pentru evenimente specifice aplicației și
măsuri.
· Memorie redusă și supraîncărcare CPU atunci când pachetul nu este utilizat.
· Folosirea instrumentelor existente de analiză și rezultate cât mai mult posibil. Cadrul poate
furnizați câteva statistici de bază, dar accentul se pune pe colectarea datelor și realizarea acestora
accesibil pentru manipulare în instrumente stabilite.
· Suportul eventual pentru distribuirea de replicări independente este important, dar nu este inclus
în prima rundă de caracteristici.
Descriere
Cadrul de statistici include următoarele caracteristici:
· Cadrul de bază și doi colectori de date de bază: un contor și un min/max/avg/total
observator.
· Extensii ale acestora pentru a lucra cu ușurință cu timpi și pachete.
· Ieșire text simplu formatat pentru OMNet++.
· Utilizarea ieșirii bazei de date SQLite, un motor SQL independent, ușor și de înaltă performanță.
· Metadate obligatorii și deschise pentru descrierea și lucrul cu rulări.
· Un exemplu bazat pe experimentul noțional de examinare a proprietăților NS-3
performanță WiFi ad-hoc implicită. Acesta încorporează următoarele:
· Construcții ale unei rețele WiFi ad-hoc cu două noduri, cu nodurile o distanță parametrizată
în afară.
· Aplicații sursă și receptor de trafic UDP cu comportament ușor diferit și
cârlige de măsurare decât clasele de stoc.
· Colectarea datelor de la nucleul NS-3 prin intermediul semnalelor de urmărire existente, în special date despre
cadre transmise și primite de obiectele WiFi MAC.
· Instrumentarea aplicațiilor personalizate prin conectarea noilor semnale de urmărire la stat
cadru, precum și prin actualizări directe. Informațiile sunt înregistrate despre pachetele totale
trimis și recepționat, octeți transmisi și întârziere de la un capăt la altul.
· Un exemplu de utilizare a etichetelor de pachete pentru a urmări întârzierea de la capăt la capăt.
· Un script de control simplu care rulează o serie de încercări ale experimentului la diferite
distanțe și interogează baza de date rezultată pentru a produce un grafic folosind GNUPlot.
A face
Elementele cu prioritate ridicată includ:
· Includerea codului de statistici online, de exemplu pentru intervale de încredere eficiente în memorie.
· Prevederi în colectorii de date pentru terminarea executării, adică atunci când un prag sau
încrederea este îndeplinită.
· Colectori de date pentru înregistrarea mostrelor în timp și rezultate în diferite formate.
· Demonstrați scrierea unui lipici simplu pentru evenimente ciclice pentru a sonda în mod regulat o anumită valoare.
Fiecare dintre acestea ar trebui să se dovedească simplu de încorporat în cadrul actual.
Abordarea
Cadrul se bazează pe următoarele principii de bază:
· O încercare experimentală este condusă de o instanță a unui program de simulare, fie în
paralel sau în serie.
· Un script de control execută instanțe ale simulării, variind parametrii după cum este necesar.
· Datele sunt colectate și stocate pentru reprezentare și analiză folosind scripturi externe și
instrumentele existente.
· Măsurile din nucleul ns-3 sunt luate prin conectarea cadrului de stat la existent
urme de semnale.
· Semnalele de urmărire sau manipularea directă a cadrului pot fi folosite pentru a instrumenta personalizarea
cod de simulare.
Acele componente de bază ale cadrului și interacțiunile lor sunt descrise în
următoarea figură. [imagine]
Exemplu
Această secțiune trece prin procesul de construire a unui experiment în cadrul și
producând date pentru analiză (grafice) din acesta, demonstrând structura și API-ul de-a lungul
modul în care.
Întrebare
''Care este performanța (simulată) a dispozitivelor WiFi NetDevices ns-3 (folosind implicit
setări)? Cât de departe pot fi nodurile wireless într-o simulare înainte ca acestea să nu poată fi
comunica fiabil?''
· Ipoteza: Pe baza cunoasterii performantelor in viata reala, nodurile ar trebui sa comunice
destul de bine la cel puțin 100 m unul de celălalt. Comunicarea peste 200 de metri nu ar trebui să fie
fezabil.
Deși nu este o întrebare foarte frecventă în contexte de simulare, aceasta este o proprietate importantă
despre care dezvoltatorii de simulare ar trebui să aibă o înțelegere de bază. Este, de asemenea, un comun
studiu realizat pe hardware live.
Simulare Program
Primul lucru de făcut în implementarea acestui experiment este dezvoltarea simulării
program. Codul pentru acest exemplu poate fi găsit în exemple/statistici/wifi-example-sim.cc.
Face următorii pași principali.
· Declararea parametrilor și analizarea liniei de comandă folosind ns3::CommandLine.
distanta dubla = 50.0;
format șir ("OMNet++");
experiment cu șiruri ("wifi-distance-test");
strategie de șir („wifi-default”);
șir runID;
CommandLine cmd;
cmd.AddValue("distanță", "Distanța față de plasarea nodurilor (în metri).", distanță);
cmd.AddValue("format", "Format de utilizat pentru ieșirea datelor.", format);
cmd.AddValue("experiment", "Identificator pentru experiment.", experiment);
cmd.AddValue("strategie", "Identificator pentru strategie.", strategie);
cmd.AddValue("run", "Identifier pentru rulare.", runID);
cmd.Parse (argc, argv);
· Crearea de noduri și stive de rețea folosind ns3::NodeContainer, ns3::WiFiHelper și
ns3::InternetStackHelper.
Nodurile NodeContainer;
noduri.Creează(2);
WifiHelper wifi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer nodeDevices = wifi.Install(noduri);
InternetStackHelper internet;
internet.Instalare(noduri);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(nodeDevices);
· Poziţionarea nodurilor folosind ns3::MobilityHelper. În mod implicit, nodurile au statice
mobilitate și nu se vor mișca, dar trebuie poziționate la distanța dată. Sunt
mai multe moduri de a face acest lucru; se face aici folosind ns3::ListPositionAllocator, care atrage
posturi dintr-o listă dată.
Mobilitate MobilityHelper;
Ptr positionAlloc =
CreateObject ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, distanta, 0.0));
mobilitate.SetPositionAllocator(positionAlloc);
mobilitate.Instalare(noduri);
· Instalarea unui generator de trafic și a unei chiuvete de trafic. Stocul Aplicatii Ar putea fi
folosit, dar exemplul include obiecte personalizate în src/test/test02-apps.(cc|h). Acestea
au un comportament simplu, generând un număr dat de pachete distanțate la un interval dat.
Deoarece există doar unul din fiecare, acestea sunt instalate manual; pentru un set mai mare
ns3::ApplicationHelper ar putea fi folosită clasa. Comentat-out Config::Set modificări de linie
destinația pachetelor, setată la difuzare implicit în acest exemplu. Rețineți că
în general, WiFi poate avea performanțe diferite pentru cadrele de difuzare și unicast din cauza
diferite politici de control al ratei și retransmisie MAC.
Ptr appSource = NodeList::GetNode(0);
Ptr expeditor = CreateObject ();
appSource->AddApplication(expeditor);
expeditor->Start(secunde(1));
Ptr appSink = NodeList::GetNode(1);
Ptr receptor = CreateObject ();
appSink->AddApplication(receptor);
receptor->Start(secunde(0));
// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
// Ipv4AddressValue("192.168.0.2"));
· Configurarea datelor și statisticilor care urmează să fie colectate. Paradigma de bază este că an
ns3::DataCollector obiectul este creat pentru a deține informații despre această cursă specială, spre
la care observatori și calculatoare sunt atașați pentru a genera efectiv date. Important,
informațiile de rulare includ etichete pentru „experiment”, „strategie”, „input” și
''alerga''. Acestea sunt folosite pentru a identifica ulterior și pentru a grupa cu ușurință datele din mai multe încercări.
· Experimentul este studiul din care face parte acest proces. Aici este pe WiFi
performanta si distanta.
· Strategia este codul sau parametrii examinați în acest studiu. În acest exemplu
este remediat, dar o extensie evidentă ar fi investigarea unui alt bit WiFi
rate, fiecare dintre ele ar fi o strategie diferită.
· Intrarea este problema specială dată acestui proces. Aici este pur și simplu
distanța dintre cele două noduri.
· RunID este un identificator unic pentru această încercare cu care sunt etichetate informațiile sale
pentru identificare în analiza ulterioară. Dacă nu este dat niciun ID de rulare, programul de exemplu face
un ID de rulare (slab) folosind ora curentă.
Aceste patru piese de metadate sunt necesare, dar se pot dori mai multe. Ele pot fi adăugate
la înregistrare folosind ns3::DataCollector::AddMetadata() metodă.
Datele DataCollector;
data.DescribeRun(experiment, strategie, intrare, runID);
data.AddMetadata("autor", "tjkopena");
Observarea efectivă și calcularea se face de ns3::DataCalculator obiecte, dintre care
există mai multe tipuri diferite. Acestea sunt create de programul de simulare, atașat la
codul de raportare sau de eșantionare și apoi înregistrat la ns3::DataCollector așa vor face
fi interogați mai târziu pentru rezultatul lor. Un mecanism simplu de observare este folosirea existentei
sursele de urmărire, de exemplu pentru a instrumenta obiectele din nucleul ns-3 fără a le schimba
cod. Aici un contor este atașat direct la un semnal de urmărire în stratul WiFi MAC activat
nodul țintă.
Ptr totalRx = CreateObject ();
totalRx->SetKey("wifi-rx-frames");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
date.AddDataCalculator(totalRx);
Calculatoarele pot fi, de asemenea, manipulate direct. În acest exemplu, este creat un contor și
transmis la aplicația de absorbție a traficului pentru a fi actualizat la primirea pachetelor.
Ptr > appRx = CreateObject >();
appRx->SetKey("receptor-rx-pachete");
receptor->SetCounter(appRx);
date.AddDataCalculator(appRx);
Pentru a crește numărul, codul de procesare a pachetelor receptorului apelează apoi unul dintre
metodele de actualizare ale calculatorului.
m_calc->Actualizare();
Programul include și alte câteva exemple, folosind atât primitivul
calculatoare precum ns3::CounterCalculator iar cele adaptate pentru observarea pachetelor şi
ori. În src/test/test02-apps.(cc|h) de asemenea, creează o etichetă personalizată simplă pe care o folosește
pentru a urmări întârzierea de la capăt la capăt pentru pachetele generate, raportând rezultatele către a
ns3::TimeMinMaxAvgTotalCalculator calculator de date.
· Rularea simulării, care este foarte simplă odată construită.
Simulator::Run();
· Generarea fie OMNet++ or SQLite ieșire, în funcție de argumentele liniei de comandă. La
face asta a ns3::DataOutputInterface obiectul este creat și configurat. Tipul specific
din aceasta va determina formatul de ieșire. Acest obiect este apoi dat
ns3::DataCollector obiect pe care îl interoghează pentru a produce rezultatul.
Ptr ieșire;
dacă (format == „OMNet++”) {
NS_LOG_INFO("Se creează ieșirea datelor formatate OMNet++.");
output = CreateObject ();
} Else {
# ifdef STAT_USE_DB
NS_LOG_INFO("Se creează ieșirea datelor formatate SQLite.");
output = CreateObject ();
# endif
}
ieșire->Ieșire (date);
· Eliberarea oricărei memorie utilizate de simulare. Acest lucru ar trebui să vină la sfârșitul principalului
funcția pentru exemplu.
Simulator::Destroy();
Exploatari forestiere
Pentru a vedea ce fac programul exemplu, aplicațiile și cadrul de stat în detaliu, setați
il NS_LOG variabilă în mod corespunzător. Următoarele vor oferi rezultate abundente de la toate
Trei.
$ export NS_LOG=WiFiDistanceExperiment:WiFiDistanceApps
Rețineți că acest lucru încetinește extraordinar de simulare.
Eşantion producție
Se va adăuga compilarea și pur și simplu rularea programului de testare OMNet++ ieșire formatată, cum ar fi
urmatoarele sa date.sca.
alerga alerga-1212239121
attr experiment „test-wifi-distanță”
attr strategie „wifi-default”
intrare attr „50”
descriere attr „”
attr „autor” „tjkopena”
Număr de cadre scalare wifi-tx 30
Număr de cadre scalare wifi-rx 30
pachetele scalare sender-tx-counter 30
Receptor scalar-rx-pachete numără 30
număr scalar tx-pkt-size 30
scalar tx-pkt-size total 1920
medie scalară tx-pkt-size 64
scalar tx-pkt-size max 64
scalar tx-pkt-size min 64
număr de întârziere scalară 30
întârziere scalară totală 5884980ns
medie de întârziere scalară 196166ns
întârziere scalară max 196166ns
întârziere scalară min 196166ns
Mod de control: Scenariu
Pentru a automatiza colectarea datelor la o varietate de intrări (distanțe), un simplu Bash
scriptul este folosit pentru a executa o serie de simulări. Poate fi găsit la
exemple/statistici/wifi-example-db.sh. Scriptul este menit să fie rulat de la exemple/statistici/
director.
Scriptul parcurge un set de distanțe, colectând rezultatele într-un SQLite
Bază de date. La fiecare distanță sunt efectuate cinci încercări pentru a oferi o imagine mai bună a așteptărilor
performanţă. Întregul experiment durează doar câteva zeci de secunde pentru a rula la un nivel scăzut
mașină deoarece nu există nicio ieșire în timpul simulării și este generat puțin trafic.
#!/ Bin / sh
DISTANCES="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
TRIALS="1 2 3 4 5"
Exemplu de experiment echo WiFi
dacă [ -e data.db ]
apoi
echo Kill data.db?
citeste ANS
dacă [ "$ANS" = "da" -o "$ANS" = "y" ]
apoi
echo Ștergerea bazei de date
rm data.db
fi
fi
pentru probă în $TRIALS
do
pentru distanta in $DISTANCES
do
echo Trial $trial, distanta $distanta
./bin/test02 --format=db --distance=$distanță --run=run-$distanță-$trial
făcut
făcut
Analiză și Concluzie
Odată ce toate încercările au fost efectuate, scriptul execută o interogare SQL simplă peste
baza de date folosind SQLite program de linie de comandă. Interogarea calculează pierderea medie de pachete în
fiecare set de încercări asociate cu fiecare distanță. Nu ține cont de diferit
strategii, dar informația este prezentă în baza de date pentru a face niște extensii simple
și fă așa. Datele colectate sunt apoi transmise la GNUPlot pentru graficare.
CMD="selectați exp.input,avg(100-((rx.value*100)/tx.value)) \
din Singletons rx, Singletons tx, Experiments exp \
unde rx.run = tx.run ȘI \
rx.run = exp.run AND \
rx.name='receiver-rx-packets' ȘI \
tx.name='sender-tx-packets' \
grupați după exp.input \
ordonați după abs(exp.input) ASC;"
sqlite3 -noheader data.db „$CMD” > wifi-default.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-example.gnuplot
Scriptul GNUPlot găsit la exemple/statistici/wifi-example.gnuplot definește pur și simplu rezultatul
format și unele formatări de bază pentru grafic.
set terminal portret postscript îmbunătățit lw 2 „Helvetica” 14
mărimea set 1.0, 0.66
#------------------------------------------------- ------
stabiliți „wifi-default.eps”
#set title „Pierdere de pachete la distanță”
setați xlabel „Distanța (m) --- medie de 5 încercări pe punct”
setați intervalul x [0:200]
setați eticheta „% Pierdere pachet”
setați intervalul [0:110]
reprezentați „wifi-default.data” cu titlul liniilor „WiFi Defaults”
Sfârşit Rezultat
Graficul rezultat nu oferă nicio dovadă că performanța modelului WiFi implicit este
neapărat nerezonabil și conferă o oarecare încredere unei credincioși cel puțin simbolice față de
realitate. Mai important, această investigație simplă a fost efectuată până la capăt
folosind cadrul statistic. Succes! [imagine]
RealTime
ns-3 a fost proiectat pentru integrarea în medii de test și mașini virtuale. La
se integrează cu stive de rețea reale și emit/consumă pachete, este un planificator în timp real
necesar pentru a încerca să blocați ceasul de simulare cu ceasul hardware. Descriem aici a
componentă a acestuia: planificatorul în timp real.
Scopul programatorului în timp real este de a determina progresia ceasului de simulare
să apară sincron cu o bază de timp externă. Fără prezența lui
o bază de timp externă (ceas de perete), timpul de simulare sare instantaneu de la unul simulat
timp la următorul.
Comportament
Când utilizați un programator în timp real (prestabilit în ns-3), simulatorul avansează
timpul de simulare până la următorul eveniment programat. În timpul execuției evenimentului, timpul de simulare este
îngheţat. Cu planificatorul în timp real, comportamentul este similar din perspectiva
modele de simulare (adică, timpul de simulare este înghețat în timpul execuției evenimentului), dar între
evenimente, simulatorul va încerca să mențină ceasul de simulare aliniat cu mașina
ceas.
Când un eveniment se încheie, iar planificatorul trece la următorul eveniment, the
planificatorul compară timpul de execuție al următorului eveniment cu ceasul mașinii. Dacă următorul
evenimentul este programat pentru o perioadă viitoare, simulatorul va dormi până când este atins acel timp real
și apoi execută următorul eveniment.
Se poate întâmpla ca, din cauza procesării inerente executării evenimentelor de simulare,
că simulatorul nu poate ține pasul cu timpul real. Într-un astfel de caz, depinde de utilizator
configurație ce trebuie făcut. Sunt două ns-3 atributele care guvernează comportamentul. The
primul este ns3::RealTimeSimulatorImpl::SynchronizationMode. Cele două intrări posibile pentru
acest atribut sunt Cel mai bun efort (implicit) sau HardLimit. În modul „BestEffort”, sistemul
simulatorul va încerca doar să ajungă din urmă în timp real executând evenimente până când ajunge la a
punctul în care următorul eveniment este în viitor (în timp real), altfel simularea se termină. În
Modul BestEffort, atunci, este posibil ca simularea să consume mai mult timp decât
ora ceasului de perete. Cealaltă opțiune „HardLimit” va face ca simularea să se anuleze dacă
pragul de toleranță este depășit. Acest atribut este ns3::RealTimeSimulatorImpl::HardLimit
iar valoarea implicită este 0.1 secunde.
Un mod diferit de operare este unul în care este timpul simulat nu înghețat în timpul unui eveniment
execuţie. Acest mod de simulare în timp real a fost implementat, dar eliminat din ns-3 copac
din cauza întrebărilor dacă ar fi util. Dacă utilizatorii sunt interesați în timp real
simulator pentru care timpul de simulare nu se îngheață în timpul execuției evenimentului (adică fiecare
Sună la Simulator::Acum() returnează ora curentă a ceasului de perete, nu ora la care
evenimentul a început să se execute), vă rugăm să contactați lista de corespondență ns-developers.
Folosire
Utilizarea simulatorului în timp real este simplă, din perspectiva scripturilor.
Utilizatorii trebuie doar să seteze atributul SimulatorImplementationType în timp real
simulator, după cum urmează:
GlobalValue::Bind ("SimulatorImplementationType",
StringValue ("ns3::RealtimeSimulatorImpl"));
Există un scenariu în exemple/realtime/realtime-udp-echo.cc care are un exemplu despre cum
configurați comportamentul în timp real. Încerca:
$ ./waf --run realtime-udp-echo
Este guvernat dacă simulatorul va funcționa cu cel mai bun efort sau cu limitele stricte
prin atributele explicate în secțiunea anterioară.
Punerea în aplicare
Implementarea este cuprinsă în următoarele fișiere:
· src/core/model/realtime-simulator-impl.{cc,h}
· src/core/model/wall-clock-synchronizer.{cc,h}
Pentru a crea un planificator în timp real, la o primă aproximare pe care doriți doar să o provocați
timpul de simulare sare pentru a consuma timp real. Vă propunem să faceți acest lucru folosind o combinație de
somn- și așteaptă ocupată. Sleep-waits fac ca procesul de apelare (thread) să producă
procesor pentru o anumită perioadă de timp. Chiar dacă acest interval de timp specificat poate fi trecut
la rezoluție în nanosecunde, este de fapt convertit într-o granularitate specifică sistemului de operare. În
Linux, granularitatea se numește Jiffy. De obicei, această rezoluție este insuficientă pentru
nevoile noastre (de ordinul a zece milisecunde), așa că ne rotunjim și dormim pentru unii
număr mai mic de Jiffies. Procesul este apoi trezit după numărul specificat de
Jiffies a trecut. În acest moment, avem ceva timp rezidual de așteptat. De data asta este
în general, mai mic decât timpul minim de somn, așa că așteptăm ocupați restul
timp. Aceasta înseamnă că firul se află într-un ciclu de consum de buclă până la
sosește timpul dorit. După combinația de așteptări de somn și de ocupat, timpul real a trecut
Ceasul (de perete) ar trebui să fie în acord cu ora de simulare a evenimentului următor și cu simularea
încasează.
Ajutoare
Capitolele de mai sus v-au prezentat diverse ns-3 concepte de programare precum smart
pointeri pentru gestionarea memoriei numărate în funcție de referințe, atribute, spații de nume, apeluri inverse etc.
Utilizatorii care lucrează la acest API de nivel scăzut se pot interconecta ns-3 obiecte cu granulație fină.
Cu toate acestea, un program de simulare scris în întregime folosind API-ul de nivel scăzut ar fi destul de lung
și plictisitor de codat. Din acest motiv, a fost suprapus un așa-numit „helper API” separat
pe miez ns-3 API. Dacă ați citit ns-3 tutorial, vei fi deja familiar
cu API-ul de ajutor, deoarece acesta este API-ul căruia utilizatorii noi sunt de obicei prezentați mai întâi.
În acest capitol, introducem filozofia de proiectare a API-ului de ajutor și o contrastăm cu aceasta
API-ul de nivel scăzut. Dacă devii un utilizator intens al ns-3, probabil vă veți mișca înainte și înapoi
între aceste API-uri chiar și în același program.
API-ul de ajutor are câteva obiective:
1. restul de src / nu are dependențe de API-ul de ajutor; orice se poate face cu
API-ul de ajutor poate fi codificat și la API-ul de nivel scăzut
2. Containere: Adesea, simulările vor trebui să facă un număr de acțiuni identice pentru grupuri
a obiectelor. API-ul de ajutor folosește intens containerele de obiecte similare cărora
pot fi efectuate operaţii similare sau identice.
3. API-ul de ajutor nu este generic; nu se străduiește să maximizeze reutilizarea codului. Asa de,
constructele de programare, cum ar fi polimorfismul și șabloanele care realizează reutilizarea codului sunt
nu la fel de răspândită. De exemplu, există ajutoare CsmaNetDevice separate și
Ajutoare PointToPointNetDevice, dar nu derivă dintr-o bază comună NetDevice
clasă.
4. API-ul de ajutor funcționează de obicei cu obiecte alocate în stivă (vs. alocate în heap). Pentru
unele programe, ns-3 este posibil ca utilizatorii să nu fie nevoiți să-și facă griji cu privire la crearea unui obiect de nivel scăzut sau
manipulare Ptr; se pot descurca cu containere de obiecte și ajutoare alocate în stivă
care operează asupra lor.
API-ul de ajutor este într-adevăr totul despre crearea ns-3 programe mai ușor de scris și citit, fără
eliminând puterea interfeței de nivel scăzut. Restul acestui capitol oferă câteva
exemple de convenții de programare ale API-ului de ajutor.
Efectuarea parcelele folosind il gnuplot Clasă
Există 2 metode comune de a face un complot folosind ns-3 și gnuplot (‐
http://www.gnuplot.info):
1. Creați un fișier de control gnuplot folosind ns-3clasa Gnuplot a lui.
2. Creați un fișier de date gnuplot folosind valorile generate de ns-3.
Această secțiune este despre metoda 1, adică despre cum să faci un complot folosind ns-3Gnuplot al lui
clasă. Dacă sunteți interesat de metoda 2, consultați subsecțiunea „Un exemplu real” sub
Secțiunea „Urmărire” din ns-3 tutorial.
Crearea parcelele Utilizarea il gnuplot Clasă
Următorii pași trebuie luați pentru a crea o parcelă folosind ns-3clasa Gnuplot a lui:
1. Modificați codul astfel încât să folosească clasa Gnuplot și funcțiile acesteia.
2. Rulați codul astfel încât să creeze un fișier de control gnuplot.
3. Apelați gnuplot cu numele fișierului de control gnuplot.
4. Vizualizați fișierul grafic care a fost produs în vizualizatorul dvs. de grafică preferat.
Consultați codul din exemplele de diagrame care sunt discutate mai jos pentru detalii despre pasul 1.
An Exemplu Program acea Utilizeaza il gnuplot Clasă
Un exemplu de program care utilizează ns-3Clasa Gnuplot a lui poate fi găsită aici:
src/stats/examples/gnuplot-example.cc
Pentru a rula acest exemplu, faceți următoarele:
$ ./coaja waf
$ cd build/debug/src/stats/examples
$ ./gnuplot-exemplu
Acest lucru ar trebui să producă următoarele fișiere de control gnuplot în directorul în care se află exemplul
este localizat:
parcela-2d.plt
plot-2d-with-error-bars.plt
parcela-3d.plt
Pentru a procesa aceste fișiere de control gnuplot, faceți următoarele:
$ gnuplot plot-2d.plt
$ gnuplot plot-2d-with-error-bars.plt
$ gnuplot plot-3d.plt
Acest lucru ar trebui să producă următoarele fișiere grafice în directorul în care se află exemplul
situat:
plot-2d.png
plot-2d-with-error-bars.png
plot-3d.png
Puteți vizualiza aceste fișiere grafice în vizualizatorul dvs. de grafică preferat. Dacă ai gimp
instalat pe mașina dvs., de exemplu, puteți face acest lucru:
$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png
An Exemplu 2-Dimensional intrigă
Următorul grafic 2-Dimensional
[Image]
a fost creat folosind următorul cod din gnuplot-example.cc:
folosirea spațiului de nume std;
string fileNameWithNoExtension = "plot-2d";
string graphicsFileName = fileNameWithNoExtension + „.png”;
șir plotFileName = fileNameWithNoExtension + „.plt”;
șir plotTitle = "Plot 2-D";
string dataTitle = "Date 2-D";
// Instanțiază intriga și stabilește-i titlul.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Faceți fișierul grafic, pe care fișierul de plot îl va crea atunci când acesta
// este folosit cu Gnuplot, fie un fișier PNG.
plot.SetTerminal ("png");
// Setați etichetele pentru fiecare axă.
plot.SetLegend ("Valori X", "Valori Y");
// Setați intervalul pentru axa x.
plot.AppendExtra ("set xrange [-6:+6]");
// Instanțiați setul de date, setați-i titlul și faceți punctele să fie
// trasat împreună cu liniile de legătură.
Gnuplot2dDataset set de date;
dataset.SetTitle (dataTitle);
set de date.SetStyle (Gnuplot2dDataset::LINES_POINTS);
x dublu;
y dublu;
// Creați setul de date 2-D.
pentru (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculați curba 2-D
//
// 2
// y = x .
//
y = x * x;
// Adăugați acest punct.
set de date.Adăugați (x, y);
}
// Adăugați setul de date la grafic.
plot.AddDataset (set de date);
// Deschideți fișierul plot.
ofstream plotFile (plotFileName.c_str());
// Scrieți fișierul plot.
plot.GenerateOutput (plotFile);
// Închideți fișierul de diagramă.
plotFile.close ();
An Exemplu 2-Dimensional intrigă implementate cu Eroare Baruri
Următorul grafic 2-Dimensional cu bare de eroare în direcțiile x și y
[Image]
a fost creat folosind următorul cod din gnuplot-example.cc:
folosirea spațiului de nume std;
string fileNameWithNoExtension = "plot-2d-with-error-bars";
string graphicsFileName = fileNameWithNoExtension + „.png”;
șir plotFileName = fileNameWithNoExtension + „.plt”;
string plotTitle = "Plot 2-D cu bare de eroare";
string dataTitle = "Date 2-D cu bare de eroare";
// Instanțiază intriga și stabilește-i titlul.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Faceți fișierul grafic, pe care fișierul de plot îl va crea atunci când acesta
// este folosit cu Gnuplot, fie un fișier PNG.
plot.SetTerminal ("png");
// Setați etichetele pentru fiecare axă.
plot.SetLegend ("Valori X", "Valori Y");
// Setați intervalul pentru axa x.
plot.AppendExtra ("set xrange [-6:+6]");
// Instanțiați setul de date, setați-i titlul și faceți punctele să fie
// trasat fără linii de legătură.
Gnuplot2dDataset set de date;
dataset.SetTitle (dataTitle);
set de date.SetStyle (Gnuplot2dDataset::POINTS);
// Faceți ca setul de date să aibă bare de eroare în ambele direcții x și y.
set de date.SetErrorBars (Gnuplot2dDataset::XY);
x dublu;
dublu xErrorDelta;
y dublu;
dublu yErrorDelta;
// Creați setul de date 2-D.
pentru (x = -5.0; x <= +5.0; x += 1.0)
{
// Calculați curba 2-D
//
// 2
// y = x .
//
y = x * x;
// Faceți ca incertitudinea în direcția x să fie constantă și faceți
// incertitudinea în direcția y să fie o fracțiune constantă a
// valoarea lui y.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;
// Adăugați acest punct cu incertitudini atât în x cât și în y
// direcție.
set de date.Add (x, y, xErrorDelta, yErrorDelta);
}
// Adăugați setul de date la grafic.
plot.AddDataset (set de date);
// Deschideți fișierul plot.
ofstream plotFile (plotFileName.c_str());
// Scrieți fișierul plot.
plot.GenerateOutput (plotFile);
// Închideți fișierul de diagramă.
plotFile.close ();
An Exemplu 3-Dimensional intrigă
Următorul grafic 3-Dimensional
[Image]
a fost creat folosind următorul cod din gnuplot-example.cc:
folosirea spațiului de nume std;
string fileNameWithNoExtension = "plot-3d";
string graphicsFileName = fileNameWithNoExtension + „.png”;
șir plotFileName = fileNameWithNoExtension + „.plt”;
șir plotTitle = "Plot 3-D";
string dataTitle = "Date 3-D";
// Instanțiază intriga și stabilește-i titlul.
Gnuplot plot (graphicsFileName);
plot.SetTitle (plotTitle);
// Faceți fișierul grafic, pe care fișierul de plot îl va crea atunci când acesta
// este folosit cu Gnuplot, fie un fișier PNG.
plot.SetTerminal ("png");
// Rotiți graficul cu 30 de grade în jurul axei x și apoi rotiți
// trasează 120 de grade în jurul noii axe z.
plot.AppendExtra („setare vizualizare 30, 120, 1.0, 1.0”);
// Faceți ca zero pentru axa z să fie în planul axei x și y.
plot.AppendExtra ("set ticslevel 0");
// Setați etichetele pentru fiecare axă.
plot.AppendExtra ("set xlabel 'X Values'");
plot.AppendExtra ("set ylabel 'Y Values'");
plot.AppendExtra ("set zlabel 'Z Values'");
// Setați intervalele pentru axa x și y.
plot.AppendExtra ("set xrange [-5:+5]");
plot.AppendExtra ("set yrange [-5:+5]");
// Instanțiați setul de date, setați-i titlul și faceți punctele să fie
// legate prin linii.
Gnuplot3dDataset set de date;
dataset.SetTitle (dataTitle);
set de date.SetStyle ("cu linii");
x dublu;
y dublu;
dublu z;
// Creați setul de date 3-D.
pentru (x = -5.0; x <= +5.0; x += 1.0)
{
pentru (y = -5.0; y <= +5.0; y += 1.0)
{
// Calculați suprafața 3-D
//
// 2 2
// z = x * y .
//
z = x * x * y * y;
// Adăugați acest punct.
set de date.Adăugați (x, y, z);
}
// Linia goală este necesară la sfârșitul datelor fiecărei valori x
// puncte pentru ca grila de suprafață 3-D să funcționeze.
set de date.AddEmptyLine ();
}
// Adăugați setul de date la grafic.
plot.AddDataset (set de date);
// Deschideți fișierul plot.
ofstream plotFile (plotFileName.c_str());
// Scrieți fișierul plot.
plot.GenerateOutput (plotFile);
// Închideți fișierul de diagramă.
plotFile.close ();
Utilizarea Piton la Alerga ns-3
Legăturile Python permit codul C++ să intre ns-3 pentru a fi apelat din Python.
Acest capitol vă arată cum să creați un script Python care poate rula ns-3 precum și
procesul de creare a legăturilor Python pentru un C++ ns-3 modul.
Introducere
Scopul legărilor Python pentru ns-3 sunt duble:
1. Permiteți programatorului să scrie scripturi complete de simulare în Python (‐
http://www.python.org);
2. Prototipați noi modele (de ex. protocoale de rutare).
Deocamdată, obiectivul principal al legăturilor este primul obiectiv, dar al doilea
obiectivul va fi susținut în cele din urmă și. Legături Python pentru ns-3 sunt dezvoltate
folosind un nou instrument numit PyBindGen (http://code.google.com/p/pybindgen).
An Exemplu Piton Scenariu acea Rulează ns-3
Iată un exemplu de cod care este scris în Python și care rulează ns-3, care este scris
în C++. Acest exemplu Python poate fi găsit în exemple/tutorial/first.py:
import ns.aplicații
import ns.core
import ns.internet
import ns.network
import ns.point_to_point
ns.core.LogComponentEnable(„UdpEchoClientApplication”, ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable(„UdpEchoServerApplication”, ns.core.LOG_LEVEL_INFO)
noduri = ns.network.NodeContainer()
noduri.Creează(2)
pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute ("DataRate", ns.core.StringValue ("5Mbps"))
pointToPoint.SetChannelAttribute(„Întârziere”, ns.core.StringValue(„2ms”))
dispozitive = pointToPoint.Install(noduri)
stivă = ns.internet.InternetStackHelper()
stack.Install(noduri)
adresa = ns.internet.Ipv4AddressHelper()
adresa.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
interfețe = adresa.Assign (dispozitive);
echoServer = ns.applications.UdpEchoServerHelper(9)
serverApps = echoServer.Install(noduri.Get(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))
echoClient = ns.applications.UdpEchoClientHelper(interfețe.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute(„Interval”, ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute ("Dimensiunea pachetului", ns.core.UintegerValue(1024))
clientApps = echoClient.Install(noduri.Get(0))
clientApps.Start(ns.core.Seconds(2.0))
clientApps.Stop(ns.core.Seconds(10.0))
ns.core.Simulator.Run()
ns.core.Simulator.Destroy()
Alergare Piton Script-uri
waf conține câteva opțiuni care actualizează automat calea python pentru a găsi ns3
modul. Pentru a rula programe exemplu, există două moduri de a folosi waf pentru a avea grijă de acest lucru. unu
este de a rula o coajă de waf; de exemplu:
$ ./waf --shell
$ python exemples/wireless/mixed-wireless.py
iar celălalt este să utilizați opțiunea --pyrun pentru a waf:
$ ./waf --pyrun exemples/wireless/mixed-wireless.py
Pentru a rula un script Python sub depanatorul C:
$ ./waf --shell
$ gdb --args python exemples/wireless/mixed-wireless.py
Pentru a rula propriul script Python care apelează ns-3 și care are această cale,
/path/to/your/example/my-script.py, urmează următoarele instrucțiuni:
$ ./waf --shell
$ python /path/to/your/example/my-script.py
Rezerve
Legături Python pentru ns-3 sunt o lucrare în curs de desfășurare și unele limitări sunt cunoscute de
dezvoltatori. Unele dintre aceste limitări (nu toate) sunt enumerate aici.
Incomplet Acoperire
În primul rând, rețineți că nu 100% din API-ul este acceptat în Python. Unele dintre
motivele sunt:
1. unele dintre API-uri implică pointeri, care necesită cunoștințe despre ce fel de memorie
semantică de trecere (cui deține ce memorie). O astfel de cunoaștere nu face parte din funcție
semnături și este fie documentată, fie uneori chiar nu documentată. Adnotările sunt
necesare pentru a lega acele funcții;
2. Uneori este folosit un tip de date fundamental neobișnuit sau un construct C++ care nu este încă
susținut de PyBindGen;
3. GCC-XML nu raportează clase bazate pe șablon decât dacă sunt instanțiate.
Cele mai multe dintre API-urile lipsă pot fi împachetate, având suficient timp, răbdare și expertiză și
va fi probabil împachetat dacă sunt trimise rapoarte de eroare. Cu toate acestea, nu trimiteți un raport de eroare
spunând „legăturile sunt incomplete”, deoarece nu avem forță de muncă pentru a finaliza 100% din
legături.
Convertire Constructori
Convertire constructori nu sunt încă pe deplin acceptate de PyBindGen și acționează întotdeauna ca
constructori explici la traducerea unui API în Python. De exemplu, în C++ puteți face
acest:
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
ipAddrs.Assign (backboneDevices);
În Python, deocamdată trebuie să faci:
ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(backboneDevices)
Linie de comanda
CommandLine::AddValue() funcționează diferit în Python decât în ns-3. În Python,
primul parametru este un șir care reprezintă numele opțiunii din linia de comandă. Când opțiunea
este setat, un atribut cu același nume cu numele opțiunii este setat pe Linie de comanda()
obiect. Exemplu:
NUM_NODES_SIDE_DEFAULT = 3
cmd = ns3.CommandLine()
cmd.NumNodesSide = Nici unul
cmd.AddValue("NumNodesSide", "Numărul de noduri din partea rețelei (numărul total de noduri va fi acest număr la pătrat)")
cmd.Parse(argv)
[...]
dacă cmd.NumNodesSide este None:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
altceva:
num_nodes_side = int(cmd.NumNodesSide)
calc
Urmărirea bazată pe apel invers nu este încă acceptată corespunzător pentru Python, ca nou ns-3 API-ul trebuie
să fie furnizate pentru ca aceasta să fie susținută.
Scrierea fișierelor Pcap este acceptată prin API-ul normal.
Urmărirea Ascii este acceptată de atunci ns-3.4 prin API-ul C++ normal tradus în Python.
Cu toate acestea, urmărirea ascii necesită crearea unui obiect ostream pentru a trece în ascii
metode de urmărire. În Python, C++ std::ofstream a fost împachetat minim pentru a permite
acest. De exemplu:
ascii = ns3.ofstream("wifi-ap.tr") # creați fișierul
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # închide fișierul
Există o avertizare: nu trebuie să permiteți ca obiectul fișierului să fie colectat gunoi în timp ce ns-3
încă îl folosește. Asta înseamnă că variabilei „ascii” de mai sus nu trebuie lăsate să dispară
în afara domeniului de aplicare, altfel programul se va prăbuși.
Cygwin limitare
Legăturile Python nu funcționează pe Cygwin. Acest lucru se datorează unei erori gccxml.
S-ar putea să scapi de asta prin rescanarea definițiilor API din interiorul cygwin
mediu (./waf --python-scan). Cu toate acestea, cea mai probabilă soluție va trebui probabil
fie că dezactivăm legăturile Python în CygWin.
Dacă vă pasă cu adevărat de legăturile Python pe Windows, încercați să construiți cu mingw și native
python în schimb. Sau, pentru a construi fără legături python, dezactivați legăturile python în
etapa de configurare:
$ ./waf configure --disable-python
De lucru implementate cu Piton Bindings
În prezent, există două tipuri de legături Python în ns-3:
1. Legăturile monolitice conțin definiții API pentru toate modulele și pot fi găsite în
un singur director, legături/python.
2. Legăturile modulare conțin definiții API pentru un singur modul și pot fi găsite în fiecare
modulelor legături director.
Piton Bindings Workflow
Procesul prin care sunt gestionate legările Python este următorul:
1. Periodic, un dezvoltator folosește un GCC-XML (http://www.gccxml.org) scanare bazată pe API
script, care salvează definiția API scanată ca bindings/python/ns3_module_*.py fișiere
sau ca fișiere Python în fiecare modul legături director. Aceste fișiere sunt păstrate sub
controlul versiunilor în principal ns-3 repertoriu;
2. Alți dezvoltatori clonează depozitul și utilizează definițiile API deja scanate;
3. La configurare ns-3, pybindgen va fi descărcat automat dacă nu este deja
instalat. Eliberată ns-3 tarballs va livra o copie a pybindgen.
Dacă ceva nu merge bine la compilarea legăturilor Python și doriți doar să le ignorați
și continuați cu C++, puteți dezactiva Python cu:
$ ./waf --disable-python
Instrucțiuni pentru Manipularea Nou Fişiere or schimbată API
Deci ai schimbat existenta ns-3 API-urile și legăturile Python nu se mai compilează? Do
nu disperați, puteți rescana legăturile pentru a crea legături noi care reflectă modificările
la ns-3 API-ul.
În funcție de dacă utilizați legături monolitice sau modulare, consultați discuțiile de mai jos pentru
aflați cum să rescanați legăturile dvs. Python.
Monolitic Piton Bindings
Scanarea il Monolitic Piton Bindings
Pentru a scana legările Python monolitice, faceți următoarele:
$ ./waf --python-scan
Organizație of il Monolitic Piton Bindings
Definițiile monolitice ale API-ului Python sunt organizate după cum urmează. Pentru fiecare ns-3 modul
, fișierul bindings/python/ns3_module_ .py își descrie API-ul. Fiecare dintre acestea
fișierele au 3 funcții de nivel superior:
1. Def register_types(modul)(): această funcție se ocupă de înregistrarea de noi tipuri (de ex
clase C++, enumerari) care sunt definite în modul;
2. Def register_methods(modul)(): această funcție apelează, pentru fiecare clasă , o alta
funcția register_methods_Ns3 (modul). Aceste din urmă funcții adaugă metoda
definiții pentru fiecare clasă;
3. Def funcții_registru(modul)(): această funcție se înregistrează ns-3 funcții care îi aparțin
acel modul.
Modular Piton Bindings
Descriere
De la ns 3.11, se adaugă legăturile modulare, în paralel cu vechiul monolitic
legături.
Noile legături python sunt generate într-un spațiu de nume „ns”, în loc de „ns3” pentru vechiul
legături. Exemplu:
de la ns.network import Node
n1 = Nod()
Cu legături Python modulare:
1. Există un modul separat de extensie Python pentru fiecare ns-3 modul;
2. Scanarea definițiilor API (apidefs) se face pe modul per ns;
3. Fișierele apidefs ale fiecărui modul sunt stocate într-un subdirector „bindings” al modulului
director;
Scanarea il Modular Piton Bindings
Pentru a scana legările Python modulare pentru modulul de bază, de exemplu, faceți următoarele:
$ ./waf --apiscan=core
Pentru a scana legările Python modulare pentru toate modulele, procedați în felul următor:
$ ./waf --apiscan=toate
Crearea a Nou Module
Dacă adăugați un nou modul, legările Python vor continua să se compileze, dar nu
acoperi noul modul.
Pentru a acoperi un nou modul, trebuie să creați un bindings/python/ns3_module_ .py fişier,
similar cu ceea ce este descris în secțiunile anterioare și înregistrați-l în variabilă
LOCAL_MODULES() in bindings/python/ns3modulegen.py
Adăugare Modular Bindings La A Existent Module
Pentru a adăuga suport pentru legături modulare la un existent ns-3 modul, adăugați pur și simplu următoarele
linie la funcția wscript build():
bld.ns3_python_bindings()
Organizație of il Modular Piton Bindings
src/ /legaturi directorul poate conține următoarele fișiere, unele dintre ele
opțional:
· callbacks_list.py: acesta este un fișier scanat, NU ATINGEȚI. Conține o listă de
Instanțe de șablon de apel invers<...> găsite în antetele scanate;
· modulegen__gcc_LP64.py: acesta este un fișier scanat, NU ATINGEȚI. Definiții API scanate
pentru arhitectura GCC, LP64 (64 de biți)
· modulegen__gcc_ILP32.py: acesta este un fișier scanat, NU ATINGEȚI. Definiții API scanate
pentru arhitectura GCC, ILP32 (32 de biți)
· modulegen_customizations.py: puteți adăuga opțional acest fișier pentru a personaliza fișierul
generarea codului pybindgen
· antet de scanare.h: puteți adăuga opțional acest fișier pentru a personaliza ce fișier de antet este scanat
pentru modul. Practic, acest fișier este scanat în loc de ns3/ -modul.h.
De obicei, prima instrucțiune este #include „ns3/ -module.h", plus unele altele
lucruri pentru a forța instanțierea șablonului;
· module_helpers.cc: puteți adăuga fișiere suplimentare, cum ar fi acesta, pentru a fi conectate la python
modul de extensie, dar trebuie să fie înregistrate în wscript. Uita-te la
src/core/wscript pentru un exemplu despre cum să faceți acest lucru;
· .py: dacă acest fișier există, devine modulul python „frontend” pentru ns3
modul, iar modulul de extensie (fișierul .so) devine _ .deci în loc de .asa de.
The Fișierul .py trebuie să importe toate simbolurile din modulul _ (acesta este mai mult
complicat decât pare, consultați src/core/bindings/core.py pentru un exemplu), apoi puteți adăuga
câteva definiții suplimentare pur-python.
Mai Mult Informații pentru Dezvoltatorii
Dacă sunteți dezvoltator și aveți nevoie de mai multe informații despre ns-3Legăturile lui Python, vă rugăm să vedeți
Piton Bindings Wiki pagină.
Teste
Descriere
Acest document se referă la testarea și validarea ns-3 Software.
Acest document prevede
· cunoștințe despre terminologie și testarea software-ului (Capitolul 2);
· o descriere a cadrului de testare ns-3 (Capitolul 3);
· un ghid pentru dezvoltatorii de modele sau contribuitorii de modele noi despre cum să scrieți teste (Cap
4);
Pe scurt, primele trei capitole ar trebui citite de dezvoltatorii ns și de colaboratorii care
trebuie să înțeleagă cum să contribui cu codul de testare și programele validate și restul
a documentului oferă spațiu pentru ca oamenii să raporteze despre ce aspecte ale modelelor selectate
au fost validate.
Context
Acest capitol Mai be omit by cititori familiar implementate cu il Elementele de bază of software-ul de testare.
Scrierea unui software fără defecte este o propunere dificilă. Există multe dimensiuni ale
problemă și există multă confuzie cu privire la ce se înțelege prin termeni diferiți în
contexte diferite. Am considerat că merită să petrecem puțin timp revizuind
subiect şi definirea unor termeni.
Testarea software-ului poate fi definită vag ca procesul de executare a unui program cu
intentia de a gasi erori. Când cineva intră într-o discuție cu privire la testarea software-ului, aceasta
devine rapid evident că există multe mentalități distincte cu care se poate
aborda subiectul.
De exemplu, s-ar putea împărți procesul în categorii funcționale largi, cum ar fi
„testare de corectitudine”, „testare de performanță”, „testare de robustețe” și „securitate”
testare.'' Un alt mod de a privi problema este prin ciclul de viață: ''testarea cerințelor''
„testare de proiectare”, „testare de acceptare” și „testare de întreținere”. Încă o altă viziune
este de domeniul de aplicare al sistemului testat. În acest caz se poate vorbi de „testare unitară”,
„testarea componentelor”, „testarea integrării” și „testarea sistemului”. Acești termeni sunt
de asemenea, nu este standardizat în niciun fel, deci „testare de întreținere” și „regresie”.
testing'' poate fi auzit interschimbabil. În plus, acești termeni sunt adesea folosiți greșit.
Există, de asemenea, o serie de abordări filozofice diferite ale testării software-ului. Pentru
De exemplu, unele organizații susțin scrierea de programe de testare înainte de implementare efectivă
software-ul dorit, producând „dezvoltare bazată pe teste”. Unele organizații susțin
testarea din perspectiva clientului cât mai curând posibil, urmând o paralelă cu
proces de dezvoltare agilă: „testați devreme și testați des”. Acest lucru este uneori numit
„testare agile”. Se pare că există cel puțin o abordare a testării pentru fiecare
metodologia de dezvoltare.
ns-3 proiectul nu are rolul de a susține niciunul dintre aceste procese, dar
proiectul în ansamblu are cerințe care ajută la informarea procesului de testare.
Ca toate produsele software majore, ns-3 are o serie de calităţi care trebuie să fie prezente pentru
produsul să aibă succes. Din perspectiva testării, unele dintre aceste calități care trebuie să fie
abordate sunt că ns-3 trebuie să fie „corect”, „robust”, „performant” și
„Menținabil”. În mod ideal, ar trebui să existe valori pentru fiecare dintre aceste dimensiuni care sunt
verificat prin teste pentru a identifica când produsul nu își îndeplinește așteptările /
cerințe.
Corectitudine
Scopul esențial al testării este de a determina dacă o bucată de software se comportă
''corect.'' Pentru ns-3 asta înseamnă că dacă simulăm ceva, simularea ar trebui
reprezintă fidel o entitate fizică sau un proces cu o precizie specificată și
precizie.
Se pare că există două perspective din care se poate vedea corectitudinea.
Verificarea faptului că un anumit model este implementat conform specificațiilor sale este
numit generic verificare. Procesul de a decide că modelul este corect pentru
utilizarea sa destinată se numește generic validare.
Validare și Verificare
Un model de calculator este o reprezentare matematică sau logică a ceva. Poate
reprezintă un vehicul, un elefant (vezi David a lui Harel vorbi despre modelare an elefant at
SIMUTools 2009, sau o placă de rețea. Modelele pot reprezenta, de asemenea, procese precum globale
încălzire, fluxul de trafic pe autostradă sau o specificare a unui protocol de rețea. Modelele pot fi
reprezentări complet fidele ale specificației unui proces logic, dar acestea
neapărat nu poate simula complet un obiect sau un proces fizic. În cele mai multe cazuri, a
Numărul de simplificări sunt făcute modelului pentru a face simularea computațional
maleabil.
Fiecare model are un ţintă sistem că încearcă să simuleze. Primul pas în
crearea unui model de simulare este de a identifica acest sistem țintă și nivelul de detaliu și
acuratețea pe care se dorește să o reproducă simularea. În cazul unui proces logic,
sistemul țintă poate fi identificat ca „TCP așa cum este definit de RFC 793”. În acest caz, acesta
va fi probabil de dorit să se creeze un model care să reproducă complet și fidel RFC
793. În cazul unui proces fizic acest lucru nu va fi posibil. Dacă, de exemplu, tu
ați dori să simuleze o placă de rețea fără fir, puteți determina că aveți nevoie de „un
implementare precisă la nivel MAC a specificației 802.11 și [...] o implementare nu atât de lentă
Model la nivel PHY al specificației 802.11a.''
Odată realizat acest lucru, se poate dezvolta un model abstract al sistemului țintă. Aceasta este
de obicei, un exercițiu de gestionare a compromisurilor între complexitate, cerințele de resurse
si acuratete. Procesul de dezvoltare a unui model abstract a fost numit model
calificare in literatura. În cazul unui protocol TCP, acest proces are ca rezultat a
design pentru o colecție de obiecte, interacțiuni și comportamente care se vor implementa pe deplin
RFC 793 in ns-3. În cazul plăcii fără fir, acest proces are ca rezultat un număr de
compromisuri pentru a permite stratul fizic să fie simulat și proiectarea unui dispozitiv de rețea
și canal pentru ns-3, împreună cu obiectele, interacțiunile și comportamentele dorite.
Acest model abstract este apoi dezvoltat într-un ns-3 model care implementează abstractul
model ca program de calculator. Procesul de a face implementarea să fie de acord cu
se numește model abstract model verificare in literatura.
Procesul de până acum este în buclă deschisă. Ceea ce rămâne este să se stabilească că un dat ns-3
modelul are o oarecare conexiune cu o anumită realitate -- că un model este o reprezentare exactă a acesteia
un sistem real, fie că este un proces logic sau o entitate fizică.
Dacă cineva va folosi un model de simulare pentru a încerca să prezică cum merge un sistem real
pentru a te comporta, trebuie să existe vreun motiv pentru a crede rezultatele tale -- adică, poate cineva să aibă încredere în asta
o inferență făcută din model se traduce într-o predicție corectă pentru sistemul real.
Procesul de a obține comportamentul modelului ns-3 să fie de acord cu sistemul țintă dorit
comportamentul definit de procesul de calificare a modelului este numit model validare în
literatură. În cazul unei implementări TCP, poate doriți să comparați comportamentul
modelul dvs. TCP ns-3 la o implementare de referință pentru a vă valida modelul. În
în cazul unei simulări de strat fizic fără fir, poate doriți să comparați comportamentul
modelul dvs. la cel al hardware-ului real într-un cadru controlat,
ns-3 mediul de testare oferă instrumente care să permită atât validarea modelului, cât și
testare și încurajează publicarea rezultatelor validării.
Robusteţe
Robustitatea este calitatea de a fi capabil să reziste la solicitări sau schimbări în mediu,
intrări sau calcule, etc. Un sistem sau un design este „robust” dacă poate face față acestui lucru
modificări cu pierderi minime de funcționalitate.
Acest tip de testare se face de obicei cu un accent special. De exemplu, sistemul ca
un întreg poate fi rulat pe mai multe configurații diferite de sistem pentru a demonstra că poate
functioneaza corect intr-un numar mare de medii.
Sistemul poate fi, de asemenea, stresat prin funcționarea aproape sau peste capacitate prin generare
sau simularea epuizării resurselor de diferite tipuri. Acest gen de testare se numește
''testare stresanta.''
Sistemul și componentele sale pot fi expuse la așa-numitele „teste curate” care demonstrează
un rezultat pozitiv -- adică sistemul funcționează corect ca răspuns la un mare
variație a configurațiilor așteptate.
Sistemul și componentele sale pot fi, de asemenea, expuse la „testări murdare” care furnizează intrări
în afara intervalului așteptat. De exemplu, dacă un modul așteaptă un șir terminat cu zero
reprezentarea unui număr întreg, un test murdar ar putea oferi un șir neterminat de aleatoriu
caractere pentru a verifica dacă sistemul nu se blochează ca urmare a acestei intrări neașteptate.
Din păcate, detectarea unei astfel de intrări „murdare” și luarea de măsuri preventive pentru a asigura
sistemul nu eșuează catastrofal poate necesita o sumă uriașă de dezvoltare.
Pentru a reduce timpul de dezvoltare, a fost luată o decizie la începutul proiectului
minimizați cantitatea de validare a parametrilor și de gestionare a erorilor în ns-3 baza de cod. Pentru
din acest motiv, nu petrecem mult timp testării murdare -- ar descoperi doar
rezultatele deciziei de proiectare pe care știm că am luat-o.
Vrem să demonstrăm asta ns-3 software-ul funcționează într-un anumit set de condiții. Noi
împrumutați câteva definiții pentru a restrânge puțin acest lucru. The domeniu of aplicabilitate is
un set de condiții prescrise pentru care modelul a fost testat, comparativ cu
realitatea în măsura în care este posibil și considerat adecvat pentru utilizare. The gamă of precizie este o
acord între modelul computerizat și realitate în cadrul unui domeniu de aplicabilitate.
ns-3 Mediul de testare oferă instrumente pentru a permite configurarea și rularea testului
medii pe mai multe sisteme (buildbot) și oferă cursuri pentru a încuraja curățarea
teste pentru a verifica funcționarea sistemului peste „domeniul de aplicabilitate” așteptat
și „gamă de precizie”.
eficient
Bine, „performant” nu este un cuvânt adevărat în engleză. Este, totuși, un neologism foarte concis
care este destul de des folosit pentru a descrie ceea ce dorim ns-3 a fi: puternic și suficient de rapid pentru a
fa-ti treaba.
Acesta este într-adevăr despre subiectul larg al testării performanței software-ului. Una dintre cheie
ceea ce se face este să compare două sisteme pentru a găsi care are performanțe mai bune (cf
repere). Aceasta este folosită pentru a demonstra că, de exemplu, ns-3 poate efectua un tip de bază
de simulare cel puțin la fel de rapid ca un instrument concurent sau poate fi folosit pentru a identifica părți ale
sistemul care funcționează prost.
În ns-3 cadru de testare, oferim suport pentru sincronizarea diferitelor tipuri de teste.
mentenabilitate
Un produs software trebuie să poată fi întreținut. Aceasta este, din nou, o afirmație foarte largă, dar a
cadrul de testare poate ajuta cu sarcina. Odată ce un model a fost dezvoltat, validat și
verificat, putem executa în mod repetat suita de teste pentru a asigura întregul sistem
că rămâne valabil și verificat pe toată durata de viață.
Când o caracteristică încetează să funcționeze așa cum a fost prevăzut după un fel de modificare a sistemului, este
integrat, se numește generic a regres. Inițial termenul de regresie
s-a referit la o modificare care a făcut ca o eroare remediată anterior să reapară, dar termenul are
a evoluat pentru a descrie orice fel de schimbare care rupe funcționalitatea existentă. Există multe
tipuri de regresii care pot apărea în practică.
A local regres este unul în care o modificare afectează direct componenta modificată. Pentru
de exemplu, dacă o componentă este modificată pentru a aloca și elibera memorie, dar pointerii învechiți sunt
folosit, componenta în sine eșuează.
A la distanta regres este unul în care o modificare a unei componente întrerupe funcționalitatea
altă componentă. Acest lucru reflectă o încălcare implicită, dar posibil nerecunoscută
contract între componente.
An demascat regres este unul care creează o situație în care o eroare existentă anterior
care nu a avut nici un efect este expus brusc în sistem. Acest lucru poate fi la fel de simplu ca și exercițiul
o cale de cod pentru prima dată.
A performanță regres este unul care determină cerințele de performanță ale sistemului
fi încălcat. De exemplu, efectuarea unor lucrări într-o funcție de nivel scăzut care poate fi repetată
de multe ori poate face brusc sistemul inutilizabil din anumite perspective.
ns-3 cadrul de testare oferă instrumente pentru automatizarea procesului utilizat pentru validarea și
verificați codul în suitele de testare nocturne pentru a ajuta la identificarea rapidă a posibilelor regresii.
Testarea cadru
ns-3 constă dintr-un motor de bază de simulare, un set de modele, exemple de programe și teste.
De-a lungul timpului, noi contribuitori contribuie cu modele, teste și exemple. Un program de testare Python
test.py servește ca manager de execuție a testelor; test.py poate rula cod de testare și exemple pentru
caută regresii, poate scoate rezultatele într-un număr de forme și poate gestiona codul
instrumente de analiză a acoperirii. Pe deasupra, stratificam Buildbots care sunt construite automatizate
roboți care efectuează teste de robustețe prin rularea cadrului de testare pe diferite sisteme
și cu diferite opțiuni de configurare.
BuildBots
La cel mai înalt nivel de testare ns-3 se află buildboții (build robots). Daca esti
nefamiliarizat cu acest sistem, priviți http://djmitche.github.com/buildbot/docs/0.7.11/.
Acesta este un sistem automatizat open-source care permite ns-3 pentru a fi reconstruit și testat fiecare
când ceva s-a schimbat. Rulând buildboții pe un număr de sisteme diferite, noi
poate asigura că ns-3 se construiește și se execută corect pe toate sistemele sale suportate.
De obicei, utilizatorii (și dezvoltatorii) nu vor interacționa cu sistemul buildbot decât pentru
citiți mesajele sale cu privire la rezultatele testelor. Dacă este detectată o eroare într-unul dintre
joburi automate de construire și testare, buildbot-ul va trimite un e-mail către ns-dezvoltatori
listă de email-uri. Acest e-mail va arăta ceva asemănător
În adresa URL a detaliilor complete afișată în e-mail, se poate căuta cuvântul cheie a eșuat și
selectați stdio link pentru pasul corespunzător pentru a vedea motivul eșecului.
Buildbot-ul își va face treaba în liniște dacă nu există erori, iar sistemul va suferi
construiți și testați cicluri în fiecare zi pentru a verifica dacă totul este bine.
Test.py
Buildboții folosesc un program Python, test.py, care este responsabil pentru rularea tuturor
teste și colectarea rapoartelor rezultate într-o formă care poate fi citită de om. Acest program este
disponibil și pentru utilizare de către utilizatori și dezvoltatori.
test.py este foarte flexibil, permițând utilizatorului să specifice numărul și tipul de teste la care se face
alerga; precum și cantitatea și tipul de ieșire de generat.
Înainte de a alerga test.py, asigurați-vă că exemplele și testele lui ns3 au fost realizate
următoarele
$ ./waf configure --enable-examples --enable-tests
$ ./waf
În mod implicit, test.py va rula toate testele disponibile și va raporta starea înapoi într-un mod foarte concis
formă. Rularea comenzii
$ ./test.py
va avea ca rezultat un număr de TRECERE, FAIL, CRASH or OCOLIRE indicatii urmate de felul de
testul care a fost rulat și numele afișat.
Waf: se introduce în directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Ieșim din directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„build” finalizat cu succes (0.939s)
ESCĂ: Modele-testSuite ns3-wifi-propagation-loss
PASS: TestSuite obiect-nume-serviciu
PASS: TestSuite pcap-file-object
PASS: TestSuite ns3-tcp-cwnd
...
PASS: TestSuite ns3-tcp-interoperability
PASS: Exemplu csma-broadcast
PASS: Exemplu csma-multicast
Acest mod este destinat să fie utilizat de utilizatorii care sunt interesați să determine dacă au
distribuția funcționează corect și de către dezvoltatori care sunt interesați să determine dacă
modificările pe care le-au făcut au cauzat regresii.
Există o serie de opțiuni disponibile pentru a controla comportamentul test.py. dacă fugi
test.py --Ajutor ar trebui să vedeți un rezumat al comenzii ca:
Utilizare: test.py [opțiuni]
Opțiuni:
-h, --help afișează acest mesaj de ajutor și ieși
-b BUILDATH, --buildpath=BUILDPATH
specificați calea în care a fost construit ns-3 (implicit la
directorul de construire pentru varianta curentă)
-c KIND, --constrain=KIND
constrângeți testatorul după tipul de test
-e EXEMPLU, --example=EXEMPLU
specificați un singur exemplu de rulat (nicio cale relativă nu este
Necesar)
-g, --grind rulează suitele de testare și exemple folosind valgrind
-k, --kinds imprimă tipurile de teste disponibile
-l, --list imprimă lista de teste cunoscute
-m, --multiple raportează mai multe eșecuri din suitele de testare și test
cazuri
-n, --nowaf nu rulați waf înainte de a începe testarea
-p PYEXAMPLE, --pyexample=PYEXAMPLE
specificați un singur exemplu python de rulat (cu relativ
cale)
-r, --retain păstrează toate fișierele temporare (care sunt în mod normal
sters)
-s TEST-SUITE, --suite=TEST-SUITE
specificați o singură suită de teste de rulat
-t TEXT-FIȘIER, --text=TEXT-FIȘIER
scrieți rezultatele testelor detaliate în TEXT-FILE.txt
-v, --verbose imprimă progresul și mesajele informaționale
-w HTML-FILE, --web=HTML-FILE, --html=HTML-FILE
scrieți rezultatele testelor detaliate în HTML-FILE.html
-x XML-FIȘIER, --xml=XML-FIȘIER
scrieți rezultatele testelor detaliate în XML-FILE.xml
Dacă se specifică un stil de ieșire opțional, se pot genera descrieri detaliate ale
teste și stare. Stilurile disponibile sunt a) Sport and Nutrition Awareness Day in Manasia Around XNUMX people from the rural commune Manasia have participated in a sports and healthy nutrition oriented activity in one of the community’s sports ready yards. This activity was meant to gather, mainly, middle-aged people from a Romanian rural community and teach them about the benefits that sports have on both their mental and physical health and on how sporting activities can be used to bring people from a community closer together. Three trainers were made available for this event, so that the participants would get the best possible experience physically and so that they could have the best access possible to correct information and good sports/nutrition practices. b) Sports Awareness Day in Poiana Țapului A group of young participants have taken part in sporting activities meant to teach them about sporting conduct, fairplay, and safe physical activities. The day culminated with a football match. și HTML. Buildboții vor selecta HTML
opțiunea de a genera rapoarte de testare HTML pentru versiunile de noapte folosind
$ ./test.py --html=nightly.html
În acest caz, un fișier HTML numit „nightly.html” va fi creat cu un rezumat destul de frumos
a testării efectuate. Un format „lizibil” este disponibil pentru utilizatorii interesați de
Detalii.
$ ./test.py --text=results.txt
În exemplul de mai sus, suita de teste verifică ns-3 pierderea de propagare a dispozitivului fără fir
modelele au esuat. În mod implicit, nu sunt furnizate alte informații.
Pentru a explora în continuare eșecul, test.py permite specificarea unei singure suite de teste.
Rularea comenzii
$ ./test.py --suite=ns3-wifi-propagation-loss-models
sau echivalent
$ ./test.py -s ns3-wifi-propagation-loss-models
are ca rezultat rularea acelei suită de teste unică.
ESCĂ: Modele-testSuite ns3-wifi-propagation-loss
Pentru a găsi informații detaliate cu privire la defecțiune, trebuie să specificați tipul de ieșire
dorit. De exemplu, majoritatea oamenilor vor fi probabil interesați de un fișier text:
$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt
Acest lucru va duce la rularea acelei suită de teste unice cu starea testului scrisă în
fișierul „results.txt”.
Ar trebui să găsiți ceva similar cu următorul fișier în acel fișier
FAIL: Test Suite ''ns3-wifi-propagation-loss-models'' (real 0.02 user 0.01 system 0.00)
PASS: Caz de testare „Verifică... Friis... model...” (real 0.01 utilizator 0.00 sistem 0.00)
FAIL: Caz de testare „Verificați ... Log Distance ... model” (real 0.01 utilizator 0.01 sistem 0.00)
Detalii:
Mesaj: S-a primit o valoare SNR neașteptată
Condiție: [descriere lungă a ceea ce a eșuat de fapt]
Real: 176.395
Limită: 176.407 +- 0.0005
Fișier: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Linie: 360
Observați că suita de teste este compusă din două cazuri de testare. Primul caz de testare a verificat
Modelul de pierdere de propagare Friis și a trecut. Al doilea caz de testare nu a reușit verificarea jurnalului
Model de propagare la distanță. În acest caz, a fost găsit un SNR de 176.395, iar testul
se aștepta o valoare de 176.407 corectă cu trei zecimale. Fișierul care a implementat
testul eșuat este listat, precum și linia de cod care a declanșat eșecul.
Dacă doriți, ați putea la fel de ușor să scrieți un fișier HTML folosind --html opțiune
așa cum este descris mai sus.
De obicei, un utilizator va rula toate testele cel puțin o dată după descărcare ns-3 a se asigura ca
mediul său a fost construit corect și generează rezultate corecte
conform suitelor de testare. De obicei, dezvoltatorii vor rula suitele de testare înainte și
după ce au făcut o modificare pentru a se asigura că nu au introdus o regresie cu lor
schimbări. În acest caz, dezvoltatorii nu vor dori să ruleze toate testele, ci doar un subset. Pentru
De exemplu, dezvoltatorul ar putea dori doar să ruleze testele unitare periodic în timpul realizării
modificări la un depozit. În acest caz, test.py i se poate spune să constrângă tipurile de
testele fiind efectuate la o anumită clasă de teste. Următoarea comandă va avea ca rezultat numai
testele unitare care se execută:
$ ./test.py --constrain=unit
În mod similar, următoarea comandă va avea ca rezultat rularea doar a exemplelor de teste de fum:
$ ./test.py --constrain=unit
Pentru a vedea o listă rapidă a tipurilor de constrângeri legale, puteți solicita ca acestea să fie listate.
Următoarea comandă
$ ./test.py --kinds
va avea ca rezultat afișarea următoarei liste:
Waf: se introduce în directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Ieșim din directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„build” s-a încheiat cu succes (0.939s) Waf: se introduce în directorul „/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build”
bvt: Teste de verificare a construcției (pentru a vedea dacă construcția a fost finalizată cu succes)
de bază: rulați toate testele bazate pe TestSuite (excludeți exemplele)
exemplu: Exemple (pentru a vedea dacă programele exemplu rulează cu succes)
performanță: teste de performanță (verificați dacă sistemul este la fel de rapid pe cât de așteptat)
sistem: Teste de sistem (se întinde pe module pentru a verifica integrarea modulelor)
unitate: teste unitare (în cadrul modulelor pentru a verifica funcționalitatea de bază)
Oricare dintre aceste tipuri de test poate fi furnizat ca o constrângere folosind --constrângere opțiune.
Pentru a vedea o listă rapidă a tuturor suitelor de testare disponibile, puteți cere să fie
enumerate. Următoarea comandă,
$ ./test.py --list
va avea ca rezultat afișarea unei liste a suitei de testare, similară cu
Waf: se introduce în directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Ieșim din directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„build” finalizat cu succes (0.939s)
histograma
ns3-wifi-interferență
ns3-tcp-cwnd
ns3-tcp-interoperabilitate
probă
dispozitive-plasă-flacără
dispozitive-mesh-dot11s
dispozitive-mesh
...
obiect-nume-serviciu
suna inapoi
atribute
config
valoare globală
Linie de comanda
număr-aleator-de bază
obiect
Oricare dintre aceste suite enumerate poate fi selectată pentru a fi rulată de la sine utilizând --suită opțiune ca
prezentat mai sus.
Similar suitelor de testare, se poate rula un singur exemplu de program C++ folosind --exemplu
opțiune. Rețineți că calea relativă pentru exemplu nu trebuie inclusă și că
executabilele construite pentru exemplele C++ nu au extensii. Intrând
$ ./test.py --example=udp-echo
rezultă că acel singur exemplu este rulat.
PASS: Exemple de exemple/udp/udp-echo
Puteți specifica directorul în care a fost construit ns-3 folosind --buildpath opțiune ca
urmează.
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
Se poate rula un singur exemplu de program Python folosind --pyexample opțiune. Rețineți că
calea relativă pentru exemplu trebuie inclusă și exemplele Python au nevoie de acestea
extensii. Intrând
$ ./test.py --pyexample=examples/tutorial/first.py
rezultă că acel singur exemplu este rulat.
PASS: Exemple de exemple/tutorial/first.py
Deoarece exemplele Python nu sunt construite, nu trebuie să specificați directorul unde ns-3
a fost construit pentru a le rula.
În mod normal, atunci când sunt executate programe exemplu, acestea scriu o cantitate mare de date ale fișierului de urmărire.
Acesta este în mod normal salvat în directorul de bază al distribuției (de exemplu,
/home/user/ns-3-dev). Când test.py rulează un exemplu, este într-adevăr complet indiferent
cu fișierele de urmărire. Vrea doar să determine dacă exemplul poate fi construit și rulat
fara eroare. Deoarece acesta este cazul, fișierele de urmărire sunt scrise într-un
/tmp/unchecked-traces director. Dacă rulați exemplul de mai sus, ar trebui să puteți găsi
asociatul udp-echo.tr și udp-echo-n-1.pcap dosare acolo.
Lista de exemple disponibile este definită de conținutul directorului „exemple” în
distributia. Dacă selectați un exemplu pentru execuție folosind --exemplu opțiune,
test.py nu va face nicio încercare de a decide dacă exemplul a fost configurat sau nu
va încerca doar să-l ruleze și să raporteze rezultatul încercării.
Cand test.py rulează, în mod implicit se va asigura mai întâi că sistemul a fost complet
construit. Acest lucru poate fi învins selectând --nowaf opțiune.
$ ./test.py --list --nowaf
va avea ca rezultat afișarea unei liste a suitelor de testare construite în prezent, similar cu:
modele-ns3-wifi-propagare-pierdere
ns3-tcp-cwnd
ns3-tcp-interoperabilitate
pcap-fișier-obiect
obiect-nume-serviciu
generatoare de numere aleatorii
Rețineți absența waf construiți mesaje.
test.py acceptă, de asemenea, rularea suitelor de testare și a exemplelor sub valgrind. Valgrind este un
program flexibil pentru depanare și profilare executabile Linux. În mod implicit, valgrind rulează
un instrument numit memcheck, care realizează o serie de funcții de verificare a memoriei, inclusiv
detectarea acceselor la memoria neinițializată, utilizarea greșită a memoriei alocate (liberări duble,
acces după liber, etc.) și detectarea scurgerilor de memorie. Acesta poate fi selectat folosind
--pisa opțiune.
$ ./test.py --grind
Pe măsură ce rulează, test.py iar programele pe care le rulează indirect generează un număr mare de
fișiere temporare. De obicei, conținutul acestor fișiere nu este interesant, însă în unele
cazuri, poate fi util (pentru depanare) vizualizarea acestor fișiere. test.py oferă o
--reține opțiunea care va face ca aceste fișiere temporare să fie păstrate după rulare
efectuat. Fișierele sunt salvate într-un director numit testpy-ieșire sub un subdirector
numită în funcție de Timpul Universal Coordonat curent (cunoscut și sub numele de Greenwich Mean
Timp).
$ ./test.py --retain
În cele din urmă, test.py oferă o --verbos opțiune care va imprima cantități mari de informații
despre progresul ei. Nu este de așteptat ca acest lucru să fie teribil de util dacă nu există
o eroare. În acest caz, puteți obține acces la ieșirea standard și la eroarea standard
raportate prin rularea suitelor de testare și a exemplelor. Selectați verbose în felul următor:
$ ./test.py --verbose
Toate aceste opțiuni pot fi combinate și combinate. De exemplu, pentru a rula tot nucleul ns-3
suite de testare sub valgrind, în modul verbose, în timp ce generează un fișier de ieșire HTML, unul
ar face:
$ ./test.py --verbose --grind --constrain=core --html=results.html
TestTaxonomie
După cum sa menționat mai sus, testele sunt grupate într-un număr de clasificări larg definite pentru
permite utilizatorilor să ruleze selectiv teste pentru a aborda diferitele tipuri de testare de care au nevoie
să fie făcut.
· Teste de verificare a construirii
· Teste unitare
· Teste de sistem
· Exemple
· Teste de performanță
BuildVerificationTests
Acestea sunt teste relativ simple care sunt construite împreună cu distribuția și sunt utilizate
pentru a vă asigura că construcția funcționează destul de mult. Testele noastre unitare actuale trăiesc în
fișierele sursă ale codului pe care îl testează și sunt încorporate în modulele ns-3; și așa se potrivește
descrierea BVT-urilor. BVT-urile trăiesc în același cod sursă care este încorporat în codul ns-3.
Testele noastre actuale sunt exemple ale acestui tip de test.
Unitate Teste
Testele unitare sunt teste mai implicate care intră în detaliu pentru a se asigura că o bucată de cod
funcționează așa cum este anunțat în mod izolat. Nu există cu adevărat niciun motiv pentru acest tip de test
integrat într-un modul ns-3. Se dovedește, de exemplu, că unitatea testează pentru obiect
serviciul de nume au aproximativ aceeași dimensiune ca și codul serviciului de nume obiect. Teste unitare
sunt teste care verifică un singur bit de funcționalitate care nu sunt încorporate în codul ns-3,
dar locuiesc în același director cu codul pe care îl testează. Este posibil ca aceste teste
verificați integrarea mai multor fișiere de implementare într-un modul, de asemenea. Fișierul
src/core/test/names-test-suite.cc este un exemplu de acest tip de test. Fișierul
src/network/test/pcap-file-test-suite.cc este un alt exemplu care folosește un pcap bun cunoscut
fișier ca fișier vector de testare. Acest fișier este stocat local în directorul src/network.
Sistem Teste
Testele de sistem sunt cele care implică mai mult de un modul în sistem. Avem multe
acest tip de test rulează în cadrul nostru de regresie actual, dar sunt de obicei
exemple supraîncărcate. Oferim un nou loc pentru acest tip de test în director
src/test. Fișierul src/test/ns3tcp/ns3-interop-test-suite.cc este un exemplu de acest fel
de testare. Utilizează NSC TCP pentru a testa implementarea ns-3 TCP. De multe ori va exista un test
vectori necesari pentru acest tip de test și sunt stocați în directorul în care
testează vieți. De exemplu, ns3tcp-interop-response-vectors.pcap este un fișier format dintr-un
numărul de anteturi TCP care sunt utilizate ca răspunsuri așteptate ale TCP-ului ns-3 testat
la un stimul generat de NSC TCP care este folosit ca implementare „bun cunoscut”.
Exemple
Exemplele sunt testate de framework pentru a vă asigura că au fost construite și vor rula. Nimic nu este
verificat, iar în prezent fișierele pcap sunt doar scrise în / tmp a fi aruncat. Dacă
exemplele rulează (nu se prăbușesc) trec acest test de fum.
Performanţă Teste
Testele de performanță sunt cele care exercită o anumită parte a sistemului și determină
dacă testele s-au executat până la finalizare într-un timp rezonabil.
Alergare Teste
Testele sunt de obicei efectuate folosind nivelul înalt test.py program. Pentru a obține o listă a
opțiunile disponibile din linia de comandă, rulați test.py --Ajutor
Programul de testare test.py va rula ambele teste și acele exemple la care au fost adăugate
lista de verificat. Diferența dintre teste și exemple este următoarea. Teste
în general, verificați dacă rezultatele sau evenimentele specifice de simulare sunt conforme cu comportamentul așteptat.
În schimb, rezultatul exemplelor nu este verificat, iar programul de testare doar verifică
starea de ieșire a programului exemplu pentru a vă asigura că rulează fără eroare.
Pe scurt, pentru a rula toate testele, mai întâi trebuie să configurați testele în timpul etapei de configurare și
de asemenea (opțional) exemple dacă exemplele trebuie verificate:
$ ./waf --configure --enable-examples --enable-tests
Apoi, construiți ns-3 și, după ce este construit, rulați test.py. test.py -h va arăta un număr
de opțiuni de configurare care modifică comportamentul test.py.
Programul test.py invocă, pentru teste și exemple C++, un program C++ de nivel inferior numit
alergător de încercare pentru a rula efectiv testele. După cum se discută mai jos, aceasta alergător de încercare poate fi a
mod util de a depana testele.
Depanarea Teste
Depanarea programelor de testare este cel mai bine efectuată rulând testul de nivel scăzut
program. Runnerul de testare este puntea de la codul generic Python la ns-3 cod. Este
scris în C++ și utilizează procesul de descoperire automată a testelor în ns-3 cod pentru a găsi și
permite executarea tuturor diferitelor teste.
Principalul motiv pentru care test.py nu este potrivit pentru depanare este că nu este permis
înregistrarea trebuie activată folosind NS_LOG variabilă de mediu când test.py rulează. Acest
limitarea nu se aplică executabilului test-runner. Prin urmare, dacă doriți să vedeți înregistrarea
rezultate din testele dvs., trebuie să le executați folosind direct test-runner.
Pentru a executa test-runner, îl rulați ca orice alt executabil ns-3 - folosind
WAF. Pentru a obține o listă de opțiuni disponibile, puteți tasta:
$ ./waf --run "test-runner --help"
Ar trebui să vedeți ceva de genul următor
Waf: se introduce în directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Ieșim din directorul `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„build” finalizat cu succes (0.353s)
--assert: spuneți testelor să segfault (cum ar fi assert) dacă este detectată o eroare
--basedir=dir: Setați directorul de bază (unde să găsiți src) la „dir”
--tempdir=dir: Setați directorul temporar (unde se găsesc fișierele de date) la „dir”
--constrain=test-type: constrânge verificările la seturile de testare de tip „test-type”
--help: Imprimați acest mesaj
--kinds: enumerați toate tipurile de teste disponibile
--list: listează toate suitele de testare (opțional limitate de tipul testului)
--out=nume-fișier: Setați fișierul de ieșire pentru starea testului la „nume-fișier”
--suite=suite-name: rulați suita de testare numită ''suite-name''
--verbose: Activați mesajele în suitele de testare de rulare
Aveți la dispoziție o serie de lucruri care vă vor fi familiare dacă aveți
sa uitat la test.py. Acest lucru ar trebui să fie de așteptat, deoarece runnerul de testare este doar o interfață
între test.py și ns-3. Este posibil să observați că aici lipsesc comenzile legate de exemple.
Asta pentru că exemplele chiar nu sunt ns-3 teste. test.py le conduce de parcă ar fi
pentru a prezenta un mediu de testare unificat, dar ele sunt într-adevăr complet diferite și nu
de găsit aici.
Prima opțiune nouă care apare aici, dar nu în test.py este --afirma opțiune. Acest
opțiunea este utilă atunci când depanați un caz de testare când rulați sub un depanator ca gDB. Când
selectată, această opțiune îi spune cazului de testare subiacent să provoace o încălcare a segmentării dacă
este detectată o eroare. Acest lucru are efectul secundar plăcut de a opri execuția programului
(intră în depanator) când este detectată o eroare. Dacă utilizați gdb, ați putea folosi
această opțiune ceva de genul,
$ ./coaja waf
$ cd build/debug/utils
$ gdb test-runner
$ run --suite=valoare-globală --assert
Dacă se găsește apoi o eroare în suita de teste cu valoare globală, va fi generată o eroare seg
iar depanatorul (la nivel de sursă) s-ar opri la NS_TEST_ASSERT_MSG care a detectat
eroare.
O altă opțiune nouă care apare aici este --basedir opțiune. Se pare că unii
testele ar putea avea nevoie să facă referire la directorul sursă al ns-3 distribuție pentru a găsi local
date, astfel încât un director de bază este întotdeauna necesar pentru a rula un test.
Dacă executați un test de la test.py, programul Python va oferi opțiunea basedir pentru
tu. Pentru a rula unul dintre teste direct de la testatorul folosind WAF, va trebui sa
specificați suita de testare care va rula împreună cu directorul de bază. Deci ai putea folosi carcasa
si fa:
$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object"
Observați ghilimelele „înapoi” de pe PWD comanda.
Dacă rulați suita de teste dintr-un depanator, poate fi destul de dureros de reținut
și tastați în mod constant calea absolută a directorului de bază de distribuție. Din cauza
asta, dacă omiteți baza, cel care alergă la testare va încerca să găsească unul pentru dvs. Aceasta
începe în directorul de lucru curent și urcă în arborele de directoare căutând a
fișier director cu fișierele numite VERSIUNE și LICENȚĂ. Dacă găsește unul, presupune că
trebuie să fie baza și ți-l oferă.
Testare producție
Multe suite de testare trebuie să scrie fișiere temporare (cum ar fi fișierele pcap) în procesul de
rularea testelor. Testele au nevoie apoi de un director temporar în care să scrie. Pitonul
Utilitarul de testare (test.py) va furniza automat un fișier temporar, dar dacă este rulat autonom
acest director temporar trebuie furnizat. La fel ca în cazul bazat, poate fi
enervant să fie nevoit să furnizeze continuu a --tempdir, astfel încât alergătorul de testare va figura unul
pentru tine dacă nu oferi unul. Mai întâi caută variabilele de mediu numite TMP
și TEMP și le folosește. Dacă nici unul TMP nici TEMP sunt definite alege / tmp. Codul
apoi prinde un identificator care indică ce a creat directorul (ns-3), apoi ora
(hh.mm.ss) urmat de un număr mare aleatoriu. Runnerul de testare creează un director al acestuia
nume care va fi folosit ca director temporar. Fișierele temporare intră apoi într-un director care
va fi numit ceva de genul
/tmp/ns-3.10.25.37.61537845
Ora este oferită ca indiciu, astfel încât să puteți reconstrui relativ ușor ce
directorul a fost folosit dacă trebuie să vă întoarceți și să priviți fișierele care au fost plasate în acesta
director.
O altă clasă de ieșire este rezultatul de testare, cum ar fi urmele pcap care sunt generate pentru a se compara
ieșire de referință. Programul de testare le va șterge de obicei după toate suitele de testare
alerga. Pentru a dezactiva ștergerea rezultatului de test, rulați test.py cu opțiunea „reține”:
$ ./test.py -r
iar rezultatul de testare poate fi găsit în testpy-output/ director.
Raportarea of test eşecuri
Când rulați o suită de teste folosind test-runner, acesta va rula testul în mod implicit.
Singurul indiciu că veți obține că testul a trecut este absență a unui mesaj
de la WAF spunând că programul a returnat altceva decât un cod de ieșire zero. A obține
unele rezultate din test, trebuie să specificați un fișier de ieșire la care vor fi testate
scrieți starea lor XML folosind --afara opțiune. Trebuie să fii atent la interpretarea
rezultate deoarece suitele de testare vor adăuga rezultate în acest fișier. Încerca,
$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"
Dacă te uiți la dosar myfile.xml ar trebui să vezi ceva de genul,
pcap-fișier-obiect
Verificați dacă PcapFile::Open with mode ''w'' funcționează
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Verificați dacă PcapFile::Open with mode ''r'' funcționează
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Verificați dacă PcapFile::Open with mode ''a'' funcționează
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Verificați dacă PcapFileHeader este gestionat corect
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Verificați dacă PcapRecordHeader este gestionat corect
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Verificați dacă PcapFile poate citi un fișier pcap cunoscut
TRECE
real 0.00 utilizator 0.00 sistem 0.00
TRECE
real 0.00 utilizator 0.00 sistem 0.00
Dacă sunteți familiarizat cu XML, acest lucru ar trebui să se explice de la sine. De asemenea, nu este un
fișier XML complet, deoarece suitele de testare sunt proiectate pentru a avea ieșirea atașată la un master
Fișierul de stare XML așa cum este descris în test.py secţiune.
Depanarea test suită eşecuri
Pentru a remedia erorile de testare, cum ar fi
CRASH: TestSuite ns3-wifi-interference
Puteți accesa programul test-runner de bază prin gdb după cum urmează, apoi treceți
Argumentul „--basedir=`pwd`” de rulat (puteți transmite și alte argumente după cum este necesar, dar
basedir este minimul necesar):
$ ./waf --command-template="gdb %s" --run "test-runner"
Waf: se introduce în directorul „/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build”
Waf: Se părăsește directorul `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
„build” finalizat cu succes (0.380s)
GNU gdb 6.8-debian
Drepturi de autor (C) 2008 Free Software Foundation, Inc.
L cens GPLv3+: GNU GPL versiunea 3 sau mai recentăhttp://gnu.org/licenses/gpl.html>
Acesta este software gratuit: sunteți liber să îl schimbați și să îl redistribuiți.
NU ESTE GARANȚIE, în măsura permisă de lege. Tastați „afișați copierea”
și „afișați garanția” pentru detalii.
Acest GDB a fost configurat ca „x86_64-linux-gnu”...
(gdb) r --basedir=`pwd`
Pornirea programului: <..>/build/debug/utils/test-runner --basedir=`pwd`
[Depanarea firelor folosind libthread_db activată]
afirmarea a eșuat. fișier=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...
Iată un alt exemplu despre cum să utilizați valgrind pentru a depana o problemă de memorie, cum ar fi:
VALGR: Dispozitive TestSuite-mesh-dot11s-regresie
$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --run test-runner
Clasă TestRunner
Executabilele care rulează programe de testare dedicate folosesc o clasă TestRunner. Această clasă
prevede înregistrarea și listarea automată a testului, precum și o modalitate de a executa
teste individuale. Suitele de testare individuale folosesc constructori globali C++ pentru a se adăuga
o colecție de suite de testare gestionate de testatorul. Runnerul de testare este folosit pentru a lista
toate testele disponibile și pentru a selecta un test de rulat. Aceasta este o clasă destul de simplă
care oferă trei metode statice pentru a furniza sau Adăugarea și obținerea suitelor de testare la a
colectare de teste. Vezi doxygenul pentru curs ns3::TestRunner pentru detalii.
Testare Suită
TOATE ns-3 testele sunt clasificate în suite de testare și cazuri de testare. O suită de teste este a
colecție de cazuri de testare care exercită complet un anumit tip de funcționalitate. La fel de
descrise mai sus, suitele de testare pot fi clasificate ca,
· Teste de verificare a construirii
· Teste unitare
· Teste de sistem
· Exemple
· Teste de performanță
Această clasificare este exportată din clasa TestSuite. Această clasă este destul de simplă,
existând doar ca loc de export de acest tip și de acumulare de cazuri de testare. De la un utilizator
perspectiva, pentru a crea un nou TestSuite în sistem trebuie doar să definiți un nou
clasa care moștenește din clasă TestSuite și îndeplinește aceste două sarcini.
Următorul cod va defini o nouă clasă care poate fi rulată de test.py ca test ''unitate''
cu numele afișat, numele-suită-mea-de-test.
clasa MySuite : public TestSuite
{
public:
MyTestSuite ();
};
MyTestSuite::MyTestSuite ()
: TestSuite ("numele-suita-mea-de-test", UNIT)
{
AddTestCase (nou MyTestCase);
}
MyTestSuite myTestSuite;
Clasa de bază se ocupă de toate înregistrările și raportările necesare pentru a fi un bun
cetăţean în cadrul testului.
Testare Caz
Testele individuale sunt create folosind o clasă TestCase. Modele comune pentru utilizarea unui test
caz includ „un caz de testare per caracteristică” și „un caz de testare per metodă”. Amestecuri de
aceste modele pot fi utilizate.
Pentru a crea un nou caz de testare în sistem, tot ce trebuie să faceți este să moșteniți de la
TestCase clasa de bază, suprascrieți constructorul pentru a da un nume și suprascrie cazului de testare
il DoRun metoda de rulare a testului.
clasa MyTestCase : TestCase public
{
MyTestCase ();
virtual void DoRun (void);
};
MyTestCase::MyTestCase ()
: TestCase ("Verificați un pic de funcționalitate")
{
}
anula
MyTestCase::DoRun (void)
{
NS_TEST_ASSERT_MSG_EQ (adevărat, adevărat, „Un mesaj de eșec”);
}
Utilități
Există o serie de utilități de diferite tipuri care fac, de asemenea, parte din testare
cadru. Exemplele includ un fișier pcap generalizat util pentru stocarea vectorilor de testare; A
container generic util pentru stocarea tranzitorie a vectorilor de testare în timpul executării testului; și
instrumente pentru generarea de prezentări bazate pe rezultatele testelor de validare și verificare.
Aceste utilitare nu sunt documentate aici, dar, de exemplu, vă rugăm să vedeți cum testează TCP
gasit in src/test/ns3tcp/ utilizați fișierele pcap și ieșirea de referință.
Cum la scrie teste
Un obiectiv principal al proiectului ns-3 este de a ajuta utilizatorii să îmbunătățească validitatea și
credibilitatea rezultatelor lor. Sunt multe elemente pentru a obține modele valabile și
simulări, iar testarea este o componentă majoră. Dacă contribuiți cu modele sau exemple la
ns-3, vi se poate cere să contribuiți cu codul de testare. Vor fi folosite modelele cu care contribuiți
de mulți ani de către alți oameni, care probabil nu au idee la prima vedere dacă
modelul este corect. Codul de testare pe care îl scrieți pentru modelul dvs. vă va ajuta să evitați viitorul
regresii în rezultat și va ajuta viitorii utilizatori în înțelegerea verificării și
limitele de aplicabilitate ale modelelor dvs.
Există multe modalități de a verifica corectitudinea implementării unui model. In acest
secțiunea, sperăm să acoperim câteva cazuri comune care pot fi folosite ca ghid pentru a scrie noi
teste.
Eşantion TestSuite schelet
Când începeți de la zero (adică nu adăugați un TestCase la un TestSuite existent), acestea
lucrurile trebuie decise din timp:
· Cum se va numi suita de teste
· Ce tip de test va fi (test de verificare a construcției, test unitar, test de sistem sau
Test de performanță)
· Unde va locui codul de testare (fie într-un modul ns-3 existent, fie separat în
directorul src/test/). Va trebui să editați fișierul wscript din acel director la
compilați noul cod, dacă este un fișier nou.
Un program numit src/create-module.py este un bun punct de plecare. Acest program poate fi
invocat precum create-module.py router pentru un nou modul ipotetic numit router. O singura data
faci asta, vei vedea a router director și a test/router-test-suite.cc suită de teste.
Acest fișier poate fi un punct de plecare pentru testul dumneavoastră inițial. Aceasta este o suită de teste funcțională,
deşi testele efective efectuate sunt banale. Copiați-l în testul modulului dvs
director și faceți o înlocuire globală a „Router” în acel fișier cu ceva aferent
la modelul pe care doriți să-l testați. De asemenea, puteți edita lucruri, cum ar fi o mai descriptivă
numele cazului de testare.
De asemenea, trebuie să adăugați un bloc în wscript-ul dvs. pentru a compila acest test:
module_test.source = [
„test/router-test-suite.cc”,
]
Înainte de a începe efectiv să faceți acest lucru să facă lucruri utile, vă poate ajuta să încercați să rulați
schelet. Asigurați-vă că ns-3 a fost configurat cu opțiunea „--enable-tests”.
Să presupunem că noua ta suită de teste se numește „router”, cum ar fi aici:
RouterTestSuite::RouterTestSuite ()
: TestSuite ("router", UNIT)
Încercați această comandă:
$ ./test.py -s router
Ar trebui să se producă rezultate precum mai jos:
PASS: router TestSuite
1 din 1 teste trecute (1 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Consultați src/lte/test/test-lte-antenna.cc pentru un exemplu funcțional.
Testare macro-uri
Există o serie de macrocomenzi disponibile pentru verificarea rezultatelor programului de testare cu așteptările
ieșire. Aceste macrocomenzi sunt definite în src/core/model/test.h.
Setul principal de macrocomenzi care sunt utilizate includ următoarele:
NS_TEST_ASSERT_MSG_EQ(actual, limită, mesaj)
NS_TEST_ASSERT_MSG_NE(real, limită, mesaj)
NS_TEST_ASSERT_MSG_LT(real, limită, mesaj)
NS_TEST_ASSERT_MSG_GT(actual, limită, mesaj)
NS_TEST_ASSERT_MSG_EQ_TOL(actual, limită, tol, mesaj)
Primul argument curent este valoarea testată, a doua valoare limita este de așteptat
valoarea (sau valoarea cu care se testează) și ultimul argument msg este mesajul de eroare către
tipăriți dacă testul eșuează.
Primele patru macrocomenzi de mai sus testează egalitatea, inegalitatea, mai mică sau mai mare decât,
respectiv. A cincea macro de mai sus testează egalitatea, dar cu o anumită toleranță.
Această variantă este utilă atunci când se testează numerele în virgulă mobilă pentru egalitate față de o limită,
unde doriți să evitați eșecul testului din cauza erorilor de rotunjire.
În cele din urmă, există variante ale celor de mai sus unde cuvântul cheie AFIRMA se înlocuiește cu AŞTEPTA.
Aceste variante sunt concepute special pentru a fi utilizate în metodele (în special callback-uri) returnare
gol. Rezervați utilizarea acestora pentru apelurile inverse pe care le utilizați în programele dvs. de testare; in caz contrar, foloseste
il AFIRMA variante.
Cum la adăuga an exemplu program la il test suită
Se poate „testa de fum” pe care exemplele se compilează și rulează cu succes până la finalizare (fără
scurgeri de memorie) folosind examples-to-run.py script-ul situat în directorul de testare al modulului dvs.
Pe scurt, incluzând o instanță a acestui fișier în directorul de testare, puteți provoca
runner de testare pentru a executa exemplele enumerate. De obicei, cel mai bine este să vă asigurați că dvs
selectați exemple care au timpi de rulare rezonabil de scurti pentru a nu bloca testele. Vedea
exemplul în src/lte/test/ director.
Testarea pentru boolean rezultate
Testarea rezultate cand dezordine is implicat
Testarea producție de date împotriva a cunoscut distribuire
Furnizarea nebanală intrare vectori of de date
depozitarea și corelarea nebanală producție de date
prezentarea ta producție test de date
Asistență
Crearea a nou ns-3 model
Acest capitol parcurge procesul de proiectare a unui ns-3 model. În multe cazuri de cercetare,
utilizatorii nu vor fi mulțumiți doar să adapteze modelele existente, ci ar putea dori să extindă
nucleul simulatorului într-un mod nou. Vom folosi exemplul de adăugare a unui ErrorModel la a
simplu ns-3 link ca exemplu motivant al modului în care s-ar putea aborda această problemă și
trece printr-o proiectare și implementare.
NOTĂ:
Documentație
Aici ne concentrăm asupra procesului de creare de noi modele și module noi, precum și unele dintre acestea
alegerile de proiectare implicate. Din motive de claritate, amânăm discuția despre mecanică
de documentare a modelelor și a codului sursă la Documentație capitol.
Amenajări Abordarea
Luați în considerare cum doriți să funcționeze; ce ar trebui să facă. Gândește-te la aceste lucruri:
· funcționalitate: Ce functionalitate ar trebui sa aiba? Care sunt atributele sau configurația
expus utilizatorului?
· reutilizabilitate: Cât de mult ar trebui alții să poată reutiliza designul meu? Pot reutiliza codul de la
ns-2 pentru a incepe? Cum integrează un utilizator modelul cu restul altuia
simulare?
· dependențe: Cum pot reduce introducerea de dependențe externe de noul meu cod
pe cât posibil (pentru a-l face mai modular)? De exemplu, ar trebui să evit vreuna
dependență de IPv4 dacă vreau să fie folosit și de IPv6? Ar trebui să evit orice dependență
pe IP deloc?
Nu ezitați să contactați ns-3-utilizatori or ns-dezvoltatori lista daca ai intrebari.
În special, este important să te gândești la API-ul public al noului tău model și să ceri
părere. De asemenea, ajută să le informați pe ceilalți despre munca dvs. în cazul în care vă interesează
colaboratori.
Exemplu: ErrorModel
Există un model de eroare în ns-2. Permite ca pachetele să fie transmise unui obiect cu stare care
determină, pe baza unei variabile aleatorii, dacă pachetul este corupt. Apelantul poate
apoi decideți ce să faceți cu pachetul (aruncați-l etc.).
API-ul principal al modelului de eroare este o funcție la care se transmite un pachet și valoarea returnată a acestuia
această funcție este un boolean care spune apelantului dacă a avut loc vreo corupție. Notă
că, în funcție de modelul de eroare, tamponul de pachete de date poate fi sau nu corupt.
Să numim această funcție „IsCorrupt()”.
Până acum, în designul nostru, avem:
clasa ErrorModel
{
public:
/ **
* \returns adevărat dacă pachetul trebuie considerat ca fiind eroat/corupt
* \param pkt Pachet la care să se aplice modelul de eroare
*/
bool IsCorrupt (Ptr pkt);
};
Rețineți că nu trecem un pointer const, permițând astfel funcției să modifice
pachet dacă IsCorrupt() returnează adevărat. Nu toate modelele de eroare vor modifica efectiv pachetul;
dacă bufferul de pachete de date este corupt sau nu ar trebui să fie documentat.
Este posibil să dorim și versiuni specializate ale acestuia, cum ar fi în ns-2, deci, deși nu este
numai alegere de proiectare pentru polimorfism, presupunem că vom subclasa o clasă de bază
ErrorModel pentru clase specializate, cum ar fi RateErrorModel, ListErrorModel etc, cum ar fi
se face în ns-2.
S-ar putea să vă gândiți în acest moment, „De ce să nu faceți din IsCorrupt() o metodă virtuală?”. Acesta este
o singură abordare; celălalt este de a face ca funcția publică non-virtuală să fie indirectă prin a
funcție virtuală privată (aceasta în C++ este cunoscută ca expresia interfeței non-virtuale și este
adoptat în ns-3 clasa ErrorModel).
În continuare, acest dispozitiv ar trebui să aibă dependențe de IP sau alte protocoale? Nu vrem
pentru a crea dependențe de protocoalele Internet (modelul de eroare ar trebui să fie aplicabil la
protocoale non-Internet), așa că vom ține cont de asta mai târziu.
O altă considerație este modul în care obiectele vor include acest model de eroare. Ne imaginăm punerea
un setter explicit în anumite implementări NetDevice, de exemplu.:
/ **
* Atașați un Model Error de primire la PointToPointNetDevice.
*
* PointToPointNetDevice poate include opțional un ErrorModel în
* lanțul de primire a pachetelor.
*
* @vedeți ErrorModel
* @param em Ptr la ErrorModel.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr em);
Din nou, aceasta nu este singura opțiune pe care o avem (modelele de eroare ar putea fi agregate la o mulțime de
alte obiecte), dar satisface cazul nostru de utilizare principal, care este de a permite unui utilizator să forțeze
erori la transmisiile de pachete de succes, la nivelul NetDevice.
După ce m-am gândit ceva la existență ns-2 cod, aici este un exemplu de API al unei baze
clasa și prima subclasă care ar putea fi postate pentru evaluarea inițială:
clasa ErrorModel
{
public:
ErrorModel ();
virtual ~ErrorModel ();
bool IsCorrupt (Ptr pkt);
void Resetare (void);
void Activare (void);
void Dezactivare (void);
bool IsEnabled (void) const;
privat:
virtual bool DoCorrupt (Ptr pkt) = 0;
virtual void DoReset (void) = 0;
};
enumerare ErrorUnit
{
EU_BIT,
EU_BYTE,
EU_PKT
};
// Determinați ce pachete sunt eroate, corespunzătoare unui subiacent
// distribuție ale variabilelor aleatoare, o rată de eroare și unitatea pentru rata.
clasa RateErrorModel : public ErrorModel
{
public:
RateErrorModel ();
virtual ~RateErrorModel ();
enum ErrorUnit GetUnit (void) const;
void SetUnit (enum ErrorUnit error_unit);
dublu GetRate (void) const;
void SetRate (rata dubla);
void SetRandomVariable (const RandomVariable &ranvar);
privat:
virtual bool DoCorrupt (Ptr pkt);
virtual void DoReset (void);
};
schelărie
Să presupunem că sunteți gata să începeți implementarea; ai o imagine destul de clara despre
ceea ce doriți să construiți și este posibil să fi solicitat câteva recenzii sau sugestii inițiale de la
lista. O modalitate de a aborda următorul pas (implementare) este crearea de schele și
completați detaliile pe măsură ce designul se maturizează.
Această secțiune prezintă mulți dintre pașii pe care ar trebui să îi luați în considerare pentru a defini schelele sau
un schelet nefuncțional al ceea ce modelul tău va implementa în cele din urmă. De obicei este bun
exersați să nu așteptați să obțineți aceste detalii integrate la sfârșit, ci să verificați a
scheletul modelului dvs. în sistem devreme și apoi adăugați funcții mai târziu, odată ce API și
integrarea pare corectă.
Rețineți că veți dori să modificați câteva lucruri în prezentarea de mai jos pentru modelul dvs
deoarece dacă urmați textul modelului de eroare, codul pe care îl produceți se va ciocni cu
modelul de eroare existent. Mai jos este doar o schiță a modului în care a fost construit ErrorModel pe care dumneavoastră
se poate adapta la alte modele.
Recenzie il ns-3 Codificare Stil Document
În acest moment, poate doriți să faceți o pauză și să citiți ns-3 document de stil de codare, în special
dacă vă gândiți să contribuiți cu codul înapoi la proiect. Stilul de codare
documentul este legat de pagina principală a proiectului: ns-3 de codificare stil.
Decide Unde in il Sursa Copac il Modele Usi Să Locui
Toate ns-3 codul sursă al modelului este în director src /. Va trebui să alegeți care
subdirectorul în care se află. Dacă este un cod de model nou, este logic să îl punem
în src / director undeva, în special pentru ușurința integrării cu build-ul
sistemului.
În cazul modelului de eroare, acesta este foarte legat de clasa de pachete, deci are sens
pentru a implementa acest lucru în src/network/ modul unde ns-3 pachetele sunt implementate.
WAF și wscript
ns-3 folosește waf construirea sistemului. Veți dori să vă integrați noul ns-3 folosește Waf-ul
construirea sistemului. Veți dori să integrați noile fișiere sursă în acest sistem. Acest
necesită să adăugați fișierele dvs. la wscript fișier găsit în fiecare director.
Să începem cu fișierele goale error-model.h și error-model.cc și să adăugăm acestea la
src/network/wscript. Este de fapt doar o chestiune de adăugare a fișierului .cc la restul fișierului
fișierele sursă și fișierul .h la lista fișierelor de antet.
Acum, apăsați în directorul de nivel superior și tastați „./test.py”. Nu ar fi trebuit să te spargi
orice prin această operațiune.
Include Paznici
În continuare, să adăugăm câteva include Gardienii în fișierul nostru antet.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
...
#endif
Spațiu de nume ns3
ns-3 folosește ns-3 Spațiu de nume pentru a-și izola simbolurile de alte spații de nume. De obicei, a
utilizatorul va pune în continuare un ns-3 bloc de spațiu de nume atât în fișierul cc, cât și în fișierul h.:
spatiu de nume ns3 {
...
}
În acest moment, avem câteva fișiere scheletice în care putem începe să definim noile noastre clase.
Fișierul antet arată astfel:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
spatiu de nume ns3 {
} // spatiu de nume ns3
#endif
in timp ce error-model.cc fișierul arată pur și simplu așa:
#include „error-model.h”
spatiu de nume ns3 {
} // spatiu de nume ns3
Aceste fișiere ar trebui să fie compilate, deoarece nu au cu adevărat conținut. Acum suntem gata
începeți să adăugați clase.
Inițială Punerea în aplicare
În acest moment, încă lucrăm la unele schele, dar putem începe să ne definim
clase, cu funcționalitatea care urmează să fie adăugată ulterior.
Moşteni de la il Obiect Clasă?
Acesta este un pas important de proiectare; dacă să folosești clasa Obiect ca o clasă de bază pentru noul tău
clase.
După cum este descris în capitolul despre ns-3 Model-obiect, clase care moștenesc din clasă
Obiect obține proprietăți speciale:
· cel ns-3 tip și sistem de atribute (vezi Atribute)
· un sistem de agregare a obiectelor
· un sistem de numărare a referințelor cu indicator inteligent (clasa Ptr)
Clase care derivă din clasă ObjectBase} obține primele două proprietăți de mai sus, dar nu
obține indicații inteligente. Clase care derivă din clasă RefCountBase obțineți doar indicatorul inteligent
sistem de numărare a referințelor.
În practică, clasă Obiect este varianta celor trei de mai sus pe care ns-3 dezvoltatorul va
întâlnire cel mai frecvent.
În cazul nostru, dorim să folosim sistemul de atribute și vom transmite instanțe
a acestui obiect peste ns-3 API public, deci clasa Obiect este potrivit pentru noi.
Inițială Clase
O modalitate de a proceda este să începeți prin a defini funcțiile minime și să vedeți dacă vor fi
compila. Să revizuim tot ce este necesar pentru a implementa atunci când derivăm din clasa Object.:
#ifndef ERROR_MODEL_H
#define ERROR_MODEL_H
#include „ns3/object.h”
spatiu de nume ns3 {
clasa ErrorModel : obiect public
{
public:
static TypeId GetTypeId (void);
ErrorModel ();
virtual ~ErrorModel ();
};
clasa RateErrorModel : public ErrorModel
{
public:
static TypeId GetTypeId (void);
RateErrorModel ();
virtual ~RateErrorModel ();
};
#endif
Câteva lucruri de remarcat aici. Trebuie să includem obiect.h. Convenția din ns-3 este că dacă
fișierul antet este co-locat în același director, poate fi inclus fără nicio cale
prefix. Prin urmare, dacă am implementa ErrorModel în src/core/model director, noi
ar fi putut spune doar "#include "obiect.h"". Dar suntem în src/rețea/model, deci trebuie
include-l ca "#include „ns3/object.h”". Rețineți, de asemenea, că aceasta iese în afara spațiului de nume
declaraţie.
În al doilea rând, fiecare clasă trebuie să implementeze o funcție membru public statică numită GetTypeId (Nu apare).
În al treilea rând, este o idee bună să implementați constructori și destructori, mai degrabă decât să lăsați
compilatorul le generează și să facă destructorul virtual. În C++, rețineți și acea copie
operatorul de atribuire și constructorii de copiere sunt generați automat dacă nu sunt definiți, deci
dacă nu le doriți, ar trebui să le implementați ca membri privați. Acest aspect al
C++ este discutat în cartea Effective C++ a lui Scott Meyers. punctul 45.
Să ne uităm acum la un cod de implementare schelet corespunzător din fișierul .cc.:
#include „error-model.h”
spatiu de nume ns3 {
NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
TypeId ErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::ErrorModel")
.SetParent ()
;
return tid;
}
ErrorModel::ErrorModel ()
{
}
ErrorModel::~ErrorModel ()
{
}
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
;
return tid;
}
RateErrorModel::RateErrorModel ()
{
}
RateErrorModel::~RateErrorModel ()
{
}
Care este GetTypeId (Nu apare) funcţie? Această funcție face câteva lucruri. Înregistrează a
șir unic în sistemul TypeId. Stabilește ierarhia obiectelor din
sistem de atribute (prin SetParent). De asemenea, declară că anumite obiecte pot fi create prin
cadrul de creare a obiectelor (AddConstructor).
Macro-ul NS_OBJECT_ENSURE_REGISTERED (numele clasei) este nevoie și o dată pentru fiecare clasă care
definește o nouă metodă GetTypeId și face înregistrarea efectivă a clasei în
sistem. Capitolul Object-model discută acest lucru mai detaliat.
Inclusiv Extern Fişiere
Exploatari forestiere Asistență
Aici, scrie a pic despre adăugare |ns3| logare macro-uri. notițe acea LOG_COMPONENT_DEFINE is
făcut exterior il Spațiu de nume ns3
Constructor, Gol Funcţie prototipuri
Cheie Variabile (Mod implicit Valori, Atribute)
Testare Program 1
Obiect Cadru
Adăugare a Eşantion Scenariu
În acest moment, poate doriți să încercați să luați schela de bază definită mai sus și să o adăugați
în sistem. Efectuarea acestui pas vă permite acum să utilizați un model mai simplu la instalații sanitare
în sistem și poate dezvălui, de asemenea, dacă este necesar să se facă modificări de design sau API
făcut. Odată ce se face acest lucru, vom reveni la construirea funcționalității
ErrorModels în sine.
Adăuga pachet de bază Asistență in il Clasă
/* punct-la-punct-net-device.h */
clasa ErrorModel;
/ **
* Model de eroare pentru primirea evenimentelor de pachete
*/
Ptr m_receiveErrorModel;
Adăuga Accesor
anula
PointToPointNetDevice::SetReceiveErrorModel (Ptr e)
{
NS_LOG_FUNCTION (acest << em);
m_receiveErrorModel = em;
}
.AddAttribute ("ReceiveErrorModel",
„Modelul de eroare al receptorului folosit pentru a simula pierderea pachetelor”,
PointerValue (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker ())
Plumb În il Sistem
void PointToPointNetDevice::Receive (Ptr pachet)
{
NS_LOG_FUNCTION (acest << pachet);
protocol uint16_t = 0;
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (pachet) )
{
//
// Dacă avem un model de eroare și acesta indică faptul că este timpul să pierdem a
// pachet corupt, nu redirecționați acest pachet, lăsați-l.
//
m_dropTrace (pachet);
}
altfel
{
//
// Apăsați cârligul de urmărire de primire, îndepărtați antetul protocolului punct la punct
// și trimite acest pachet în stiva de protocol.
//
m_rxTrace (pachet);
ProcessHeader (pachet, protocol);
m_rxCallback (acest, pachet, protocol, GetRemote ());
dacă (!m_promiscCallback.IsNull ())
{ m_promiscCallback (acest, pachet, protocol, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}
Crează Null Funcțională Scenariu
/* simple-error-model.cc */
// Eroare model
// Dorim să adăugăm un model de eroare la NetDevice al nodului 3
// Putem obține un handle pentru NetDevice prin canal și nod
// indicatoare
Ptr nd3 = PointToPointTopology::GetNetDevice
(n3, canal2);
Ptr em = Creați ();
nd3->SetReceiveErrorModel (em);
bool
ErrorModel::DoCorrupt (Pachet și p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("Corupt!");
return false;
}
În acest moment, putem rula programul cu ErrorModel-ul nostru trivial conectat la recepție
calea PointToPointNetDevice. Tipărește șirul „Corupt!” pentru fiecare pachet
primit la nodul n3. Apoi, revenim la modelul de eroare pentru a adăuga o subclasă care funcționează
modelarea erorilor mai interesantă.
Adăuga a Subclasă
Clasa de bază trivială ErrorModel nu face nimic interesant, dar oferă o
interfață utilă a clasei de bază (Corrupt () și Resetare ()), redirecționată către funcții virtuale care
poate fi subclasat. În continuare, să luăm în considerare ceea ce numim un BasicErrorModel pe care se bazează
il ns-2 Clasa ErrorModel (în ns-2/queue/errmodel.{cc,h}).
Ce proprietăți vrem să aibă acest lucru, din perspectiva interfeței cu utilizatorul? Am vrea
pentru ca utilizatorul să poată schimba trivial tipul de ErrorModel folosit în
NetDevice. Ne-am dori, de asemenea, posibilitatea de a seta parametri configurabili.
Iată câteva cerințe simple pe care le vom lua în considerare:
· Abilitatea de a seta variabila aleatoare care guvernează pierderile (implicit este UniformVariable)
· Abilitatea de a seta unitatea (bit, octet, pachet, timp) de granularitate pentru care sunt erori
aplicat.
· Abilitatea de a seta rata de erori (de ex. 10^-3) corespunzătoare unității de mai sus
granularitatea.
· Abilitatea de a activa/dezactiva (implicit este activat)
Cum la Subclasă
Declarăm BasicErrorModel ca fiind o subclasă a ErrorModel, după cum urmează:
clasa BasicErrorModel : public ErrorModel
{
public:
static TypeId GetTypeId (void);
...
privat:
// Implementează funcții virtuale pure din clasa de bază
virtual bool DoCorrupt (Ptr p);
virtual bool DoReset (void);
...
}
și configurați funcția de subclasă GetTypeId setând un șir unic TypeId și
setarea părintelui la ErrorModel:
TypeId RateErrorModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RateErrorModel")
.SetParent ()
.AddConstructor ()
...
Construi Nucleu funcţii și Unitate Teste
assert Macrocomenzi
Scris Unitate Teste
Adăugare a Nou Module la ns-3
Când ați creat un grup de clase, exemple și teste înrudite, acestea pot fi
combinate într-un ns-3 modulul astfel încât acestea să poată fi utilizate cu cele existente ns-3 module
și de către alți cercetători.
Acest capitol vă prezintă pașii necesari pentru a adăuga un nou modul ns-3.
Pas 0 - Module Aspect
Toate modulele pot fi găsite în src director. Fiecare modul poate fi găsit într-un director
care are același nume cu modulul. De exemplu, cel spectru modul poate fi găsit aici:
src/spectrum. Vom cita din spectru modul pentru ilustrare.
Un modul prototip are următoarea structură de directoare și fișiere necesare:
src /
nume-modul/
legături/
doc/
exemple /
wscript
ajutor/
model/
Test/
examples-to-run.py
wscript
Nu toate directoarele vor fi prezente în fiecare modul.
Pas 1 - Crează a Module Schelet
Un program python este furnizat în directorul sursă care va crea un schelet pentru un nou
modul. În scopul acestei discuții, vom presupune că noul dvs. modul este numit
modul nou. De la src director, faceți următoarele pentru a crea noul modul:
$ ./create-module.py new-module
În continuare, cd în modul nou; veți găsi acest aspect de director:
$ cd nou-modul
$ls
Exemple de documente test de model de ajutor wscript
Mai detaliat, cel create-module.py scriptul va crea directoarele, precum și inițiale
schelet wscript, .h, . Cc și .în primul rând fișiere. Modulul complet cu fișiere schelete arată
asa:
src /
modul-nou/
doc/
modul-nou.în primul rând
exemple /
nou-modul-exemplu.cc
wscript
ajutor/
new-module-helper.cc
nou-modul-ajutor.h
model/
modul-nou.cc
modul-nou.h
Test/
new-module-test-suite.cc
wscript
(Dacă este necesar, legături/ directorul listat în Step-0 va fi creat automat în timpul
construcția.)
Urmărim în continuare cum să personalizăm acest modul. Informare WAF despre dosarele care
alcătuiți modulul dvs. se face prin editarea celor două wscript fișiere. Ne vom plimba prin
pașii principali din acest capitol.
TOATE ns-3 modulele depind de CORE modul și de obicei pe alte module. Această dependență
este specificat în wscript fișier (la nivelul superior al modulului, nu separat wscript
de fișier în exemple director!). În schelet wscript apelul care va declara dvs
modul nou la WAF va arăta astfel (înainte de editare):
def build(bld):
module = bld.create_ns3_module('new-module', ['core'])
Să presupunem că modul nou depinde de Internet, mobilitate și aodv module. După
editandu-l pe wscript fișierul ar trebui să arate ca:
def build(bld):
module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv'])
Rețineți că ar trebui listate numai dependențele de modul de prim nivel, motiv pentru care am eliminat
CORE; Internet modulul la rândul său depinde de CORE.
Modulul dumneavoastră va avea cel mai probabil fișiere sursă model. Scheletele inițiale (care vor
compilare cu succes) sunt create în model/modul-nou.cc și model/modul-nou.h.
Dacă modulul dumneavoastră va avea fișiere sursă de ajutor, atunci acestea vor intra în ajutor/
director; din nou, scheletele inițiale sunt create în acel director.
În cele din urmă, este o practică bună să scrieți teste și exemple. Acestea vor fi aproape sigur
necesare pentru ca noi module să fie acceptate în oficial ns-3 arborele sursă. Un schelet
suita de testare și cazul de testare sunt create în Test/ director. Suita de testare a scheletului va
conțin constructorul de mai jos, care declară un nou test unitar numit modul nou, Cu un
caz de testare unic constând din clasa NewModuleTestCase1:
NewModuleTestSuite::NewModuleTestSuite ()
: TestSuite („modul nou”, UNIT)
{
AddTestCase (nou NewModuleTestCase1);
}
Pas 3 - Declara Sursa Fişiere
Antetul public și fișierele de cod sursă pentru noul dvs. modul ar trebui să fie specificate în
wscript fișier modificându-l cu editorul de text.
De exemplu, după declararea spectru modulul, cel src/spectrum/wscript specifică
fișiere de cod sursă cu următoarea listă:
def build(bld):
module = bld.create_ns3_module('spectru', ['internet', 'propagare', 'antenă', 'aplicaţii'])
module.source = [
„model/spectrum-model.cc”,
„model/spectrum-value.cc”,
.
.
.
„model/cuptor-micunde-spectru-valoare-helper.cc”,
„helper/spectrum-helper.cc”,
„helper/adhoc-aloha-noack-ideal-phy-helper.cc”,
„helper/waveform-generator-helper.cc”,
„helper/spectrum-analyzer-helper.cc”,
]
Obiectele rezultate din compilarea acestor surse vor fi asamblate într-o bibliotecă de linkuri,
care va fi legat de orice programe care se bazează pe acest modul.
Dar cum învață astfel de programe API-ul public al noului nostru modul? Citește mai departe!
Pas 4 - Declara Public Antet Fişiere
Ar trebui să fie și fișierele de antet care definesc API-ul public al modelului dvs. și ajutoarele
specificat în wscript fișier.
Continuând cu spectru ilustrație model, sunt specificate fișierele antet publice
cu strofa următoare. (Rețineți că argumentul la bld funcția spune WAF la
instalați anteturile acestui modul cu celălalt ns-3 anteturi):
anteturi = bld(funcții='ns3header')
headers.module = 'spectru'
headers.source = [
„model/spectru-model.h”,
„model/spectru-valoare.h”,
.
.
.
„model/cuptor-micunde-spectru-valoare-helper.h”,
'helper/spectrum-helper.h',
„helper/adhoc-aloha-noack-ideal-phy-helper.h”,
'helper/generator-formă-de-undă-helper.h',
„helper/spectrum-analyzer-helper.h”,
]
Anteturile făcute publice în acest fel vor fi accesibile utilizatorilor modelului dvs. cu includere
afirmatii ca
#include „ns3/spectrum-model.h”
Anteturile folosite strict intern în implementarea dvs. nu ar trebui incluse aici. ei
sunt încă accesibile pentru implementarea dvs. prin includere declarații precum
#include „my-module-implementation.h”
Pas 5 - Declara Teste
Dacă noul dvs. modul are teste, atunci acestea trebuie specificate în dvs wscript dosar de
modificându-l cu editorul de text.
spectru testele model sunt specificate cu următoarea strofă:
module_test = bld.create_ns3_module_test_library('spectru')
module_test.source = [
„test/spectrum-interference-test.cc”,
„test/spectrum-value-test.cc”,
]
Vedea Teste pentru mai multe informații despre cum să scrieți cazuri de testare.
Pas 6 - Declara Exemple
Dacă noul dvs. modul are exemple, atunci acestea trebuie specificate în dvs exemple/wscript
fişier. (Scheletul de nivel superior wscript va include recursiv exemple/wscript doar dacă
exemplele au fost activate la momentul configurării.)
spectru modelul își definește primul exemplu în src/spectrum/examples/wscript implementate cu
def build(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
[„spectru”, „mobilitate”])
obj.source = 'adhoc-aloha-ideal-phy.cc'
Rețineți că al doilea argument al funcției create_ns3_program() este lista de module
că programul creat depinde de; din nou, nu uitați să includeți modul nou in
lista. Este cea mai bună practică să enumerați numai dependențele directe ale modulelor și să lăsați WAF
deduceți arborele de dependență complet.
Ocazional, pentru claritate, poate doriți să împărțiți implementarea pentru exemplul dvs. între
mai multe fișiere sursă. În acest caz, includeți doar acele fișiere ca explicit suplimentar
sursele exemplului:
obj = bld.create_ns3_program('noul-modul-exemplu', [nou-modul])
obj.source = ['new-module-example.cc', 'new-module-example-part.cc']
Exemplele Python sunt specificate folosind următorul apel de funcție. Rețineți că al doilea
argument pentru funcție register_ns3_script() este lista de module pe care Python-ul
exemplu depinde de:
bld.register_ns3_script('new-module-example.py', ['new-module'])
Pas 7 - Exemple Alerga as Teste
Pe lângă rularea codului de testare explicit, cadrul de testare poate fi, de asemenea, instrumentat
rulați programe de exemplu complete pentru a încerca să prindeți regresii în exemple. Cu toate acestea, nu toate
exemplele sunt potrivite pentru testele de regresie. Fișierul test/examples-to-run.py controlează
invocarea exemplelor când rulează cadrul de testare.
spectru exemple de model conduse de test.py sunt specificate în
src/spectrum/test/examples-to-run.py folosind următoarele două liste de C++ și Python
exemple:
# O listă de exemple C++ de rulat pentru a ne asigura că rămân
# construibil și rulabil în timp. Fiecare tuplu din listă conține
#
# (exemplu_nume, do_run, do_valgrind_run).
#
# Consultați test.py pentru mai multe informații.
cpp_examples = [
("adhoc-aloha-ideal-phy", "True", "True"),
("adhoc-aloha-ideal-phy-cu-cuptor-micunde", "True", "True"),
("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "True", "True"),
]
# O listă de exemple Python de rulat pentru a vă asigura că rămân
# rulabil în timp. Fiecare tuplu din listă conține
#
# (exemplu_nume, do_run).
#
# Consultați test.py pentru mai multe informații.
python_examples = [
("sample-simulator.py", "True"),
]
După cum se indică în comentariu, fiecare intrare din lista C++ de exemple de rulat conține
tuplu (nume_exemplu, alerga, do_valgrind_run), În cazul în care
· exemplu_nume este executabilul care urmează să fie rulat,
· do_run este o condiție în care să rulați exemplul și
· do_valgrind_run este o condiție în care să rulați exemplul sub valgrind. (Acest
este necesar deoarece NSC provoacă blocări ilegale de instrucțiuni cu unele teste atunci când acestea
sunt conduse sub valgrind.)
Rețineți că cele două condiții sunt instrucțiuni Python de care pot depinde WAF configuraţie
variabile. De exemplu,
("tcp-nsc-lfn", "NSC_ENABLED == Adevărat", "NSC_ENABLED == Fals"),
Fiecare intrare din lista Python de exemple de rulat conține tuplu (nume_exemplu,
do_run), unde, ca și pentru exemplele C++,
· exemplu_nume este scriptul Python care urmează să fie rulat și
· do_run este o condiție în care să rulați exemplul.
Din nou, condiția este o declarație Python de care poate depinde WAF variabile de configurare.
De exemplu,
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
Pas 8 - Configurați și Construi
Acum vă puteți configura, construi și testa modulul în mod normal. Trebuie să reconfigurați
proiect ca prim pas astfel încât WAF memorează în cache noile informații în dvs wscript fișiere sau
altfel noul tău modul nu va fi inclus în build.
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
$ ./test.py
Căutați suita de testare a noului dvs. modul (și exemple de programe, dacă modulul dvs. are
activat) în ieșirea de test.
Pas 9 - Piton Bindings
Adăugarea de legături Python la modulul dvs. este opțională, iar pasul este comentat de
implicit în create-module.py script-ul.
# bld.ns3_python_bindings()
Dacă doriți să includeți legături Python (necesar numai dacă doriți să scrieți Python ns-3
programe în loc de programele C++ ns-3), ar trebui să anulați comentariile de mai sus și să instalați
Sistemul de scanare API Python (acoperit în altă parte în acest manual) și scanați-vă modulul în
generează noi legături.
Crearea Documentație
ns-3 furnizează două tipuri de documentație: capitole expozitive în stil „ghid de utilizator” și
documentația API a codului sursă.
Capitolele „ghidul utilizatorului” sunt scrise manual reStructuredText format (.în primul rând), care este
procesate de sistemul de documentare Python Sfinx pentru a genera pagini web și fișiere pdf.
Documentația API este generată din codul sursă însuși, folosind Oxigen, a genera
pagini web încrucișate. Ambele sunt importante: capitolele Sfinx explică de ce
și prezentarea generală a utilizării unui model; documentația API explică cum Detalii.
Acest capitol oferă o privire de ansamblu rapidă asupra acestor instrumente, subliniind utilizarea preferată și
personalizări pentru ns-3.
Pentru a construi toată documentația standard:
$ ./waf docs
Pentru mai multe opțiuni specializate, citiți mai departe.
documentarea implementate cu Sfinx
Noi folosim Sfinx pentru a genera capitole expozitive care descriu designul și utilizarea fiecăruia
modul. Chiar acum citești Documentație Capitol. The Spectacol Sursa link în
bara laterală vă va arăta sursa reStructuredText pentru acest capitol.
Adăugare Nou capitolele
Adăugarea unui nou capitol necesită trei pași (descriși mai detaliat mai jos):
1. Alege Unde? dosarul(ele) de documentație vor fi în viață.
2. Link de la o pagină existentă la noua documentație.
3. Adăugați noul fișier la makefile.
Unde?
Documentație pentru un anumit modul, foo, ar trebui să intre în mod normal src/foo/doc/. De exemplu
src/foo/doc/foo.rst ar fi documentul de nivel superior pentru modul. The
src/create-module.py scriptul va crea acest fișier pentru dvs.
Unele modele necesită mai multe .în primul rând dosare și figuri; toate acestea ar trebui să intre în
src/foo/doc/ director. Documentele sunt de fapt construite de un Sphinx Makefile. Pentru mai ales
documentația implicată, poate fi util să aveți un local makefile în src/foo/doc/
director pentru a simplifica construirea documentației pentru acest modul (Antenă este un exemplu).
Configurarea acestui lucru nu este deosebit de dificilă, dar depășește scopul acestui capitol.
În unele cazuri, documentația se întinde pe mai multe modele; cel Reţea capitolul este un exemplu. În
aceste cazuri adăugând .în primul rând fișiere direct către document/modele/sursa/ ar putea fi potrivit.
Link
Sfinxul trebuie să știe Unde ar trebui să apară noul tău capitol. În cele mai multe cazuri, un model nou
capitolul ar trebui să apară în modele carte. Pentru a adăuga un capitol acolo, editați
doc/models/source/index.rst
.. toctree::
:maxdepth: 1
organizație
animaţie
antenă
aodv
aplicatii
...
Adăugați numele documentului dvs. (fără .în primul rând extensie) la această listă. Vă rugăm să păstrați
Modelați capitolele în ordine alfabetică, pentru a ușura scanarea vizuală pentru anumite capitole.
makefile
De asemenea, trebuie să adăugați documentul la cel corespunzător makefile, asa de face stie sa o verifice
pentru actualizări. Cartea de modele Makefile este doc/models/Makefile, Cartea manuală Makefile este
doc/manual/Makefile.
# enumerați toate fișierele .rst ale bibliotecii de modele care trebuie copiate în $SOURCETEMP
SURSE = \
source/conf.py \
sursa/_static \
source/index.rst \
source/replace.txt \
sursă/organizație.prima \
...
$(SRC)/antenna/doc/source/antenna.rst \
...
Tu adaugi a ta .în primul rând fișiere către SURSE variabil. Pentru a adăuga cifre, citiți comentariile din
makefile pentru a vedea ce variabilă ar trebui să conțină fișierele dvs. de imagine. Din nou, vă rog să păstrați acestea
in ordine alfabetica.
Clădire Sfinx Google Docs
Construirea documentației Sphinx este destul de simplă. Pentru a construi tot Sfinxul
documentație:
$ ./waf sfinx
Pentru a construi doar documentația Modelelor:
$ make -C doc/modele
Pentru a vedea documentația generată, indicați browserul dvs. la doc/models/build/html.
După cum puteți vedea, Sphinx folosește Make pentru a ghida procesul. Ținta implicită construiește totul
formularele de ieșire activate, care în ns-3 sunt cele cu mai multe pagini html, pagina singura singlehtml și
pdf (latex). Pentru a construi doar html-ul cu mai multe pagini, adăugați html ţintă:
$ make -C doc/modele html
Acest lucru poate fi util pentru a reduce timpul de construire (și dimensiunea chatter-ului de construire) ca tine
vă scriu capitolul.
Înainte de a trimite documentația dvs. în repo, vă rugăm să verificați dacă se creează fără
erori sau avertismente. Procesul de compilare generează o mulțime de rezultate (mai ales chatter normal
de la LaTeX), ceea ce poate face dificil să vezi dacă există avertismente Sphinx sau
erori. Pentru a găsi avertismente și erori importante, construiți doar html versiune, apoi caută
jurnalul de construire pentru de avertizare or eroare.
ns-3 Specificul
Sfinxul documentaţie și tutorial sunt destul de bune. Nu vom duplica elementele de bază
aici, concentrându-ne în schimb pe utilizarea preferată pentru ns-3.
· Începeți documentele cu aceste două rânduri:
.. includ:: înlocuiți.txt
.. evidentiaza:: cpp
Prima linie permite câteva înlocuiri simple. De exemplu, tastarea |ns3| redă ca
ns-3. Al doilea setează limbajul de evidențiere implicit al codului sursă în mod explicit pentru
fișier, deoarece estimarea analizatorului nu este întotdeauna exactă. (De asemenea, este posibil să setați
limbaj explicit pentru un singur bloc de cod, vezi mai jos.)
· Secțiuni:
Sphinx este destul de liberal în ceea ce privește marcarea titlurilor de secțiuni. Prin convenție, preferăm asta
ierarhie:
.. ierarhia titlurilor:
------------- Capitolul
************* Secțiune (#.#)
============= Subsecțiune (#.#.#)
############# Sub-subsecțiune
· Evidențierea sintaxei:
Pentru a utiliza evidențiatorul de sintaxă implicit, pur și simplu porniți un bloc de cod sursă:
┌──────────────────────────────────────── ───────┬── ───────────────────────────────┐
│Sursa Sfinx │ Ieșire redată │
├───────────────────────────────────────── ───────┼── ───────────────────────────────┤
│ │ Cel Frobnitz este accesat de: │
│ ``Frobnitz`` este accesat de:: │ │
│ │ Foo::Frobnitz frob; │
│ Foo::Frobnitz frob; │ frob.Set (...); │
│ frob.Set (...); │ │
└───────────────────────────────────────── ───────┴── ────────────────────────────────
Pentru a utiliza un evidențiator de sintaxă specific, de exemplu, pocni comenzi shell:
┌──────────────────────────────────────── ────────── ───┐
│Sursa Sfinx │ Ieșire redată │
├─────────────────────────────────────────── ────────── ───┤
│ │ │
│ .. cod sursă:: bash │ $ ls │
│ │ │
│ $ ls │ │
└────────────────────────────────────────── ────────── ───┘
· Notații stenografice:
Aceste stenografie sunt definite:
┌────────────────────────┬────────────── ───┐
│Sursa Sfinx │ Ieșire redată │
├────────────────────────┼───────────────── ───┤
│ │ ns-3 │
│ |ns3| │ │
├────────────────────────┼───────────────── ───┤
│ │ ns-2 │
│ |ns2| │ │
├────────────────────────┼───────────────── ───┤
│ │ │
│ |verifica| │ │
├────────────────────────┼───────────────── ───┤
│ │ RFC 6282 │
│ :rfc:`6282` │ │
└────────────────────────┴─────────────── ───┘
documentarea implementate cu Oxigen
Noi folosim Oxigen a genera navigabil Documentația API. Doxygen oferă un număr de
caracteristici utile:
· Tabel rezumativ al tuturor membrilor clasei.
· Grafice de moștenire și colaborare pentru toate clasele.
· Legături către codul sursă care implementează fiecare funcție.
· Link-uri către fiecare loc în care este folosit un membru.
· Legături către fiecare obiect utilizat în implementarea unei funcții.
· Gruparea claselor înrudite, cum ar fi toate clasele legate de un anumit protocol.
În plus, folosim TypeId sistem de adăugat la documentația pentru fiecare clasă
· config căi prin care se poate ajunge la astfel de obiecte.
· Documentație pentru orice Atribute, inclusiv Atribute definite în clasele părinte.
· Documentație pentru orice Urmă surse definite de clasă.
Doxygen operează prin scanarea codului sursă, căutând comentarii marcate special. Aceasta
creează, de asemenea, o referință încrucișată, indicând Unde fiecare fișier, clasă, metodă și variabilă este
folosit.
preferat Stil
Stilul preferat pentru comentariile Doxygen este stilul JavaDoc:
/ **
* Scurtă descriere a acestei clase sau metode.
* Liniile adiacente devin un singur paragraf.
*
* Descriere mai lungă, cu multe detalii.
*
* Liniile goale separă paragrafele.
*
* Explicați ce face clasa sau metoda, folosind ce algoritm.
* Explicați unitățile de argumente și valorile returnate.
*
* \notă Rețineți orice limitări sau probleme.
*
* (Pentru funcții cu argumente sau returnate cu valoare:)
* \param foo Scurtă sintagmă nominală care descrie acest argument.
* \param bar Notă Cazul propoziției și perioada de încheiere.
* \return Sintagma nominală scurtă care descrie valoarea.
*
* \intern
*
* De asemenea, puteți discuta despre detaliile interne de implementare.
* Înțelegerea acestui material nu ar trebui să fie necesară pentru utilizare
* clasa sau metoda.
*/
Exemplu de clasă
În acest stil, blocul de comentarii Doxygen începe cu două caractere „*”: / **, și precede
elementul care se documentează.
Pentru articolele care necesită doar o scurtă descriere, oricare dintre aceste forme scurte este potrivită:
/** Implementarea destructorului. */
void DoDispose ();
int m_count; //!< Numărul de...
Observați forma specială a comentariului de la sfârșitul rândului, //!, indicând că se referă la
precedent articol.
Câteva elemente de reținut:
· Folosiți cazul de propoziție, inclusiv majusculul inițial.
· Folosiți semnele de punctuație, în special `.'s la sfârșitul propozițiilor sau frazelor.
· \scurt eticheta nu este necesară; prima propoziție va fi folosită ca brief
Descriere.
Fiecare clasă, metodă, typedef, variabilă membru, argument funcție și valoare returnată ar trebui
să fie documentate în toate fișierele de cod sursă care formează API-ul formal și implementarea pentru
ns-3, Cum ar fi src/ /model/*, src/ /ajutor/* și src/ /utils/*.
Documentația pentru articolele din src/ /Test/* și src/ /exemple/* este de preferat,
dar nu obligatoriu.
util Categorii
· Membrii moșteniți vor moșteni automat documentele de la părinte, (dar pot fi înlocuiți
prin documentația locală).
1. Documentați clasa de bază.
2. În subclasa marchează funcțiile moștenite cu un comentariu obișnuit:
// Metode moștenite
virtual void FooBar (void);
virtual int BarFoo (baz dublu);
Rețineți că semnăturile trebuie să se potrivească exact, așa că includeți argumentul formal (Nu apare)
Acest lucru nu funcționează pentru funcțiile statice; vedea GetTypeId, mai jos, de exemplu.
Clădire Oxigen Google Docs
Crearea documentației Doxygen este destul de simplă:
$ ./waf doxygen
Aceasta se construiește folosind configurația implicită, care generează secțiuni de documentație pentru
toate articole, chiar dacă nu au blocuri explicite de documentație pentru comentarii. Aceasta are
efect de suprimare a avertismentelor pentru articolele nedocumentate, dar se asigură că totul apare
în ieșirea generată.
Când scrieți documentația, este adesea mai util să vedeți ce elemente sunt generate
avertismente, de obicei despre lipsa documentației. Pentru a vedea lista completă de avertismente, utilizați
doc/doxygen.warnings.report.sh scenariu:
$ doc/doxygen.warnings.report.sh
Waf: se introduce directorul `build'
...
Waf: Părăsesc directorul `build'
„construire” finalizată cu succes (3m24.094s)
Reconstruirea documentelor doxygen cu erori complete... Gata.
Raport de avertismente Doxygen
----------------------------------------
(Toate contoarele sunt limite inferioare.)
Avertismente pe modul/director:
Director de numărare
----- ----------------------------------
3844 src/lte/model
1718 src/wimax/model
1423 src/core/model
....
138 de parametri suplimentari nedocumentati.
----------------------------------------
15765 avertismente totale
126 de directoare cu avertismente
Avertismente după fișier (alfabetic)
Fișier de numărare
----- ----------------------------------
17 doc/introspectat-doxygen.h
15 exemple/routing/manet-routing-compare.cc
26 de exemple/statistici/wifi-example-apps.h
....
----------------------------------------
967 de fișiere cu avertismente
Avertismente după fișier (numerice)
Fișier de numărare
----- ----------------------------------
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 de fișiere cu avertismente
Rezumatul avertismentelor Doxygen
----------------------------------------
126 de directoare
Fișiere 967
15765 avertismente
Scriptul modifică configurația pentru a afișa toate avertismentele și pentru a scurta timpul de rulare.
După cum puteți vedea, la acest scris avem a mult a obiectelor nedocumentate. Raportul
rezumă avertismentele pe modul src/*/*, și după fișier, în ordine alfabetică și numerică.
Scriptul are câteva opțiuni pentru a reduce lucrurile și a face acest lucru mai ușor de gestionat. Pentru ajutor,
utilizați -h opțiune. După ce l-am rulat o dată pentru a face construirea Doxygen și a genera întregul
jurnalul de avertismente, puteți reprocesa fișierul jurnal cu diverse „filtre”, fără a fi nevoie să faceți
Doxygen complet construit de, din nou folosind -s opțiune. Puteți exclude avertismentele din
*/exemple/* fisiere (-e opțiune), și/sau */Test/* fisiere (-t).
Poate cea mai utilă opțiune atunci când scrieți comentarii de documentare este -m , Care
va limita raportul la doar fișierele care se potrivesc src/ /*, și urmați raportul cu
liniile de avertizare efective. Combina cu ETH și vă puteți concentra pe avertismentele care sunt
cel mai urgent într-un singur modul:
$ doc/doxygen.warnings.report.sh -m mesh/helper
...
Rezumatul avertismentelor Doxygen
----------------------------------------
1 de directoare
Fișiere 3
149 avertismente
Avertismente filtrate
========================================
src/mesh/helper/dot11s/dot11s-installer.h:72: avertisment: Membrul m_root (variabilă) al clasei ns3::Dot11sStack nu este documentat.
src/mesh/helper/dot11s/dot11s-installer.h:35: avertisment: tipul de returnare al membrului ns3::Dot11sStack::GetTypeId nu este documentat
src/mesh/helper/dot11s/dot11s-installer.h:56: avertisment: tipul de returnare al membrului ns3::Dot11sStack::InstallStack nu este documentat
src/mesh/helper/flame/lfame-installer.h:40: avertisment: Membrul GetTypeId() (funcție) din clasa ns3::FlameStack nu este documentat.
src/mesh/helper/flame/flame-installer.h:60: avertisment: tipul de returnare al membrului ns3::FlameStack::InstallStack nu este documentat
src/mesh/helper/mesh-helper.h:213: warning: Membrul m_nInterfaces (variabilă) din clasa ns3::MeshHelper nu este documentat.
src/mesh/helper/mesh-helper.h:214: warning: Membrul m_spreadChannelPolicy (variabilă) din clasa ns3::MeshHelper nu este documentat.
src/mesh/helper/mesh-helper.h:215: warning: Membrul m_stack (variabilă) din clasa ns3::MeshHelper nu este documentat.
src/mesh/helper/mesh-helper.h:216: warning: Membrul m_stackFactory (variabilă) din clasa ns3::MeshHelper nu este documentat.
src/mesh/helper/mesh-helper.h:209: avertisment: parametrii membrului ns3::MeshHelper::CreateInterface nu sunt (toți) documentați
src/mesh/helper/mesh-helper.h:119: avertisment: parametrii membrului ns3::MeshHelper::SetStandard nu sunt (toți) documentați
Acum este doar o chestiune de înțelegere a codului și de a scrie niște documente!
ns-3 Specificul
Cât despre Sfinx, Doxygenul docs și referință sunt destul de bune. Nu o vom duplica
elementele de bază aici, concentrându-ne în schimb pe utilizarea preferată pentru ns-3.
· Folosiți Doxygen Panouri pentru a grupa articolele conexe.
În antetul principal pentru un modul, creați un grup Doxgyen:
/ **
* \defgroup foo Protocolul Foo.
*/
Marcați fiecare clasă asociată ca aparținând grupului:
/ **
* \ingroup foo
*
* Tip de pachet Foo.
*/
clasa Foo
· Știați typedefs poate avea argumente formale? Acest lucru permite documentarea funcției
semnături indicator:
/ **
* Bară semnătura funcției de apel invers.
*
* \param ale Mărimea unei halbe de bere, în uncii imperiale.
*/
typedef void (* BarCallback)(const int ale);
· Copiați Atribut siruri de ajutor din GetTypeId metoda de utilizat ca brief
descrieri ale membrilor asociați.
· \bugid{298} va crea un link către bug 298 în Bugzilla.
· \pname{foo} într-o descriere va formata foo ca o \param foo parametrul, clarificând
că te referi la un argument real.
· \RFC{301} va crea o legătură către RFC 301.
· \intern ar trebui folosit doar pentru a declanșa o discuție despre detaliile implementării, nu pentru
marca privat funcții (sunt deja marcate, ca privat!)
· Nu creați clase cu nume banale, cum ar fi clasă A, chiar și în suitele de testare. Aceste
determină toate instanțele numelui de clasă literal „A” să fie redate ca legături.
După cum sa menționat mai sus, funcțiile statice nu moștenesc documentația acelorași funcții în
clasa de părinte. ns-3 folosește în mod omniprezent câteva funcții statice; cel sugerat
blocul de documentație pentru aceste cazuri este:
· Constructor/destructor implicit:
Clasa mea (); //!< Constructor implicit
~MyClass (); //!< Destructor
· Dummy destructor și DoDispose:
/** Destructor fals, vezi DoDispose. */
~MyClass ();
/** Implementarea destructorului */
virtual void DoDispose ();
· GetTypeId:
/ **
* Înregistrați acest tip.
* \return Obiectul TypeId.
*/
static TypeId GetTypeId (void);
Activarea Subseturi of ns-3 Panouri
Ca și în cazul majorității proiectelor software, ns-3 este din ce în ce mai mare în ceea ce privește numărul de module,
linii de cod și amprenta memoriei. Cu toate acestea, utilizatorii pot folosi doar câteva dintre aceste module
la un moment dat. Din acest motiv, utilizatorii pot dori să activeze în mod explicit numai subsetul de
posibil ns-3 module de care au nevoie de fapt pentru cercetarea lor.
Acest capitol discută cum să activați numai ns-3 modulele care vă interesează
folosind.
Cum la permite a submult of ns-3's module
Dacă se construiesc biblioteci partajate, atunci activarea unui modul va provoca cel puțin unul
biblioteca de construit:
libns3-modulename.so
Dacă modulul are o bibliotecă de testare și se construiesc biblioteci de testare, atunci
libns3-modulename-test.so
va fi construit, de asemenea. Alte module de care depinde modulul și bibliotecile lor de testare
va fi de asemenea construit.
În mod implicit, toate modulele sunt încorporate ns-3. Există două moduri de a activa un subset al acestora
module:
1. Folosind opțiunea --enable-modules de la waf
2. Folosind ns-3 Fișier de configurare
Permite module folosind waf-uri --activare-module opțiune
Pentru a activa numai modulul de bază cu exemple și teste, de exemplu, încercați aceste comenzi:
$ ./waf curat
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
$ ./waf build
$ cd build/debug/
$ls
iar următoarele biblioteci ar trebui să fie prezente:
legături libns3-core.so ns3 scratch utils
exemple libns3-core-test.so mostre src
Rețineți ./waff curat pasul se face aici doar pentru a face mai evident ce biblioteci de module
au fost construite. Nu trebuie să faci ./waff curat pentru a activa subseturi de module.
Rularea test.py va determina rularea numai acele teste care depind de nucleul modulului:
24 din 24 teste trecute (24 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Repetați pașii de mai sus pentru modulul „rețea” în loc de modulul „nucleu” și
vor fi construite următoarele, deoarece rețeaua depinde de nucleu:
legături libns3-core.so libns3-network.so ns3 scratch utils
exemple libns3-core-test.so libns3-network-test.so mostre src
Rularea test.py va determina acele teste care depind doar de modulele de bază și de rețea
fi condus:
31 din 31 teste trecute (31 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Permite module folosind il ns-3 configuraţie fişier
Un fișier de configurare, .ns3rc, a fost adăugat la ns-3 care permite utilizatorilor să specifice care
modulele vor fi incluse în build.
La activarea unui subset de ns-3 module, regulile de prioritate sunt următoarele:
1. șirul de configurare --enable-modules suprascrie orice fișier .ns3rc
2. fișierul .ns3rc la nivelul superior ns-3 directorul este consultat în continuare, dacă este prezent
3. sistemul caută ~/.ns3rc dacă cele două de mai sus sunt nespecificate
Dacă niciuna dintre cele de mai sus nu limitează modulele care urmează să fie construite, toate modulele despre care waf știe vor fi
fi construit.
Versiunea menținută a fișierului .ns3rc în fișierul ns-3 depozitul de cod sursă se află în
il utils director. Motivul pentru aceasta este dacă ar fi în directorul de nivel superior al
depozit, ar fi predispus la verificări accidentale de la întreținerii care activează
modulele pe care doresc să le folosească. Prin urmare, utilizatorii trebuie să copieze manual fișierul .ns3rc din
utils director la locul lor preferat (directorul de nivel superior sau directorul lor principal) la
activați configurația de construcție modulară persistentă.
Presupunând că sunteți la nivelul superior ns-3 directorul, puteți obține o copie a fișierului .ns3rc
fișierul care se află în utils director după cum urmează:
$ cp utils/.ns3rc .
Fișierul .ns3rc ar trebui să fie acum la nivelul superior ns-3 director și conține fișierul
Următor:
#! /usr/bin/env piton
# O listă a modulelor care vor fi activate atunci când ns-3 este rulat.
# Modulele care depind de modulele enumerate vor fi, de asemenea, activate.
#
# Toate modulele pot fi activate alegând „all_modules”.
modules_enabled = ['toate_modulele']
# Setați acest lucru egal cu adevărat dacă doriți ca exemple să fie executate.
examples_enabled = False
# Setați acest lucru egal cu adevărat dacă doriți ca testele să fie executate.
tests_enabled = Fals
Utilizați editorul preferat pentru a modifica fișierul .ns3rc pentru a activa numai modulul de bază cu
exemple si teste de genul acesta:
#! /usr/bin/env piton
# O listă a modulelor care vor fi activate atunci când ns-3 este rulat.
# Modulele care depind de modulele enumerate vor fi, de asemenea, activate.
#
# Toate modulele pot fi activate alegând „all_modules”.
modules_enabled = ['core']
# Setați acest lucru egal cu adevărat dacă doriți ca exemple să fie executate.
examples_enabled = Adevărat
# Setați acest lucru egal cu adevărat dacă doriți ca testele să fie executate.
tests_enabled = Adevărat
Numai modulul de bază va fi activat acum dacă încercați aceste comenzi:
$ ./waf curat
$ ./waf configurați
$ ./waf build
$ cd build/debug/
$ls
iar următoarele biblioteci ar trebui să fie prezente:
legături libns3-core.so ns3 scratch utils
exemple libns3-core-test.so mostre src
Rețineți ./waff curat pasul se face aici doar pentru a face mai evident ce biblioteci de module
au fost construite. Nu trebuie să faci ./waff curat pentru a activa subseturi de module.
Rularea test.py va determina rularea numai acele teste care depind de nucleul modulului:
24 din 24 teste trecute (24 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Repetați pașii de mai sus pentru modulul „rețea” în loc de modulul „nucleu” și
vor fi construite următoarele, deoarece rețeaua depinde de nucleu:
legături libns3-core.so libns3-network.so ns3 scratch utils
exemple libns3-core-test.so libns3-network-test.so mostre src
Rularea test.py va determina acele teste care depind doar de modulele de bază și de rețea
fi condus:
31 din 31 teste trecute (31 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Activare/dezactivare ns-3 Teste și Exemple
ns-3 distribuția include multe exemple și teste care sunt folosite pentru a valida ns-3
sistem. Cu toate acestea, utilizatorii ar putea să nu-și dorească întotdeauna ca aceste exemple și teste să fie executate pentru ei
instalarea ns-3.
Acest capitol discută cum să construiți ns-3 cu sau fără exemplele și testele sale.
Cum la permite dezactivarea exemple și teste in ns-3
Există 3 moduri de a activa/dezactiva exemplele și testele în ns-3:
1. Folosind build.py când ns-3 este construit pentru prima dată
2. Folosind waf o dată ns-3 a fost construit
3. Folosind ns-3 fișierul de configurare o dată ns-3 a fost construit
Permite dezactivarea exemple și teste folosind construi.py
Puteți folosi build.py pentru a activa/dezactiva exemplele și testele când ns-3 este construit pentru primul
timp.
În mod implicit, exemplele și testele nu sunt încorporate ns-3.
Din directorul ns-3-allinone, puteți construi ns-3 fără exemple sau teste pur și simplu
facand:
$ ./build.py
Rularea test.py la nivelul superior ns-3 directorul acum nu va face ca exemple sau teste să fie
alerga:
0 din 0 teste trecute (0 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Dacă doriți să construiți ns-3 cu exemple și teste, apoi faceți următoarele din
Directorul ns-3-allinone:
$ ./build.py --enable-examples --enable-tests
Rularea test.py la nivelul superior ns-3 directorul va provoca toate exemplele și testele
a fi condus:
170 din 170 teste trecute (170 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Permite dezactivarea exemple și teste folosind WAF
Puteți folosi waf pentru a activa/dezactiva exemplele și testele o dată ns-3 a fost construit.
În mod implicit, exemplele și testele nu sunt încorporate ns-3.
De la nivelul superior ns-3 director, puteți construi ns-3 fără exemple sau teste pur și simplu
facand:
$ ./waf configurați
$ ./waf build
Rularea test.py acum nu va face ca exemple sau teste să fie executate:
0 din 0 teste trecute (0 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Dacă doriți să construiți ns-3 cu exemple și teste, apoi faceți următoarele de sus
nivel ns-3 director:
$ ./waf configure --enable-examples --enable-tests
$ ./waf build
Rularea test.py va face ca toate exemplele și testele să fie rulate:
170 din 170 teste trecute (170 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Permite dezactivarea exemple și teste folosind il ns-3 configuraţie fişier
Un fișier de configurare, .ns3rc, a fost adăugat la ns-3 care permite utilizatorilor să specifice dacă
exemplele și testele ar trebui construite sau nu. Puteți utiliza acest fișier pentru a activa/dezactiva
exemple și teste o dată ns-3 a fost construit.
Când activați dezactivarea exemplelor și a testelor, regulile de prioritate sunt următoarele:
1. șirurile de configurare --enable-examples/--disable-examples suprascriu orice fișier .ns3rc
2. șirurile de configurare --enable-tests/--disable-tests anulează orice fișier .ns3rc
3. fișierul .ns3rc la nivelul superior ns-3 directorul este consultat în continuare, dacă este prezent
4. sistemul caută ~/.ns3rc dacă fișierul .ns3rc nu a fost găsit la pasul anterior
Dacă niciunul dintre cele de mai sus nu există, atunci exemplele și testele nu vor fi construite.
Versiunea menținută a fișierului .ns3rc în fișierul ns-3 depozitul de cod sursă se află în
il utils director. Motivul pentru aceasta este dacă ar fi în directorul de nivel superior al
depozit, ar fi predispus la verificări accidentale de la întreținerii care activează
modulele pe care doresc să le folosească. Prin urmare, utilizatorii trebuie să copieze manual fișierul .ns3rc din
utils director la locul lor preferat (directorul de nivel superior sau directorul lor principal) la
permite activarea persistentă a exemplelor și a testelor.
Presupunând că sunteți la nivelul superior ns-3 directorul, puteți obține o copie a fișierului .ns3rc
fișierul care se află în utils director după cum urmează:
$ cp utils/.ns3rc .
Fișierul .ns3rc ar trebui să fie acum la nivelul superior ns-3 director și conține fișierul
Următor:
#! /usr/bin/env piton
# O listă a modulelor care vor fi activate atunci când ns-3 este rulat.
# Modulele care depind de modulele enumerate vor fi, de asemenea, activate.
#
# Toate modulele pot fi activate alegând „all_modules”.
modules_enabled = ['toate_modulele']
# Setați acest lucru egal cu adevărat dacă doriți ca exemple să fie executate.
examples_enabled = False
# Setați acest lucru egal cu adevărat dacă doriți ca testele să fie executate.
tests_enabled = Fals
De la nivelul superior ns-3 director, puteți construi ns-3 fără exemple sau teste pur și simplu
facand:
$ ./waf configurați
$ ./waf build
Rularea test.py acum nu va face ca exemple sau teste să fie executate:
0 din 0 teste trecute (0 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Dacă doriți să construiți ns-3 cu exemple și teste, folosește editorul tău preferat pentru a schimba
valorile din fișierul .ns3rc pentru fișierul examples_enabled și tests_enabled să fie adevărate:
#! /usr/bin/env piton
# O listă a modulelor care vor fi activate atunci când ns-3 este rulat.
# Modulele care depind de modulele enumerate vor fi, de asemenea, activate.
#
# Toate modulele pot fi activate alegând „all_modules”.
modules_enabled = ['toate_modulele']
# Setați acest lucru egal cu adevărat dacă doriți ca exemple să fie executate.
examples_enabled = Adevărat
# Setați acest lucru egal cu adevărat dacă doriți ca testele să fie executate.
tests_enabled = Adevărat
De la nivelul superior ns-3 director, puteți construi ns-3 cu exemple și teste pur și simplu prin
face:
$ ./waf configurați
$ ./waf build
Rularea test.py va face ca toate exemplele și testele să fie rulate:
170 din 170 teste trecute (170 trecut, 0 omis, 0 eșuat, 0 blocat, 0 erori valgrind)
Depanare
Acest capitol postează câteva informații despre posibilele erori comune la crearea sau rularea
ns-3 programe.
Vă rugăm să rețineți că wiki (http://www.nsnam.org/wiki/Troubleshooting) poate să fi contribuit
articole.
Construi Erori
Rulează Erori
Uneori, pot apărea erori cu un program după o construcție reușită. Acestea sunt timpul de rulare
erori și pot apărea în mod obișnuit atunci când memoria este coruptă sau valorile pointerului sunt neașteptate
nul.
Iată un exemplu a ceea ce s-ar putea întâmpla:
$ ./waf --run tcp-punct-la-punct
Se introduce în directorul „/home/tomh/ns-3-nsc/build”
Compilarea sa încheiat cu succes
Comanda ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] a ieșit cu codul -11
Mesajul de eroare spune că programul s-a încheiat fără succes, dar nu este clar
din aceste informații ce ar putea fi greșit. Pentru a examina mai îndeaproape, încercați să îl rulați sub
il gDB debugger:
$ ./waf --run tcp-point-to-point --command-template="gdb %s"
Se introduce în directorul „/home/tomh/ns-3-nsc/build”
Compilarea sa încheiat cu succes
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Copyright 2004 Free Software Foundation, Inc.
GDB este un software gratuit, acoperit de Licența publică generală GNU, iar dvs. sunteți
bineveniți să îl modificați și/sau să distribuiți copii ale acestuia în anumite condiții.
Tastați „afișați copierea” pentru a vedea condițiile.
Nu există absolut nicio garanție pentru GDB. Introduceți „afișați garanția” pentru detalii.
Acest GDB a fost configurat ca „i386-redhat-linux-gnu”... Folosind gazda libthread_db
biblioteca „/lib/libthread_db.so.1”.
(gdb) alerga
Program de pornire: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Citirea simbolurilor din obiectul partajat citit din memoria țintă... gata.
Sistemul încărcat a furnizat DSO la 0xf5c000
Semnal recepţionat program SIGSEGV, Eroare de segmentare.
0x0804aa12 în principal (argc=1, argv=0xbfdfefa4)
la ../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) renunță
Programul rulează. Ieși oricum? (y sau n) y
Rețineți mai întâi modul în care programul a fost invocat -- treceți comanda pentru a rula ca argument la
șablon de comandă „gdb %s”.
Acest lucru ne spune că a existat o încercare de a anula referința unui pointer nul socketFactory.
Să ne uităm în jurul liniei 136 din tcp-point-to-point, așa cum sugerează gdb:
Ptr socketFactory = n2->GetObject (Tcp::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();
Vinovatul aici este că valoarea returnată a GetObject nu este verificată și poate fi
nul.
Uneori poate fi necesar să utilizați valgrind memorie Checker pentru erori mai subtile. Din nou,
invocați utilizarea valgrind în mod similar:
$ ./waf --run tcp-point-to-point --command-template="valgrind %s"
SOURCE
Acest document este scris în reStructuredText pentru Sfinx și se menține în
document/manual directorul codului sursă al lui ns-3.
Utilizați ns-3-manual online folosind serviciile onworks.net
