GoGPT Best VPN GoSearch

Ulubiona usługa OnWorks

ns-3-manual - Online w chmurze

Uruchom ns-3-manual w darmowym dostawcy hostingu OnWorks przez Ubuntu Online, Fedora Online, emulator online Windows lub emulator online MAC OS

To jest polecenie ns-3-manual, które można uruchomić w darmowym dostawcy usług hostingowych OnWorks przy użyciu jednej z wielu naszych bezpłatnych stacji roboczych online, takich jak Ubuntu Online, Fedora Online, emulator online systemu Windows lub emulator online MAC OS

PROGRAM:

IMIĘ


ns-3-instrukcja - ns-3 instrukcja

To jest ns-3 Instrukcja obsługi. Podstawowa dokumentacja dla projektu ns-3 jest dostępna w pięciu
formy:

· ns-3 doxygen: Dokumentacja publicznych interfejsów API symulatora

· Samouczek, podręcznik (to dokument)i Biblioteka modeli dla firmy zwolnić oraz
rozwój drzewo

· ns-3 wiki

SPIS TREŚCI


Organizacja
W tym rozdziale opisano całość ns-3 organizacja oprogramowania i odpowiednie
organizację tego podręcznika.

ns-3 to symulator sieci zdarzeń dyskretnych, w którym znajduje się rdzeń symulacji i modele
zaimplementowane w C++. ns-3 jest zbudowany jako biblioteka, która może być statyczna lub dynamiczna
połączony z głównym programem C++, który definiuje topologię symulacji i uruchamia
symulator. ns-3 eksportuje również prawie cały swój interfejs API do Pythona, umożliwiając programom Pythona
zaimportuj moduł „ns3” w taki sam sposób, jak moduł ns-3 biblioteka jest połączona przez pliki wykonywalne
w C++.
[zdjęcie] Organizacja oprogramowania ns-3.NIEZDECYDOWANY

Kod źródłowy dla ns-3 zorganizowana jest głównie w src katalogu i można je opisać
według schematu w Oprogramowanie organizacja of ns-3. Będziemy pracować od dołu
w górę; ogólnie moduły mają zależności tylko od modułów znajdujących się pod nimi na rysunku.

Najpierw opisujemy rdzeń symulatora; te elementy, które są wspólne dla wszystkich
protokoły, sprzęt i modele środowiskowe. Rdzeń symulacji jest zaimplementowany w
źródło/rdzeń. Pakiety są podstawowymi obiektami w symulatorze sieci i są implementowane w
źródło/sieć. Te dwa moduły symulacyjne same w sobie mają obejmować:
ogólny rdzeń symulacji, który może być używany przez różne rodzaje sieci, nie tylko
Sieci oparte na Internecie. Powyższe moduły dot ns-3 są niezależne od konkretnej sieci
i modeli urządzeń, które zostały omówione w dalszych częściach niniejszej instrukcji.

W uzupełnieniu do powyższego ns-3 rdzeń, wprowadzamy, również w początkowej części
manual, dwa inne moduły, które uzupełniają podstawowy interfejs API oparty na C++. ns-3 programy mogą
uzyskać bezpośredni dostęp do wszystkich API lub skorzystać z tzw pomocnik API zapewniający
wygodne opakowania lub enkapsulacja wywołań API niskiego poziomu. Fakt, że ns-3 programów
można zapisać w dwóch interfejsach API (lub ich kombinacji) jest podstawowym aspektem
symulator. Opisujemy również, w jaki sposób Python jest obsługiwany w ns-3 przed przejściem do konkretów
modele mające znaczenie dla symulacji sieci.

Pozostała część podręcznika koncentruje się na dokumentowaniu modeli i wspieraniu
możliwości. W kolejnej części skupiono się na dwóch podstawowych obiektach w ns-3: the Node oraz
Urządzenie sieciowe. Dwa specjalne typy urządzeń NetDevice są przeznaczone do obsługi emulacji sieci
przypadkach, a emulacja jest opisana poniżej. Poniższy rozdział jest poświęcony
Modele związane z Internetem, w tym interfejs API gniazd używany przez aplikacje internetowe. The
następny rozdział dotyczy aplikacji, a następny rozdział opisuje dodatkowe wsparcie
do symulacji, takich jak animatorzy i statystyki.

W ramach projektu prowadzony jest odrębny podręcznik poświęcony testowaniu i walidacji ns-3 kod
(Patrz ns-3 Testy oraz Walidacja podręcznik).

Losowy Zmienne
ns-3 zawiera wbudowany generator liczb pseudolosowych (PRNG). Jest to ważne dla
poważnych użytkowników symulatora, aby zrozumieć jego funkcjonalność, konfigurację i użytkowanie
tego PRNG i zdecydować, czy jest on wystarczający do jego celów badawczych.

Szybki Omówienie
ns-3 liczby losowe są dostarczane za pośrednictwem instancji ns3::RandomVariableStream.

· domyślnie, ns-3 symulacje wykorzystują stałe ziarno; jeśli jest jakaś losowość w
symulacji, każde uruchomienie programu da identyczne wyniki, chyba że ziarno i/lub
numer biegu jest zmieniony.

· W ns-3.3 i wcześniej, ns-3 symulacje domyślnie wykorzystywały losowe ziarno; to oznacza A
zmiana zasad począwszy od ns-3.4.

· W ns-3.14 i wcześniej, ns-3 symulacje wykorzystywały inną klasę opakowania o nazwie
ns3::Zmienna losowa. Od dnia ns-3.15, ta klasa została zastąpiona przez
ns3::RandomVariableStream; bazowy generator liczb pseudolosowych nie
zmianie.

· aby uzyskać losowość w wielu przebiegach symulacji, należy albo ustawić ziarno
inaczej lub ustaw numer biegu inaczej. Aby zasiać ziarno, zadzwoń
ns3::RngSeedManager::SetSeed() na początku programu; ustawić numer biegu za pomocą
to samo ziarno, zawołanie ns3::RngSeedManager::SetRun() na początku programu; Widzieć
Tworzenie przypadkowy zmienne.

· każdy RandomVariableStream użyty w ns-3 ma powiązany wirtualny generator liczb losowych
z tym; wszystkie zmienne losowe używają stałego lub losowego materiału siewnego w oparciu o użycie
globalny materiał siewny (poprzedni punkt);

· jeśli zamierzasz wykonać wiele przebiegów tego samego scenariusza, z różnymi losami
numerów, koniecznie przeczytaj sekcję dotyczącą wykonywania niezależnych replikacji:
Tworzenie przypadkowy zmienne.

Czytaj dalej, aby uzyskać więcej wyjaśnień na temat funkcji liczb losowych dla ns-3.

Tło
Symulacje wykorzystują wiele liczb losowych; jedno badanie wykazało, że większość symulacji sieciowych
wydać aż 50% procesora na generowanie liczb losowych. Użytkownicy symulacji muszą być
dotyczy jakości (pseudo) liczb losowych i niezależności między nimi
różne strumienie liczb losowych.

Użytkownicy muszą być zaniepokojeni kilkoma kwestiami, takimi jak:

· zaszczepianie generatora liczb losowych i czy wynik symulacji jest
deterministyczny czy nie,

· jak pozyskiwać różne niezależne od siebie strumienie liczb losowych
inny i

· jak długo trwa cykl strumieni

Wprowadzimy tutaj kilka terminów: RNG zapewnia długą sekwencję (pseudo)losową
liczby. Długość tego ciągu nazywa się cykl długość or okres, po czym
RNG się powtórzy. Sekwencję tę można podzielić na rozłączną Strumienie, ZA
strumień RNG jest ciągłym podzbiorem lub blokiem sekwencji RNG. Na przykład, jeśli
Okres RNG ma długość N, az tego RNG dostarczane są dwa strumienie, a następnie pierwszy
strumień może wykorzystywać pierwsze wartości N/2, a drugi strumień może generować drugie N/2
wartości. Ważną właściwością jest tutaj to, że te dwa strumienie są nieskorelowane. Podobnie,
każdy strumień można podzielić rozłącznie na pewną liczbę nieskorelowanych podstrumienie,
miejmy nadzieję, że podstawowe RNG tworzy pseudolosową sekwencję liczb z bardzo długim
długości cyklu i efektywnie dzieli ją na strumienie i podstrumienie.

ns-3 używa tego samego podstawowego generatora liczb losowych, co robi ns-2: MRG32k3a
generator firmy Pierre L'Ecuyer. Szczegółowy opis znajduje się w
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. Generator MRG32k3a
zapewnia 1.8x10^{19} niezależnych strumieni liczb losowych, z których każdy się składa
2.3x10^{15} strumieni podrzędnych. Każdy podstrumień ma kropkę (tj., liczba liczb losowych
przed nałożeniem) 7.6x10^{22}. Okres całego generatora wynosi 3.1x10^{57}.

Klasa ns3::RandomVariableStream jest interfejsem publicznym dla tej bazowej liczby losowej
generator. Gdy użytkownicy tworzą nowe zmienne losowe (np ns3::UniformLosowazmienna,
ns3::WykładniczaZmienna losowa, itp.), tworzą obiekt, który używa jednego z
odrębne, niezależne strumienie generatora liczb losowych. Dlatego każdy przedmiot z
rodzaj ns3::RandomVariableStream ma, koncepcyjnie, swój własny „wirtualny” RNG. Ponadto,
każdy ns3::RandomVariableStream można skonfigurować tak, aby korzystał z jednego z narysowanych zestawów strumieni podrzędnych
z głównego strumienia.

Alternatywną implementacją byłoby zezwolenie każdej zmiennej RandomVariable na posiadanie własnej
(różnie rozstawione) RNG. Nie możemy jednak zagwarantować tak mocno, że różne
w takim przypadku sekwencje byłyby nieskorelowane; dlatego wolimy używać pojedynczego RNG i
z niego strumienie i podstrumienie.

Tworzenie przypadkowy zmienne
ns-3 obsługuje wiele losowych obiektów zmiennych z klasy bazowej
Strumień losowej zmiennej. Przedmioty te wywodzą się z ns3::Obiekt i są obsługiwane przez smart
wskaźniki.

Właściwym sposobem tworzenia tych obiektów jest użycie szablonu UtwórzObiekt<> metoda
takich jak:

Ptr x = Utwórz obiekt ();

następnie możesz uzyskać dostęp do wartości, wywołując metody na obiekcie, takie jak:

myRandomNo = x->GetInteger();

Jeśli zamiast tego spróbujesz zrobić coś takiego:

myRandomNo = UniformRandomVariable().GetInteger();

Twój program napotka błąd segmentacji, ponieważ implementacja polega na
pewna konstrukcja atrybutu, która występuje tylko wtedy, gdy Utwórz obiekt nazywa się.

Znaczna część pozostałej części tego rozdziału omawia teraz właściwości strumienia
liczby pseudolosowe generowane z takich obiektów i jak kontrolować ich rozsiewanie
obiekty.

Siew oraz niezależny replikacje
ns-3 symulacje można skonfigurować tak, aby generowały wyniki deterministyczne lub losowe. jeśli
ns-3 symulacja jest skonfigurowana do używania stałego, deterministycznego materiału siewnego o tym samym numerze przebiegu,
powinien dawać ten sam wynik za każdym razem, gdy jest uruchamiany.

Domyślnie ns-3 symulacje wykorzystują stały numer początkowy i roboczy. Te wartości są przechowywane w
drugiej ns3::GlobalValue instancje: g_rngNasiono oraz g_rngUruchom.

Typowym przypadkiem użycia jest uruchomienie symulacji jako sekwencji niezależnych prób, tak aby
obliczać statystyki dla dużej liczby niezależnych przebiegów. Użytkownik może albo zmienić
globalny materiał siewny i ponownie uruchom symulację lub może przyspieszyć stan podstrumienia RNG, który
jest określane jako zwiększanie numeru przebiegu.

Klasa ns3::RngSeedManager zapewnia interfejs API do kontrolowania inicjowania i numeru uruchomienia
zachowanie. To ustawienie inicjowania i stanu strumienia podrzędnego musi zostać wywołane przed jakimkolwiek random
tworzone są zmienne; np:

RngSeedManager::SetSeed (3); // Zmienia seed z domyślnej wartości 1 na 3
RngSeedManager::SetRun (7); // Zmienia numer uruchomienia z domyślnego 1 na 7
// Teraz utwórz zmienne losowe
Ptr x = Utwórz obiekt ();
Ptr y = UtwórzObiekt ();
...

Co jest lepsze, ustawienie nowego materiału siewnego lub postęp w stanie podstrumienia? Nie ma
gwarantują, że strumienie wytwarzane przez dwa losowe nasiona nie będą się pokrywać. Jedyny sposób na
Aby zagwarantować, że dwa strumienie nie będą się nakładać, należy użyć funkcji strumienia podrzędnego zapewnianej przez
wdrożenie RNG. Dlatego posługiwać się dotychczasowy podstrumień zdolność do produkować wielokrotność
niezależny działa of dotychczasowy taki sam symulacja. Innymi słowy, bardziej rygorystyczne statystycznie
sposobem na skonfigurowanie wielu niezależnych replikacji jest użycie stałego materiału siewnego i postęp
numer biegu. Ta implementacja pozwala na maksymalnie 2.3x10^{15} niezależnych
replikacje przy użyciu strumieni podrzędnych.

Dla ułatwienia obsługi nie jest konieczne kontrolowanie nasion i numeru serii z poziomu programu
program; użytkownik może ustawić NS_GLOBAL_VALUE zmienną środowiskową w następujący sposób:

$ NS_GLOBAL_VALUE="RngRun=3" ./waf --uruchom nazwa-programu

Innym sposobem kontrolowania tego jest przekazanie argumentu wiersza poleceń; ponieważ jest to ns-3
GlobalValue, jest to równoważne w następujący sposób:

$./waf --command-template="%s --RngRun=3" --uruchom nazwa-programu

lub, jeśli uruchamiasz programy bezpośrednio poza waf:

$ ./build/optimized/scratch/nazwa-programu --RngRun=3

Powyższe warianty wiersza poleceń ułatwiają uruchamianie wielu różnych uruchomień z poziomu powłoki
script, po prostu przekazując inny indeks RngRun.

Klasa Strumień losowej zmiennej
Wszystkie zmienne losowe powinny pochodzić z klasy Zmienna losowa. Ta klasa bazowa zapewnia
kilka metod globalnej konfiguracji zachowania generatora liczb losowych. Pochodny
klasy udostępniają interfejs API do rysowania losowych zmiennych z danej dystrybucji
utrzymany.

Każdy RandomVariableStream utworzony w symulacji otrzymuje nowy generator
RNGStream z podstawowego PRNG. Użyta w ten sposób implementacja L'Ecuyera
pozwala na maksymalnie 1.8x10^19 zmiennych losowych. Każda zmienna losowa w jednym
replikacja może wytworzyć do 7.6x10^22 liczb losowych przed nałożeniem.

Baza klasa publiczny API
Poniżej znajduje się fragment kilku publicznych metod klasy Strumień losowej zmiennej które mają dostęp do
następna wartość w strumieniu podrzędnym.

/ **
* \brief Zwraca losową podwójność z podstawowego rozkładu
* \return Losowa wartość zmiennoprzecinkowa
*/
double GetValue (void) const;

/ **
* \brief Zwraca losową liczbę całkowitą z podstawowego rozkładu
* \return Rzut całkowity ::GetValue()
*/
uint32_t GetInteger (pusta) stała;

Opisaliśmy już konfigurację inicjowania powyżej. Inna zmienna losowa
podklasy mogą mieć dodatkowe API.

rodzaje of Zmienne losowe
Dostępne są następujące typy zmiennych losowych i są one udokumentowane w ns-3
Doxygen lub przez czytanie src/core/model/random-variable-stream.h. Użytkownicy mogą również tworzyć
własne niestandardowe zmienne losowe, wywodząc się z klasy Strumień losowej zmiennej.

· klasa UniformLosowazmienna

· klasa StałaLosowazmienna

· klasa Sekwencyjnazmienna losowa

· klasa Wykładniczazmienna losowa

· klasa Zmienna losowa Pareto

· klasa Zmienna losowa Weibulla

· klasa NormalnaLosowazmienna

· klasa LogNormalLosowazmienna

· klasa GammaZmienna losowa

· klasa ErlangZmienna losowa

· klasa TrójkątnaLosowazmienna

· klasa ZipfZmienna losowa

· klasa ZetaZmienna losowa

· klasa Deterministycznazmienna losowa

· klasa EmpirycznaZmiennaLosowa

Semantyka of Strumień losowej zmiennej obiekty
Obiekty RandomVariableStream wywodzą się z ns3::Obiekt i są obsługiwane przez inteligentne wskaźniki.

Instancje RandomVariableStream mogą być również używane w ns-3 atrybuty, co oznacza, że
wartości można dla nich ustawić za pomocą ns-3 system atrybutów. Przykład jest w
modele propagacji dla WifiNetDevice:

TypId
RandomPropagationDelayModel::GetTypeId (void)
{
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
.Ustaw Rodzica ()
.AddConstructor ()
.AddAttribute("Zmienna",
"Zmienna losowa, która generuje losowe opóźnienia (s).",
StringValue ("ns3::UniformRandomVariable"),
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
MakePointerChecker ())
;
pora powrotu;
}

Tutaj ns-3 użytkownik może zmienić domyślną zmienną losową dla tego modelu opóźnienia (tzn
UniformRandomVariable w zakresie od 0 do 1) poprzez system atrybutów.

Korzystanie z Inne PRNG
Obecnie nie ma wsparcia dla zastąpienia innej bazowej liczby losowej
generator (np. GNU Scientific Library lub pakiet Akaroa). Patche mile widziane.

Oprawa dotychczasowy strumień numer
Bazowy generator MRG32k3a zapewnia 2^64 niezależnych strumieni. W ns-3 są to
przypisywane sekwencyjnie, począwszy od pierwszego strumienia, jako nowe instancje RandomVariableStream
wykonać swoje pierwsze wywołanie funkcji GetValue().

W wyniku sposobu, w jaki te obiekty RandomVariableStream są przypisywane do podstawowych strumieni,
przypisanie jest wrażliwe na zakłócenia konfiguracji symulacji. The
konsekwencją jest to, że jeśli jakikolwiek aspekt konfiguracji symulacji zostanie zmieniony, mapowanie
RandomVariables do strumieni może (ale nie musi) ulec zmianie.

Konkretnym przykładem może być użytkownik przeprowadzający badanie porównawcze między protokołami routingu
okaże się, że akt zmiany jednego protokołu routingu na inny zauważy, że
podstawowy wzorzec mobilności również się zmienił.

Począwszy od ns-3.15, udostępniono użytkownikom pewną kontrolę, aby umożliwić użytkownikom
opcjonalnie napraw przypisanie wybranych obiektów RandomVariableStream do bazowych
strumienie. To jest Strumień atrybut, część klasy bazowej RandomVariableStream.

Dzieląc istniejącą wcześniej sekwencję strumieni:

<---------------------------------------------------------------- ---------->
strumień 0 strumień (2^64 - 1)

na dwa równej wielkości zestawy:

<---------------------------------------------------------------- ---------->
^^ ^
| || |
strumień 0 strumień (2^63 - 1) strumień 2^63 strumień (2^64 - 1)
<- przypisane automatycznie --------------------------><- przypisane przez użytkownika -->

Pierwsze 2^63 strumienie są nadal przypisywane automatycznie, a ostatnie 2^63 są
podane indeksy strumienia zaczynające się od zera do 2^63-1.

Przypisanie strumieni do stałego numeru strumienia jest opcjonalne; przypadki
RandomVariableStream, które nie mają przypisanej wartości strumienia, otrzymają następną
jeden z puli automatycznych strumieni.

Aby naprawić RandomVariableStream do określonego strumienia bazowego, przypisz jego Strumień
atrybut na nieujemną liczbę całkowitą (wartość domyślna -1 oznacza, że ​​wartość będzie
przydzielane automatycznie).

Wydawniczy Twój wyniki
Gdy publikujesz wyniki symulacji, kluczowa informacja o konfiguracji, którą ty
zawsze należy podać, w jaki sposób użyłeś generatora liczb losowych.

· jakich nasion użyłeś,

· jakiego RNG użyłeś, jeśli nie domyślnego,

· jak przebiegały niezależne biegi,

· w przypadku dużych symulacji, w jaki sposób sprawdziłeś, czy nie jeździłeś na rowerze.

Na badaczu publikującym wyniki spoczywa obowiązek uwzględnienia wystarczającej ilości informacji
pozwalać innym na powielanie jego wyników. Na badaczu spoczywa również obowiązek
przekonać się, że użyte liczby losowe były statystycznie poprawne, i stwierdzić
artykuł, dlaczego zakłada się taką pewność.

Podsumowanie
Przyjrzyjmy się, co należy zrobić podczas tworzenia symulacji.

· Zdecyduj, czy biegniesz ze stałym, czy losowym ziarnem; stałe ziarno to
domyślna,

· Zdecyduj, jak będziesz zarządzać niezależnymi replikacjami, jeśli dotyczy,

· Przekonaj się, że nie rysujesz więcej losowych wartości niż długość cyklu, jeśli
przeprowadzasz bardzo długą symulację i

· Podczas publikowania postępuj zgodnie z powyższymi wytycznymi dotyczącymi dokumentowania korzystania z random
generator liczb.

Haszysz Funkcje
ns-3 zapewnia ogólny interfejs do funkcji skrótu ogólnego przeznaczenia. W najprostszym
użycia, funkcja skrótu zwraca 32-bitowy lub 64-bitowy skrót bufora danych lub ciągu znaków.
Domyślną bazową funkcją skrótu jest szmer3, wybrany, ponieważ ma dobrą funkcję skrótu
properties i oferuje wersję 64-bitową. Czcigodny FNV1a hash jest również dostępny.

Istnieje prosty mechanizm dodawania (lub dostarczania w czasie wykonywania) alternatywnego skrótu
implementacje funkcji.

Basic Stosowanie
Najprostszym sposobem uzyskania wartości skrótu bufora danych lub łańcucha jest po prostu:

#include "ns3/hash.h"

używając przestrzeni nazw ns3;

znak * bufor = ...
rozmiar_t rozmiar_bufora = ...

uint32_t bufor_hash = Hash32 (bufor, rozmiar_bufora);

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

Równoważne funkcje są zdefiniowane dla 64-bitowych wartości skrótu.

Przyrostowe Hashing
W niektórych sytuacjach przydatne jest obliczenie skrótu wielu buforów, tak jakby miały
zostały połączone. (Na przykład możesz potrzebować skrótu strumienia pakietów, ale nie
chcesz złożyć pojedynczy bufor z połączoną zawartością wszystkich pakietów.)

Jest to prawie tak proste, jak pierwszy przykład:

#include "ns3/hash.h"

używając przestrzeni nazw ns3;

znak * bufor;
rozmiar_t rozmiar_bufora;

haszer haszer; // Użyj domyślnej funkcji skrótu

Do ( )
{
bufor = get_next_buffer ();
hasher (bufor, rozmiar_bufora);
}
uint32_t Combined_hash = hasher. GetHash32 ();

Domyślnie Haszer zachowuje stan wewnętrzny, aby umożliwić przyrostowe mieszanie. Jeśli chcesz
ponownie użyć Haszer obiekt (na przykład dlatego, że jest skonfigurowany z niedomyślnym skrótem
function), ale nie chcesz dodawać do wcześniej obliczonego skrótu, musisz to zrobić jasny()
pierwszy:

hasher.clear ().GetHash32 (bufor, rozmiar_bufora);

Spowoduje to ponowne zainicjowanie stanu wewnętrznego przed haszowaniem bufora.

Korzystanie z an alternatywa Haszysz Funkcjonować
Domyślną funkcją skrótu jest szmer3. FNV1a jest również dostępny. Aby określić skrót
funkcja jawnie, użyj tego konstruktora:

Hasher hasher = Hasher ( Utwórz () );

Dodawanie Nowości Haszysz Funkcjonować Wdrożenia
Aby dodać funkcję skrótu bla, Podążaj za hash-murmur3.h/. DC wzór:

· Utwórz deklarację klasy (.h) i definicja (. DC) dziedziczy po
Hash::Implementacja.

· zawierać deklaracja w hasz.h (w miejscu gdzie hash-murmur3.h jest wliczony w cenę.

· We własnym kodzie utwórz instancję a Haszer obiekt przez konstruktor Haszer
(Ptr ())

Jeśli twoja funkcja skrótu jest pojedynczą funkcją, np haszysz, nie musisz nawet tworzyć pliku
nowa klasa wywodząca się z HashImplementation:

haszer haszer =
Hasher (Utwórz (&hashf) );

Aby to się skompilowało, twój haszysz musi pasować do jednej z sygnatur wskaźnika funkcji:

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

Źródła dla Haszysz Funkcje
Źródła innych implementacji funkcji skrótu obejmują:

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

Wydarzenia oraz Symulator
ns-3 jest symulatorem sieci zdarzeń dyskretnych. Koncepcyjnie symulator śledzi a
liczba zdarzeń, które są zaplanowane do wykonania w określonym czasie symulacji. praca
symulator ma wykonywać zdarzenia w sekwencyjnym porządku czasowym. Po zakończeniu
wystąpi zdarzenie, symulator przejdzie do następnego zdarzenia (lub zakończy działanie, jeśli nie ma zdarzenia
więcej zdarzeń w kolejce zdarzeń). Jeśli, na przykład, zdarzenie zaplanowane na czas symulacji
„100 sekund” jest wykonywane, a następne zdarzenie jest zaplanowane dopiero po „200 sekundach”.
symulator natychmiast przeskoczy ze 100 sekund do 200 sekund (czasu symulacji) do
wykonać następne zdarzenie. To właśnie oznacza symulator „zdarzeń dyskretnych”.

Aby to wszystko się stało, symulator potrzebuje kilku rzeczy:

1. obiekt symulatora, który może uzyskać dostęp do kolejki zdarzeń, w której przechowywane są zdarzenia i który może
zarządzać realizacją wydarzeń

2. harmonogram odpowiedzialny za wstawianie i usuwanie zdarzeń z kolejki

3. sposób reprezentacji czasu symulacji

4. same wydarzenia

Ten rozdział podręcznika opisuje te podstawowe obiekty (symulator, harmonogram,
czas, wydarzenie) i sposób ich wykorzystania.

wydarzenie
Do be zakończony

Symulator
Klasa Simulator jest publicznym punktem wejścia umożliwiającym dostęp do funkcji planowania zdarzeń. Raz
kilka zdarzeń zostało zaplanowanych, aby rozpocząć symulację, użytkownik może rozpocząć
wykonać je, wchodząc w główną pętlę symulatora (tzw Symulator::Uruchom). Kiedyś główna pętla
zacznie działać, sekwencyjnie wykona wszystkie zaplanowane zdarzenia w kolejności od najstarszego do
najnowsze, dopóki w kolejce zdarzeń nie będzie już żadnych zdarzeń lub
Symulator::Wywołano Stop.

Aby zaplanować zdarzenia do wykonania przez główną pętlę symulatora, zapewnia klasa Simulator
rodziny funkcji Simulator::Schedule*.

1. Obsługa procedur obsługi zdarzeń z różnymi sygnaturami

Funkcje te są zadeklarowane i zaimplementowane jako szablony C++ do automatycznej obsługi
szeroką gamę sygnatur obsługi zdarzeń C++ używanych w środowisku naturalnym. Na przykład, aby zaplanować
zdarzenie do wykonania za 10 sekund w przyszłości i wywołaj metodę lub funkcję C++ za pomocą
konkretnych argumentów, możesz napisać tak:

procedura obsługi pustki (int arg0, int arg1)
{
std::cout << "wywołano procedurę obsługi z argumentem arg0=" << arg0 << " i
arg1=" << arg1 << std::endl;
}

Symulator::Harmonogram(Sekund (10), &przewodnik, 10, 5);

Który wyprowadzi:

handler wywołany z argumentem arg0=10 i arg1=5

Oczywiście te szablony C++ mogą również obsługiwać w sposób przejrzysty metody składowe w C++
obiekty:

Do be zakończony: członek metoda przykład

Uwagi:

· Metody harmonogramu ns-3 automatycznie rozpoznają funkcje i metody tylko wtedy, gdy one
weź mniej niż 5 argumentów. Jeśli potrzebujesz ich do poparcia większej liczby argumentów, złóż wniosek a
Zgłoszenie błędu.

· Czytelnicy zaznajomieni z terminem „fuktory w pełni związane” rozpoznają
Simulator::Schedule metody jako sposób na automatyczne konstruowanie takich obiektów.

2. Typowe operacje planowania

Interfejs API symulatora został zaprojektowany tak, aby planowanie większości wydarzeń było naprawdę proste. To
zapewnia trzy warianty, aby to zrobić (uporządkowane od najczęściej używanych do najrzadziej używanych):

· Zaplanuj metody, które pozwalają zaplanować wydarzenie w przyszłości, podając
opóźnienie między bieżącym czasem symulacji a datą wygaśnięcia zdarzenia docelowego.

· Metody ScheduleNow, które pozwalają zaplanować zdarzenie dla bieżącej symulacji
time: wykonają się _po_ zakończeniu wykonywania bieżącego zdarzenia, ale _przed_
czas symulacji zostanie zmieniony dla następnego zdarzenia.

· Metody ScheduleDestroy, które umożliwiają włączenie się w proces zamykania Symulatora
do czyszczenia zasobów symulacji: każde zdarzenie „zniszczenia” jest wykonywane, gdy użytkownik dzwoni
metoda Simulator::Destroy.

3. Utrzymanie kontekstu symulacji

Istnieją dwa podstawowe sposoby planowania wydarzeń: z i bez kontekst. Co to
oznaczać?

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

vs

Symulator::ScheduleWithContext (kontekst uint32_t, czas stały i czas, mem_ptr MEM, obj obj);

Czytelnicy, którzy inwestują czas i wysiłek w opracowanie lub użycie nietrywialnego modelu symulacyjnego
będzie znał wartość środowiska rejestrowania ns-3 do debugowania prostych i złożonych symulacji
zarówno. Jedną z ważnych funkcji zapewnianych przez tę platformę rejestrowania jest
automatyczne wyświetlanie identyfikatora węzła sieciowego powiązanego z aktualnie trwającym zdarzeniem.

Identyfikator aktualnie wykonywanego węzła sieciowego jest w rzeczywistości śledzony przez Symulator
klasa. Można uzyskać do niego dostęp za pomocą metody Simulator::GetContext, która zwraca plik
„kontekst” (32-bitowa liczba całkowita) powiązany i przechowywany w aktualnie wykonywanym zdarzeniu. W
rzadkich przypadkach, gdy zdarzenie nie jest związane z określonym węzłem sieci, jego
„kontekst” jest ustawiony na 0xffffffff.

Aby skojarzyć kontekst z każdym zdarzeniem, metody Schedulei ScheduleNow automatycznie
ponownie użyj kontekstu aktualnie wykonywanego zdarzenia jako kontekstu zaplanowanego zdarzenia
do późniejszej egzekucji.

W niektórych przypadkach, zwłaszcza podczas symulacji transmisji pakietu z węzła do
inny, to zachowanie jest niepożądane, ponieważ oczekiwany kontekst zdarzenia przyjęcia jest taki
węzeł odbierający, a nie węzeł wysyłający. Aby uniknąć tego problemu, symulator
class zapewnia określoną metodę harmonogramu: ScheduleWithContext, która pozwala na zapewnienie
wyraźnie identyfikator węzła odbierającego powiązanego ze zdarzeniem odbierania.

XXX: kod przykład

W bardzo rzadkich przypadkach programiści mogą potrzebować zmodyfikować lub zrozumieć, w jaki sposób kontekst
(identyfikator węzła) pierwszego zdarzenia jest ustawiony na identyfikator powiązanego z nim węzła. To jest osiągnięte
przez klasę NodeList: za każdym razem, gdy tworzony jest nowy węzeł, klasa NodeList używa
ScheduleWithContext, aby zaplanować zdarzenie „inicjowania” dla tego węzła. Zdarzenie „inicjalizuj”.
w ten sposób wykonuje się z kontekstem ustawionym na identyfikator węzła i może używać normalnej odmiany
Zaplanuj metody. Wywołuje metodę Node::Initialize, która propaguje metodę „inicjowania”
zdarzenie przez wywołanie metody DoInitialize dla każdego obiektu powiązanego z węzłem. The
Metoda DoInitialize została zastąpiona w niektórych z tych obiektów (przede wszystkim w Application
klasa bazowa) zaplanuje niektóre zdarzenia (w szczególności Application::StartApplication), które
z kolei zaplanuje zdarzenia generujące ruch, które z kolei zaplanują
zdarzenia na poziomie sieci.

Uwagi:

· Użytkownicy muszą uważać, aby propagować metody DoInitialize między obiektami przez wywołanie
Zainicjuj jawnie na swoich obiektach członkowskich

· Identyfikator kontekstu powiązany z każdą metodą ScheduleWithContext ma poza tym inne zastosowania
rejestrowanie: jest używane przez eksperymentalną gałąź ns-3 do przeprowadzania symulacji równoległych
systemy wielordzeniowe wykorzystujące wielowątkowość.

Funkcje Simulator::* nie znają kontekstu: po prostu to zapewniają
jakikolwiek kontekst określony za pomocą ScheduleWithContext jest dostępny, gdy odpowiedni
zdarzenie jest wykonywane z ::GetContext.

Interpretacja wartości kontekstu zależy od modeli zaimplementowanych na Simulator::*.
W ns-3 modele sieciowe interpretują kontekst jako identyfikator węzła, który
wygenerował zdarzenie. Dlatego ważne jest, aby wywołać ScheduleWithContext in
ns3::Channel podklasy, ponieważ generujemy zdarzenie od węzła i do węzła j i my
chcesz mieć pewność, że zdarzenie, które zostanie uruchomione w węźle j, ma odpowiedni kontekst.

Czas
Do be zakończony

Scheduler
Do be zakończony

Callbacki
Niektórzy nowi użytkownicy do ns-3 nie są zaznajomieni z często używanym idiomem programistycznym
w całym kodzie: the ns-3 oddzwonić. W tym rozdziale znajdziesz motywację dot
wywołania zwrotnego, wskazówki, jak z niego korzystać oraz szczegóły dotyczące jego implementacji.

Callbacki Motywacja
Weź pod uwagę, że masz dwa modele symulacyjne A i B i chcesz, aby przeszły pomyślnie
informacji między nimi podczas symulacji. Jednym ze sposobów, w jaki możesz to zrobić, jest ty
może sprawić, że A i B uzyskają jawną wiedzę o drugim, aby mogli się powoływać
metody na siebie:

klasa A {
publiczny:
void ReceiveInput ( // parametry );
...
}

(w innym pliku źródłowym :)

klasa B {
publiczny:
nieważne Zrób coś (nieważne);
...

prywatny:
A* a_instancja; //wskaźnik do A
}

unieważnić
B::Zrób coś()
{
// Poinformuj a_instancję, że coś się stało
a_instance->ReceiveInput ( // parametry);
...
}

To z pewnością działa, ale ma tę wadę, że wprowadza zależność od A i B
wiedzieć o innych w czasie kompilacji (utrudnia to posiadanie niezależnych
jednostek kompilacji w symulatorze) i nie jest uogólniony; jeśli w późniejszym scenariuszu użycia,
B musi rozmawiać z zupełnie innym obiektem C, kod źródłowy dla B musi być
zmieniono, aby dodać a c_instancja i tak dalej. Łatwo zauważyć, że jest to brutalna siła
mechanizm komunikacji, który może prowadzić do błędów programistycznych w modelach.

Nie oznacza to, że obiekty nie powinny wiedzieć o sobie nawzajem, jeśli jest to trudne
zależności między nimi, ale często model można uczynić bardziej elastycznym, jeśli jest
interakcje są mniej ograniczone w czasie kompilacji.

Nie jest to abstrakcyjny problem dla badań symulacji sieci, ale raczej był
źródłem problemów w poprzednich symulatorach, gdy badacze chcą rozszerzyć lub zmodyfikować
systemu do robienia różnych rzeczy (tak jak robią to w badaniach). Rozważ np.
użytkownik, który chce dodać podwarstwę protokołu bezpieczeństwa IPsec między TCP a IP:

------------ -----------
| TCP | | TCP |
------------ -----------
| staje się -> |
----------- -----------
| adres IP | | IPsec |
----------- -----------
|
-----------
| adres IP |
-----------

Jeśli symulator przyjął założenia i zakodował je na stałe w kodzie, to IP zawsze mówi
do powyższego protokołu transportowego, użytkownik może zostać zmuszony do włamania się do systemu, aby uzyskać
pożądane połączenia. To wyraźnie nie jest optymalny sposób projektowania generycznego
symulator.

Callbacki Tło
UWAGA:
Czytelnicy zaznajomieni z programowaniem wywołań zwrotnych mogą pominąć tę sekcję samouczka.

Podstawowy mechanizm, który pozwala rozwiązać powyższy problem, to a oddzwonić.
Ostatecznym celem jest umożliwienie jednemu fragmentowi kodu wywołania funkcji (lub metody w C++)
bez żadnej szczególnej zależności międzymodułowej.

Ostatecznie oznacza to, że potrzebujesz pewnego rodzaju pośrednictwa - traktujesz adres the
nazywana funkcją jako zmienną. Ta zmienna jest nazywana zmienną wskaźnika do funkcji.
Relacja między funkcją a wskaźnikiem do funkcji nie różni się tak naprawdę
że obiekt i wskaźnik do obiektu.

W C kanonicznym przykładem wskaźnika do funkcji jest a
wskaźnik do funkcji zwracającej liczbę całkowitą (PFI). W przypadku PFI przyjmującego jeden parametr int, this
można zadeklarować jak:

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

To, co otrzymujesz z tego, to zmienna o prostej nazwie pfi który jest inicjowany na wartość 0.
Jeśli chcesz zainicjować ten wskaźnik do czegoś znaczącego, musisz mieć plik a
funkcję z pasującym podpisem. W tym przypadku:

int MojaFunkcja (int arg) {}

Jeśli masz ten cel, możesz zainicjować zmienną, aby wskazywała na twoją funkcję, na przykład:

pfi = Moja funkcja;

Następnie możesz wywołać MyFunction pośrednio, używając bardziej sugestywnej formy wywołania:

int wynik = (*pfi) (1234);

Jest to sugestywne, ponieważ wygląda na to, że usuwasz referencje tylko ze wskaźnika funkcji
tak jakbyś wyłuskał dowolny wskaźnik. Zazwyczaj jednak ludzie korzystają z tzw
fakt, że kompilator wie, co się dzieje i po prostu użyje krótszej formy:

int wynik = pfi (1234);

Zauważ, że wskaźnik funkcji jest zgodny z semantyką wartości, więc możesz go przekazywać jak każdy inny
inna wartość. Zazwyczaj, gdy używasz interfejsu asynchronicznego, przekażesz jakąś jednostkę
w ten sposób do funkcji, która wykona akcję i wezwanie z powrotem aby ci to uświadomić
zakończony. Oddzwania, postępując zgodnie z kierunkiem i wykonując podaną funkcję.

W C++ masz dodatkową złożoność obiektów. Powyższa analogia z PFI odnosi się do Ciebie
mieć wskaźnik do funkcji składowej zwracającej int (PMI) zamiast wskaźnika do
funkcja zwracająca int (PFI).

Tylko nieznacznie inaczej wygląda deklaracja zmiennej podającej kierunek:

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

To deklaruje zmienną o nazwie pmi tak jak w poprzednim przykładzie zadeklarowano zmienną o nazwie
pfi. Ponieważ będzie to wywołanie metody instancji określonej klasy, trzeba
zadeklaruj tę metodę w klasie:

klasa MojaKlasa {
publiczny:
int MojaMetoda (int arg);
};

Biorąc pod uwagę tę deklarację klasy, można by zainicjować tę zmienną w następujący sposób:

pmi = &MojaKlasa::MojaMetoda;

To przypisuje adres kodu implementującego metodę do zmiennej, uzupełniając
kierunek. Aby wywołać metodę, kod potrzebuje a to wskaźnik. To z kolei
oznacza, że ​​musi istnieć obiekt MyClass, do którego można się odwoływać. Uproszczonym tego przykładem jest po prostu
wywołanie metody pośrednio (pomyśl o funkcji wirtualnej):

int (MojaKlasa::*pmi) (int arg) = 0; // Zadeklaruj PMI
pmi = &MojaKlasa::MojaMetoda; // Wskaż kod implementacji

moja klasa moja klasa; // Potrzebna instancja klasy
(mojaKlasa.*pmi) (1234); // Wywołaj metodę z obiektem ptr

Podobnie jak w przykładzie C, możesz użyć tego w asynchronicznym wywołaniu innego modułu
, które będą wezwanie z powrotem za pomocą metody i wskaźnika obiektu. Proste rozszerzenie
można rozważyć przekazanie wskaźnika do obiektu i zmiennej PMI. Moduł
zrobiłby tylko:

(*obiektPtr.*pmi) (1234);

aby wykonać wywołanie zwrotne na żądanym obiekcie.

Ktoś mógłby zapytać w tej chwili, co jest dotychczasowy punkt? Wywołany moduł będzie musiał zrozumieć
konkretny typ obiektu wywołującego, aby poprawnie wykonać wywołanie zwrotne. Dlaczego nie
po prostu zaakceptuj to, przekaż poprawnie wpisany wskaźnik obiektu i zrób obiekt->Metoda wykonania(1234) in
kod zamiast wywołania zwrotnego? To jest właśnie problem opisany powyżej. Co jest
potrzebny jest sposób na całkowite oddzielenie funkcji wywołującej od wywoływanej klasy. Ten
wymóg doprowadził do powstania tzw Funktor.

Funktor jest następstwem czegoś, co wynaleziono w latach 1960. XX wieku, zwanego zamknięciem. To jest
w zasadzie tylko spakowane wywołanie funkcji, być może z pewnym stanem.

Funktor ma dwie części, część specyficzną i część rodzajową, powiązane poprzez dziedziczenie.
Kod wywołujący (kod, który wykonuje wywołanie zwrotne) wykona ogólne przeciążenie
operator () ogólnego funktora, który powoduje wywołanie wywołania zwrotnego. Wywołany kod (tzw
kod, który chce zostać wywołany z powrotem) będzie musiał zapewnić specjalistyczną implementację
dotychczasowy operator () który wykonuje specyficzną dla klasy pracę, która spowodowała bliskie powiązanie
problem powyżej.

Z określonym funktorem i jego przeciążeniem operator () utworzony, a następnie wywołany kod
przekazuje wyspecjalizowany kod do modułu, który wykona wywołanie zwrotne (tzw
kod).

Kod wywołujący przyjmie ogólny funktor jako parametr, więc wykonywane jest niejawne rzutowanie
w wywołaniu funkcji, aby przekonwertować określony funktor na ogólny funktor. To znaczy
że moduł wywołujący musi po prostu zrozumieć ogólny typ funktora. Jest oddzielony
całkowicie z kodu wywołującego.

Informacje potrzebne do stworzenia określonego funktora to wskaźnik obiektu i
adres wskaźnika do metody.

Istotą tego, co musi się wydarzyć, jest to, że system deklaruje ogólną część pliku
funktor:

szablon
Funktor klasy
{
publiczny:
wirtualny int operator() (Targ) = 0;
};

Wywołujący definiuje określoną część funktora, która tak naprawdę jest tam tylko do zaimplementowania
specyficzny operator() metoda:

szablon
klasa SpecyficznyFunktor: publiczny Funktor
{
publiczny:
SpecyficznyFunktor(T* p, int (T::*_pmi)(ARG arg))
{
m_p = p;
m_pmi = _pmi;
}

wirtualny operator int() (ARG argument)
{
(*m_p.*m_pmi)(arg);
}
prywatny:
int (T::*m_pmi)(ARG argument);
T* m_p;
};

Oto przykład użycia:

klasa A
{
publiczny:
A (int a0): a (a0) {}
int Witaj (int b0)
{
std::cout << "Witaj od A, a = " << a << " b0 = " << b0 << std::endl;
}
int;
};

int main ()
{
A a(10);
SpecyficznyFunktor sf(&a, &A::Cześć);
sf(5);
}

UWAGA:
Poprzedni kod nie jest prawdziwym kodem ns-3. Jest to uproszczony przykładowy kod używany tylko do
zilustrować związane z tym koncepcje i pomóc lepiej zrozumieć system. Nie rób
spodziewaj się znaleźć ten kod w dowolnym miejscu drzewa ns-3.

Zauważ, że w powyższej klasie zdefiniowano dwie zmienne. Zmienna m_p to
wskaźnik obiektu, a m_pmi to zmienna zawierająca adres funkcji do
wykonać.

Zauważ, że kiedy operator() jest wywoływana, to z kolei wywołuje metodę dostarczoną z
wskaźnik obiektu przy użyciu składni C++ PMI.

Aby tego użyć, można następnie zadeklarować kod modelu, który przyjmuje ogólny funktor jako a
parametr:

void LibraryFunction (Funktor Funktora);

Kod, który będzie rozmawiał z modelem, zbuduje określony funktor i przekaże go do
Funkcja biblioteki:

moja klasa moja klasa;
SpecyficznyFunktor funktor (&mojaklasa, MojaKlasa::MojaMetoda);

Kiedy Funkcja biblioteki jest zrobione, wykonuje wywołanie zwrotne za pomocą operator() na generycznym
functor został przekazany iw tym konkretnym przypadku podaje argument typu integer:

unieważnić
LibraryFunction (Funktor Funktora)
{
// Wykonaj funkcję biblioteczną
funktor(1234);
}

Zauważ, że Funkcja biblioteki jest całkowicie oddzielony od określonego typu klienta.
Połączenie jest realizowane przez polimorfizm Functora.

Interfejs API wywołania zwrotnego w ns-3 implementuje obiektowe wywołania zwrotne przy użyciu mechanizmu funktora.
Ten interfejs API wywołania zwrotnego, oparty na szablonach języka C++, jest bezpieczny pod względem typu; to znaczy działa statycznie
kontrole typu w celu wyegzekwowania odpowiedniej zgodności podpisów między dzwoniącymi i wywoływanymi. To jest
dlatego jest bardziej bezpieczny w użyciu niż tradycyjne wskaźniki funkcji, ale składnia może
na pierwszy rzut oka wyglądać imponująco. Ta sekcja ma na celu przeprowadzenie Cię przez system oddzwaniania
dzięki czemu możesz wygodnie go używać ns-3.

Korzystanie z dotychczasowy Callback API
Interfejs API wywołania zwrotnego jest dość minimalny i zapewnia tylko dwie usługi:

1. deklaracja typu wywołania zwrotnego: sposób na zadeklarowanie typu wywołania zwrotnego o podanej sygnaturze,
i,

2. instancja wywołania zwrotnego: sposób na utworzenie instancji wywołania zwrotnego przekierowania wygenerowanego przez szablon
który może przekazywać dowolne wywołania do innej metody składowej klasy C++ lub funkcji C++.

Najlepiej można to zaobserwować, przechodząc przez przykład oparty na sample/main-callback.cc.

Korzystanie z dotychczasowy Callback API w statyczny Funkcje
Rozważ funkcję:

statyczny podwójny
CbOne (podwójne a, podwójne b)
{
std::cout << "wywołaj cbOne a=" << a << ", b=" << b << std::endl;
zwrócić;
}

Rozważmy również następujący główny fragment programu:

int main (int argc, char *argv[])
{
// zwracany typ: double
// pierwszy typ argumentu: double
// drugi typ argumentu: double
Oddzwonić jeden;
}

To jest przykład wywołania zwrotnego w stylu C - takiego, które nie zawiera ani nie potrzebuje a to
wskaźnik. Szablon funkcji Callback jest zasadniczo deklaracją zmiennej
zawierający wskaźnik do funkcji. W powyższym przykładzie wyraźnie pokazaliśmy wskaźnik
do funkcji, która zwróciła liczbę całkowitą i przyjęła pojedynczą liczbę całkowitą jako parametr, The
Callback Funkcja szablonu jest jej ogólną wersją — służy do deklarowania typu
oddzwonienia.

UWAGA:
Czytelnicy niezaznajomieni z szablonami C++ mogą zasięgnąć porady
http://www.cplusplus.com/doc/tutorial/templates/.

Callback szablon wymaga jednego obowiązkowego argumentu (typ zwracany przez funkcję do
być przypisany do tego wywołania zwrotnego) i maksymalnie pięć opcjonalnych argumentów, z których każdy określa
typ argumentów (jeśli dana funkcja wywołania zwrotnego ma więcej niż pięć argumentów,
wtedy można to obsłużyć, rozszerzając implementację wywołania zwrotnego).

Tak więc w powyższym przykładzie mamy zadeklarowane wywołanie zwrotne o nazwie „jeden”, które ostatecznie
przytrzymaj wskaźnik funkcji. Sygnatura funkcji, którą będzie pełnić, musi zostać zwrócona
double i musi obsługiwać dwa podwójne argumenty. Jeśli ktoś spróbuje przekazać funkcję, której
podpis nie pasuje do zadeklarowanego wywołania zwrotnego, wystąpi błąd kompilacji. Także jeśli
próbuje się przypisać do wywołania zwrotnego niekompatybilny, kompilacja się powiedzie, ale a
run-time NS_FATAL_ERROR zostanie podniesiony. Przykładowy program
src/core/examples/main-callback.cc pokazuje oba te przypadki błędów na końcu
dotychczasowy Głównym () program.

Teraz musimy powiązać tę instancję wywołania zwrotnego z rzeczywistą funkcją docelową
(CbJeden). Zauważ powyżej, że CbOne ma te same typy sygnatur funkcji, co wywołanie zwrotne -
To jest ważne. Do tego wywołania zwrotnego możemy przekazać dowolną taką prawidłowo wpisaną funkcję.
Przyjrzyjmy się temu bliżej:

statyczne podwójne CbOne (podwójne a, podwójne b) {}
^ ^ ^
| | |
| | |
Oddzwonić jeden;

Możesz powiązać funkcję z wywołaniem zwrotnym tylko wtedy, gdy mają pasującą sygnaturę. Pierwszy
argument szablonu to typ zwracany, a dodatkowe argumenty szablonu to typy
argumentów podpisu funkcji.

Teraz powiążmy nasze wywołanie zwrotne „jeden” z funkcją, która pasuje do jego podpisu:

// zbuduj instancję wywołania zwrotnego, która wskazuje na funkcję cbOne
jeden = MakeCallback (&CbOne);

To wezwanie do Wykonaj oddzwonienie polega w istocie na stworzeniu jednego z wyspecjalizowanych funktorów
wspomniano powyżej. Zmienna zadeklarowana przy użyciu Callback funkcja szablonu zmierza do
odgrywać rolę funktora generycznego. Przydzial pierwszej = Wykonaj oddzwonienie (&CbOne) is
rzutowanie, które konwertuje wyspecjalizowany funktor znany odbiorcy na funktor ogólny
znane dzwoniącemu.

Następnie, w dalszej części programu, jeśli potrzebne jest wywołanie zwrotne, można go użyć w następujący sposób:

NS_ASSERT (!one.IsNull ());

// wywołaj funkcję cbOne poprzez instancję wywołania zwrotnego
podwójny retOne;
retJeden = jeden (10.0, 20.0);

Czek za CzyNull() zapewnia, że ​​wywołanie zwrotne nie jest puste — że istnieje funkcja
zadzwonić za tym wywołaniem zwrotnym. Następnie, jeden() wykonuje generyczny operator() co jest naprawdę
przeciążony konkretną implementacją operator() i zwraca taki sam wynik jak if
CbOne() został wezwany bezpośrednio.

Korzystanie z dotychczasowy Callback API w członek Funkcje
Ogólnie rzecz biorąc, nie będziesz wywoływać funkcji statycznych, ale zamiast tego publiczne funkcje członkowskie
obiekt. W takim przypadku do funkcji MakeCallback potrzebny jest dodatkowy argument, to
powiedzieć systemowi, na jakim obiekcie funkcja ma zostać wywołana. Rozważ ten przykład,
również z main-callback.cc:

klasa MyCb {
publiczny:
int CbTwo (podwójne a) {
std::cout << "wywołaj cbTwo a=" << a << std::endl;
return -5;
}
};

wew główna ()
{
...
// zwracany typ: int
// pierwszy typ argumentu: double
Oddzwonić dwa;
Moje Cb;
// zbuduj instancję wywołania zwrotnego, która wskazuje na MyCb::cbTwo
two = MakeCallback (&MyCb::CbTwo, &cb);
...
}

Tutaj przekazujemy dodatkowy wskaźnik do obiektu Wykonaj oddzwonienie<> funkcjonować. Przypomnij sobie z
sekcja tła powyżej Operator() użyje składni wskaźnika do elementu członkowskiego, gdy it
wykonuje na obiekcie:

wirtualny operator int() (ARG argument)
{
(*m_p.*m_pmi)(arg);
}

Musieliśmy więc podać dwie zmienne (poseł oraz m_pmi), kiedy zrobiliśmy specyfikę
funktor. Linia:

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

dokładnie to robi. W tym przypadku kiedy drugiej () jest wywoływany:

int wynik = dwa (1.0);

spowoduje wezwanie do CbTwo funkcja członkowska (metoda) na obiekcie wskazanym przez
&cb.

Budowanie Null Callbacki
Możliwe jest, że wywołania zwrotne będą miały wartość NULL; dlatego warto je sprawdzić przed użyciem.
Istnieje specjalna konstrukcja dla wywołania zwrotnego o wartości null, która jest lepsza niż zwykłe przekazywanie
„0” jako argument; to jest MakeNullCallback<> zbudować:

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

Wywołanie pustego wywołania zwrotnego jest jak wywołanie pustego wskaźnika funkcji: spowoduje awarię
czas wykonywania.

Granica Callbacki
Bardzo użytecznym rozszerzeniem koncepcji funktora jest Bound Callback. Wcześniej to
wspomniano, że zamknięcia były pierwotnie wywołaniami funkcji spakowanymi na później
wykonanie. Zauważ, że we wszystkich powyższych opisach wywołań zwrotnych nie ma takiej możliwości
zapakuj dowolne parametry do późniejszego wykorzystania — kiedy plik Callback nazywa się przez operator().
Wszystkie parametry są dostarczane przez funkcję wywołującą.

Co zrobić, jeśli pożądane jest zezwolenie funkcji klienta (tej, która zapewnia wywołanie zwrotne) na
podać jakieś parametry? Aleksandrescu wywołuje proces zezwalania klientowi na
określić jeden z parametrów "wiążący". Jeden z parametrów operator() został
związany (ustalony) przez klienta.

Dobrym tego przykładem jest część naszego kodu śledzącego pcap. Jest taka funkcja
musi być wywoływana za każdym razem, gdy pakiet jest odbierany. Ta funkcja wywołuje obiekt, który
faktycznie zapisuje pakiet na dysk w formacie pliku pcap. Podpis jednego z nich
funkcje będą:

static void DefaultSink (Ptr plik, str P);

Słowo kluczowe static oznacza, że ​​jest to funkcja statyczna, która nie wymaga a to wskaźnik, tak
będzie używać wywołań zwrotnych w stylu C. Nie chcemy, aby kod wywołujący musiał o tym wiedzieć
wszystko oprócz Pakietu. To, czego chcemy w kodzie wywołującym, to po prostu wywołanie, które wygląda tak:

m_promiscSnifferTrace (m_currentPkt);

To, co chcemy zrobić, to zrobić związania dotychczasowy Ptr filet do konkretnego wywołania zwrotnego
wdrożenia, gdy jest on tworzony i zorganizować dla operator() oddzwaniania do
podaj ten parametr za darmo.

Zapewniamy MakeBoundCallback w tym celu funkcję szablonu. Trwa to samo
parametry jak Wykonaj oddzwonienie szablonu, ale przyjmuje również parametry
zobowiązany. W przypadku powyższego przykładu:

MakeBoundCallback (&DefaultSink, plik);

utworzy konkretną implementację wywołania zwrotnego, która wie, jak dodać dodatkowe ograniczenie
argumenty. Koncepcyjnie rozszerza określony funktor opisany powyżej o jeden lub więcej
powiązane argumenty:

szablon
klasa SpecyficznyFunktor: publiczny Funktor
{
publiczny:
SpecyficznyFunktor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG związany Arg)
{
m_p = p;
m_pmi = pmi;
m_powiązany Arg = powiązany Arg;
}

wirtualny operator int() (ARG argument)
{
(*m_p.*m_pmi)(m_boundArg, arg);
}
prywatny:
nieważne (T::*m_pmi)(ARG arg);
T* m_p;
BOUND_ARG m_boundArg;
};

Możesz zobaczyć, że kiedy tworzony jest określony funktor, związany argument jest zapisywany w pliku
sam obiekt funktora/wywołania zwrotnego. Kiedy operator() jest wywoływany z singlem
parametr, jak w:

m_promiscSnifferTrace (m_currentPkt);

Wdrożenie operator() dodaje związany parametr do rzeczywistego wywołania funkcji:

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

Możliwe jest również powiązanie dwóch lub trzech argumentów. Powiedzmy, że mamy funkcję z
podpis:

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

Można utworzyć powiązane wywołanie zwrotne wiążące pierwsze dwa argumenty, takie jak:

MakeBoundCallback (&NotifyEvent, a1, b1);

zarozumiały a1 oraz b1 są obiektami typu A oraz B odpowiednio. Podobnie dla trzech
argumenty, które miałyby funkcję z podpisem:

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

Wiązanie trzech argumentów zakończone:

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

ponownie zakładając a1, b1 oraz c1 są obiektami typu A, B oraz C odpowiednio.

Ten rodzaj wiązania można wykorzystać do wymiany informacji między obiektami w symulacji;
w szczególności powiązane wywołania zwrotne mogą być używane jako śledzone wywołania zwrotne, co zostanie opisane w
następna sekcja.

prześledzić Callbacki
zastępczy podrozdział

Callback lokalizacji in ns-3
Gdzie są często używane wywołania zwrotne ns-3? Oto niektóre z bardziej widocznych
typowi użytkownicy:

· Gniazdo API

· Interfejs API warstwy 2/3 warstwy

· Podsystem śledzenia

· API między podsystemami IP i routingu

Wdrożenie detale
Powyższe fragmenty kodu są uproszczone i mają na celu jedynie zilustrowanie mechanizmu
samo. Rzeczywisty kod wywołania zwrotnego jest dość skomplikowany i zawiera wiele szablonów
głębokie zrozumienie kodu nie jest wymagane. Jeśli są zainteresowani, doświadczeni użytkownicy mogą znaleźć plik
następujące przydatne.

Kod został pierwotnie napisany w oparciu o techniki opisane w
http://www.codeproject.com/cpp/TTLFunction.asp. Został on następnie przepisany, aby śledzić
architektura opisana w Nowoczesne technologie C + + Konstrukcja Rodzajowy Programowanie oraz Wnętrze wzory
Stosowany, Aleksandrescu, publikacji naukowej 5, Uogólnione Funktory.

Ten kod używa:

· domyślne parametry szablonu oszczędzają użytkownikom konieczności określania pustych parametrów, kiedy
liczba parametrów jest mniejsza niż maksymalna obsługiwana liczba

· idiom pimpl: klasa Callback jest przekazywana przez wartość i deleguje sedno
praca do wskaźnika pimpl.

· można użyć dwóch implementacji pimpl wywodzących się z CallbackImpl FunctorCallbackImpl
z dowolnym typem funktora, podczas gdy MemPtrCallbackImpl może być używany ze wskaźnikami do członka
funkcje.

· implementacja listy referencyjnej w celu zaimplementowania semantyki wartości wywołania zwrotnego.

Ten kod w szczególności odbiega od implementacji Alexandrescu, ponieważ tak nie jest
użyj list typów, aby określić i przekazać typy argumentów wywołania zwrotnego. Oczywiście,
nie używa również semantyki niszczenia kopii i opiera się raczej na liście referencyjnej niż
autoPtr, aby przytrzymać wskaźnik.

przedmiot model
ns-3 jest zasadniczo systemem obiektowym C++. Obiekty mogą być deklarowane i tworzone jako
zwykle, zgodnie z zasadami C++. ns-3 dodaje również pewne funkcje do tradycyjnych obiektów C++, jak
opisane poniżej, aby zapewnić większą funkcjonalność i funkcje. Ten rozdział podręcznika jest
mający na celu zapoznanie czytelnika z tzw ns-3 model obiektowy.

W tej sekcji opisano projekt klasy C++ dla ns-3 obiekty. W skrócie kilka projektów
używane wzorce obejmują klasyczne projektowanie zorientowane obiektowo (interfejsy polimorficzne i
implementacje), oddzielenie interfejsu i implementacji, niewirtualna publiczność
wzorzec projektowy interfejsu, narzędzie do agregacji obiektów i zliczanie referencji
zarządzanie pamięcią. Osoby zaznajomione z modelami komponentów, takimi jak COM lub Bonobo, będą zachwycone
rozpoznaje elementy projektu w j ns-3 model agregacji obiektów, chociaż ns-3
projekt nie jest ściśle zgodny z żadnym z nich.

Obiektowy zachowanie
Ogólnie rzecz biorąc, obiekty C++ zapewniają wspólne możliwości zorientowane obiektowo (abstrakcja,
enkapsulacja, dziedziczenie i polimorfizm), które są częścią klasycznej obiektowości
projekt. ns-3 obiekty wykorzystują te właściwości; na przykład:

adres klasy
{
publiczny:
Adres ();
Adres (typ uint8_t, const uint8_t *buffer, uint8_t len);
Adres (const Adres i adres);
&operator adresu = (stały adres &adres);
...
prywatny:
uint8_t typ_m;
uint8_t m_len;
...
};

przedmiot baza Klasy
Istnieją trzy specjalne klasy podstawowe używane w ns-3. Klasy, które dziedziczą z tych baz
klasy mogą tworzyć instancje obiektów o specjalnych właściwościach. Te podstawowe klasy to:

· klasa przedmiot

· klasa Baza obiektów

· klasa Prosta liczba ref

Nie jest to wymagane ns-3 obiekty dziedziczą z tych klas, ale te, które je otrzymują
specjalne właściwości. Klasy wywodzące się z klasy przedmiot uzyskać następujące właściwości.

· ten ns-3 system typów i atrybutów (patrz Atrybuty)

· system agregacji obiektów

· system zliczania referencji smart-pointer (klasa Ptr)

Klasy wywodzące się z class Baza obiektów uzyskać dwie pierwsze właściwości powyżej, ale nie
uzyskać inteligentne wskazówki. Klasy wywodzące się z class Prosta liczba ref: zdobądź tylko
inteligentny system liczenia referencji.

W praktyce klasa przedmiot jest wariantem trzech powyższych, że ns-3 deweloper będzie
najczęściej spotykane.

Pamięć i konserwacjami oraz klasa Ptr
Zarządzanie pamięcią w programie C++ jest złożonym procesem i często jest wykonywane niepoprawnie lub
niekonsekwentnie. Zdecydowaliśmy się na projekt liczenia referencji opisany poniżej.

Wszystkie obiekty używające liczenia referencji utrzymują wewnętrzną liczbę referencji do ustalenia
kiedy obiekt może się bezpiecznie usunąć. Za każdym razem, gdy uzyskany zostanie wskaźnik do
interfejs, liczba odwołań do obiektu jest zwiększana przez wywołanie Ref(). To jest
obowiązek użytkownika wskaźnika do wyraźnego Unref() wskaźnik po zakończeniu. Gdy
liczba referencji spada do zera, obiekt jest usuwany.

· Kiedy kod klienta uzyskuje wskaźnik z samego obiektu poprzez tworzenie obiektu,
lub przez GetObject, nie musi zwiększać liczby referencji.

· Gdy kod klienta uzyskuje wskaźnik z innego źródła (np. kopiując wskaźnik), musi to zrobić
wezwanie Ref() aby zwiększyć liczbę referencji.

· Wszyscy użytkownicy wskaźnika obiektu muszą zadzwonić Unref() aby zwolnić referencję.

Ciężar powołania Unref() jest nieco złagodzony przez użycie liczenia referencji
klasa inteligentnego wskaźnika opisana poniżej.

Użytkownicy korzystający z interfejsu API niskiego poziomu, którzy chcą jawnie przydzielać obiekty bez zliczania odwołań
na stercie, używając operatora new, odpowiadają za usuwanie takich obiektów.

Numer Referencyjny rachunkowość mądry wskaźnik (Ptr)
powołanie Ref() oraz Unref() cały czas byłoby uciążliwe, więc ns-3 zapewnia mądry
klasa wskaźnika Ptr podobnego do Wzmocnienie::intruzyjne_ptr. Ta klasa inteligentnego wskaźnika zakłada, że
typ bazowy zapewnia parę Ref oraz bez ref metody, których się oczekuje
zwiększać i zmniejszać wewnętrzny refcount instancji obiektu.

Ta implementacja pozwala manipulować inteligentnym wskaźnikiem tak, jakby był normalny
wskaźnik: możesz porównać go z zerem, porównać z innymi wskaźnikami, przypisać zero do
to itp.

Możliwe jest wyodrębnienie surowego wskaźnika z tego inteligentnego wskaźnika za pomocą PobierzPointer()
oraz PeekPointer() Metody.

Jeśli chcesz zapisać nowy obiekt w inteligentnym wskaźniku, zalecamy użycie metody
Funkcje szablonu CreateObject do tworzenia obiektu i przechowywania go w inteligentnym wskaźniku
uniknąć wycieków pamięci. Funkcje te są naprawdę małymi funkcjami wygodnymi i ich celem
jest tylko po to, aby zaoszczędzić ci trochę pisania.

Utwórz obiekt oraz Stwórz
Obiekty w C++ mogą być tworzone statycznie, dynamicznie lub automatycznie. To prawda
dla ns-3 również, ale niektóre obiekty w systemie mają dostępne dodatkowe ramy.
W szczególności, zliczane obiekty referencyjne są zwykle przydzielane przy użyciu szablonu Utwórz lub
CreateObject metodę w następujący sposób.

Dla obiektów pochodzących z klasy przedmiot:

Ptr urządzenie = Utwórz obiekt ();

Proszę nie tworzyć takich obiektów za pomocą operator nowych; tworzyć je za pomocą UtwórzObiekt()
zamiast.

Dla obiektów pochodzących z klasy Prosta liczba ref, lub inne obiekty obsługujące użycie
smart pointer, dostępna jest szablonowa funkcja pomocnicza, której użycie jest zalecane:

Ptr b = Utwórz ();

Jest to po prostu opakowanie wokół operatora new, które poprawnie obsługuje zliczanie referencji
pomimo napiętego harmonogramu

Podsumowując, użyj Tworzyć jeśli B nie jest obiektem, ale po prostu używa liczenia referencji (np
Paczka), I użyć Utwórz obiekt jeśli B pochodzi od ns3::Obiekt.

Zbiór
ns-3 system agregacji obiektów jest w dużej mierze motywowany uznaniem, że a
wspólny przypadek użycia dla ns-2 było wykorzystanie dziedziczenia i polimorfizmu do rozszerzenia
modele protokołów. Na przykład wyspecjalizowane wersje protokołu TCP, takie jak RenoTcpAgent, są pochodnymi
from (i przesłaniaj funkcje z) klasy TcpAgent.

Jednak dwa problemy, które pojawiły się w ns-2 modelem są spadki i „słaba baza”.
class." Rzutowanie w dół odnosi się do procedury używania wskaźnika klasy bazowej do obiektu i
zapytanie w czasie wykonywania, aby znaleźć informacje o typie, używane do jawnego rzutowania wskaźnika
do wskaźnika podklasy, aby można było użyć interfejsu API podklasy. Słaba klasa bazowa odnosi się do
problemy, które pojawiają się, gdy klasa nie może być skutecznie ponownie wykorzystana (pochodząca z), ponieważ
brakuje niezbędnej funkcjonalności, co powoduje, że programista musi zmodyfikować klasę bazową i
powodując rozprzestrzenianie wywołań API klasy bazowej, z których niektóre mogą nie być semantycznie
poprawne dla wszystkich podklas.

ns-3 używa wersji wzorca projektowego interfejsu zapytań, aby uniknąć tych problemów.
Projekt ten oparty jest na elementach tzw Składnik przedmiot Model oraz GNOME Bonobo chociaż
pełna kompatybilność na poziomie binarnym wymiennych komponentów nie jest obsługiwana, a my ją mamy
próbował uprościć składnię i wpływ na twórców modeli.

Przykłady
Zbiór przykład
Node jest dobrym przykładem zastosowania agregacji w ns-3. Zauważ, że nie ma pochodnych
klasy węzłów w ns-3 takie jak klasa Węzeł internetowy. Zamiast tego komponenty (protokoły) są
agregowane do węzła. Przyjrzyjmy się, jak niektóre protokoły Ipv4 są dodawane do węzła.:

statyczna pustka
AddIpv4Stack(Ptr węzeł)
{
Ptr ipv4 = Utwórz obiekt ();
ipv4->SetNode (węzeł);
węzeł->AggregateObject (ipv4);
Ptr ipv4Impl = Utwórz obiekt ();
ipv4Impl->UstawIpv4 (ipv4);
węzeł->AggregateObject (ipv4Impl);
}

Należy pamiętać, że protokoły IPv4 są tworzone przy użyciu UtwórzObiekt(). Następnie są one agregowane
do węzła. W ten sposób klasa podstawowa Node nie musi być edytowana, aby umożliwić użytkownikom
ze wskaźnikiem węzła klasy bazowej, aby uzyskać dostęp do interfejsu IPv4; użytkownicy mogą poprosić węzeł o a
wskaźnik do jego interfejsu IPv4 w czasie wykonywania. Sposób, w jaki użytkownik pyta węzeł, jest opisany w
następny podrozdział.

Należy zauważyć, że agregowanie więcej niż jednego obiektu tego samego typu jest błędem programistycznym
an ns3::Obiekt. Na przykład agregacja nie jest opcją przechowywania wszystkich
aktywne gniazda węzła.

Pobierz obiekt przykład
GetObject to bezpieczny dla typu sposób na osiągnięcie bezpiecznego rzutowania w dół i umożliwienie istnienia interfejsów
znalezione na obiekcie.

Rozważ wskaźnik węzła m_węzeł który wskazuje na obiekt Node, który ma implementację
IPv4 wcześniej zagregowany do niego. Kod klienta chce skonfigurować trasę domyślną. Do
to zrobić, musi uzyskać dostęp do obiektu w węźle, który ma interfejs do przekazywania IP
konfiguracja. Wykonuje następujące czynności:

Ptr ipv4 = m_node->GetObject ();

Jeśli w rzeczywistości węzeł nie ma zagregowanego obiektu Ipv4, metoda to zrobi
zwróć zero. Dlatego dobrą praktyką jest sprawdzenie wartości zwracanej przez taką funkcję
dzwonić. Jeśli się powiedzie, użytkownik może teraz użyć Ptr do obiektu Ipv4, który był wcześniej
agregowane do węzła.

Innym przykładem zastosowania agregacji jest dodanie opcjonalnych modeli do obiektów. Dla
na przykład istniejący obiekt Node może mieć zagregowany obiekt „Model energetyczny”.
czasie wykonywania (bez modyfikowania i ponownej kompilacji klasy węzła). Istniejący model (np
bezprzewodowe urządzenie sieciowe) może później „GetObject” dla modelu energetycznego i działać odpowiednio
jeśli interfejs został wbudowany w bazowy obiekt Node lub zagregowany
to w czasie wykonywania. Jednak inne węzły nie muszą wiedzieć nic o modelach energetycznych.

Mamy nadzieję, że ten tryb programowania będzie wymagał znacznie mniej modyfikacji od programistów
klasy bazowe.

przedmiot fabryki
Typowym przypadkiem użycia jest tworzenie wielu podobnie skonfigurowanych obiektów. Można wielokrotnie
wezwanie UtwórzObiekt() ale istnieje również wzorzec projektowania fabrycznego w użyciu ns-3 pomimo napiętego harmonogramu
Jest intensywnie używany w interfejsie API „pomocnika”.

Klasa Fabryka Obiektów może służyć do tworzenia instancji obiektów i konfigurowania atrybutów
te obiekty:

void SetTypeId (TypeId tid);
void Set (std::string name, const AttributeValue &value);
Ptr Utwórz (unieważnij) const;

Pierwsza metoda pozwala na użycie ns-3 System TypeId do określania typu obiektów
Utworzony. Drugi pozwala ustawić atrybuty tworzonych obiektów, a
trzeci pozwala na samodzielne tworzenie obiektów.

Na przykład:

fabryka ObjectFactory;
// Spraw, aby ta fabryka tworzyła obiekty typu FriisPropagationLossModel
factory.SetTypeId ("ns3::FriisPropagationLossModel")
// Spraw, aby ten obiekt fabryki zmienił domyślną wartość atrybutu, for
// następnie utworzone obiekty
factory.Set ("SystemLoss", DoubleValue (2.0));
// Utwórz jeden taki obiekt
Ptr obiekt = fabryka. Utwórz ();
factory.Set ("SystemLoss", DoubleValue (3.0));
// Utwórz kolejny obiekt z inną wartością SystemLoss
Ptr obiekt = fabryka. Utwórz ();

Obniżanie
Pytanie, które pojawiło się kilka razy, brzmi: „Jeśli mam wskaźnik klasy bazowej (Ptr) do pliku an
obiekt i chcę wskaźnik klasy pochodnej, czy powinienem obniżyć (przez dynamiczne rzutowanie C++) do
uzyskać pochodny wskaźnik, czy też powinienem użyć systemu agregacji obiektów PobierzObiekt<> ()
znaleźć Ptr do interfejsu API podklasy?”

Odpowiedź na to jest taka, że ​​w wielu sytuacjach obie techniki będą działać. ns-3 zapewnia
szablonowa funkcja, dzięki której składnia dynamicznego rzutowania obiektów jest znacznie bardziej użyteczna
przyjazny:

szablon
Ptr
DynamicCast (Ptr stała&p)
{
powrót Ptr (dynamic_cast (Wskaźnik Peek (p)));
}

DynamicCast działa, gdy programista ma wskaźnik typu podstawowego i testuje z a
wskaźnik podklasy. GetObject działa podczas wyszukiwania różnych obiektów zagregowanych, ale także
działa z podklasami, w taki sam sposób jak DynamicCast. Jeśli nie jesteś pewien, programista powinien
użyj GetObject, ponieważ działa we wszystkich przypadkach. Jeśli programista zna hierarchię klas
rozważanego obiektu, bardziej bezpośrednie jest po prostu użycie DynamicCast.

Konfiguracja oraz Atrybuty
In ns-3 symulacji, istnieją dwa główne aspekty konfiguracji:

· Topologia symulacji i sposób łączenia obiektów.

· Wartości używane przez modele utworzone w topologii.

Ten rozdział koncentruje się na drugim elemencie powyżej: jak wiele wartości używanych w ns-3 jest
zorganizowane, udokumentowane i modyfikowalne przez ns-3 użytkowników. Plik ns-3 system atrybutów jest również
stanowiące podstawę sposobu gromadzenia śladów i statystyk w symulatorze.

W trakcie tego rozdziału omówimy różne sposoby ustawiania lub modyfikowania wartości
wykorzystane przez ns-3 obiekty wzorcowe. W rosnącej kolejności specyficzności są to:

┌─────────────────────────────────┬───── ─────────────────────────────┐
│Metoda │ Zakres │
├─────────────────────────────────┼───── ─────────────────────────────┤
│Domyślne ustawione wartości atrybutów │ Wpływa na wszystkie wystąpienia │
│kiedy atrybuty są zdefiniowane w │ klasie. │
PobierzTypeId (). │ │
└─────────────────────────────────┴───── ─────────────────────────────┘

Wiersz poleceń │ Wpływa na wszystkie przyszłe instancje. │
Konfiguracja::SetDefault() │ │.
Magazyn konfiguracji │ │.
├─────────────────────────────────┼───── ─────────────────────────────┤
Fabryka Obiektów │ Wpływa na wszystkie utworzone instancje │
│ │ z fabryką. │
├─────────────────────────────────┼───── ─────────────────────────────┤
Atrybut XHelperSet () │ Wpływa na wszystkie instancje utworzone przez │
│ │ pomocnik. │
├─────────────────────────────────┼───── ─────────────────────────────┤
MojaKlasa::SetX () │ Zmienia tę konkretną instancję. │
Obiekt::SetAttribute () │ Generalnie jest to jedyna forma │
Konfiguracja::Ustaw() │ które można zaplanować zmienić │
│ │ instancja po symulacji │
│ │ działa. │
└─────────────────────────────────┴───── ─────────────────────────────┘

Przez „specyficzność” rozumiemy, że metody w późniejszych wierszach tabeli zastępują ustawione wartości
przez i zazwyczaj wpływają na mniej przypadków niż wcześniejsze metody.

Zanim zagłębimy się w szczegóły systemu wartości atrybutów, warto przejrzeć niektóre z nich
podstawowe właściwości klasy przedmiot.

przedmiot Omówienie
ns-3 jest zasadniczo systemem obiektowym C++. Rozumiemy przez to, że nowe klasy C++
(typy) można jak zwykle deklarować, definiować i tworzyć podklasy.

Wiele ns-3 obiekty dziedziczą po przedmiot klasa podstawowa. Obiekty te mają pewne dodatkowe
właściwości, które wykorzystujemy do organizowania systemu i ulepszania zarządzania pamięcią
naszych obiektów:

· System „Metadane”, który łączy nazwę klasy z wieloma metainformacjami o klasie
obiekt, w tym:

· Klasa bazowa podklasy,

· Zbiór dostępnych konstruktorów w podklasie,

· Zbiór „atrybutów” podklasy,

· Czy każdy atrybut można ustawić, czy jest tylko do odczytu,

· Dozwolony zakres wartości dla każdego atrybutu.

· Implementacja inteligentnego wskaźnika liczenia referencji do zarządzania pamięcią.

ns-3 obiekty korzystające z systemu atrybutów wywodzą się z któregokolwiek z nich przedmiot or Baza obiektów. Najbardziej
ns-3 obiekty, o których będziemy mówić, wywodzą się przedmiot, ale kilku iż poza przemądrzałym
ramy zarządzania pamięcią wskaźników wywodzą się z Baza obiektów.

Przyjrzyjmy się kilku właściwościom tych obiektów.

Smart wskaźniki
Jak wprowadzono w ns-3 instruktaż, ns-3 obiekty są pamięcią zarządzaną przez a odniesienie
rachunkowość mądry wskaźnik realizacja, klasa Ptr.

Inteligentne wskaźniki są szeroko stosowane w ns-3 API, aby uniknąć przekazywania odniesień do
obiekty przydzielone na stercie, które mogą powodować wycieki pamięci. W przypadku najbardziej podstawowego użycia (składni) traktuj
inteligentny wskaźnik jak zwykły wskaźnik:

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

Jak więc uzyskać inteligentny wskaźnik do obiektu, jak w pierwszym wierszu tego przykładu?

Utwórz obiekt
Jak omówiliśmy powyżej w Memory-management-and-class-Ptr, w interfejsie API najniższego poziomu, obiekty
typu przedmiot nie są tworzone za pomocą operator nowych jak zwykle, ale zamiast tego przez szablon
funkcja o nazwie Utwórz obiekt ().

Typowy sposób tworzenia takiego obiektu jest następujący:

Ptr nd = UtwórzObiekt ();

Możesz myśleć o tym jako o funkcjonalnie równoważnym z:

WifiNetDevice* nd = nowe WifiNetDevice ();

Obiekty, które wywodzą się z przedmiot musi być przydzielony na stercie za pomocą Utwórz obiekt (). Te
pochodzące z Baza obiektów, Takie jak ns-3 funkcje pomocnicze oraz nagłówki i nagłówki pakietów,
można umieścić na stosie.

W niektórych skryptach możesz nie widzieć wielu z nich Utwórz obiekt () wzywa kod; to jest
ponieważ istnieją pewne obiekty pomocnicze, które wykonują Utwórz obiekt () Połączenia
dla Ciebie.

TypId
ns-3 klasy wywodzące się z klasy przedmiot może zawierać klasę metadanych o nazwie TypId że
rejestruje metainformacje o klasie, do wykorzystania w agregacji obiektów i komponencie
systemy menedżerskie:

· Unikalny ciąg identyfikujący klasę.

· Klasa bazowa podklasy w systemie metadanych.

· Zbiór dostępnych konstruktorów w podklasie.

· Lista publicznie dostępnych właściwości („atrybutów”) klasy.

przedmiot Podsumowanie
Łącząc wszystkie te koncepcje, spójrzmy na konkretny przykład: klasę Node.

Publiczny plik nagłówkowy węzeł.h ma deklarację, która zawiera static PobierzTypeId ()
wywołanie funkcji:

klasa Węzeł: obiekt publiczny
{
publiczny:
statyczny TypeId GetTypeId (pusty);
...

Jest to określone w węzeł.cc plik w następujący sposób:

TypId
Węzeł::GetTypeId (pusty)
{
static TypeId tid = TypeId ("ns3::węzeł")
.Ustaw Rodzica ()
.AddConstructor ()
.AddAttribute("Lista urządzeń",
"Lista urządzeń powiązanych z tym Węzłem.",
ObiektVectorValue (),
MakeObjectVectorAccessor (&Node::m_devices),
MakeObjectVectorChecker ())
.AddAttribute("Lista aplikacji",
"Lista aplikacji powiązanych z tym Węzłem.",
ObiektVectorValue (),
MakeObjectVectorAccessor (&Node::m_applications),
MakeObjectVectorChecker ())
.AddAttribute("Id",
"Id (unikalna liczba całkowita) tego węzła.",
TypeId::ATTR_GET, // pozwala tylko na pobieranie.
UcałkowitaWartość(0),
MakeUintegerAccessor (&Node::m_id),
MakeUintegerChecker ())
;
pora powrotu;
}

Weź pod uwagę TypId ukończenia ns-3 przedmiot class jako rozszerzoną formę typu wykonawczego
informacje (RTTI). Język C++ zawiera prosty rodzaj RTTI w celu wsparcia
dynamic_cast oraz Typeid operatorów.

Ustaw Rodzica () wywołanie w powyższej definicji jest używane w połączeniu z naszym
mechanizmy agregacji obiektów umożliwiające bezpieczne rzutowanie w górę iw dół w drzewach dziedziczenia
podczas Pobierz obiekt (). Umożliwia również podklasom dziedziczenie atrybutów ich rodzica
class.

AddConstructor () call jest używane w połączeniu z naszą fabryką obiektów abstrakcyjnych
mechanizmy, które pozwalają nam konstruować obiekty C++ bez zmuszania użytkownika do znajomości
konkretną klasę obiektu, który buduje.

Trzy wezwania do Dodaj atrybut () powiąż dany ciąg z silnie wpisaną wartością
klasa. Zwróć uwagę, że musisz podać ciąg pomocy, który może zostać wyświetlony, na przykład
przez procesory wiersza poleceń. Każdy Atrybut jest powiązany z mechanizmami dostępu
podstawowa zmienna członkowska w obiekcie (na przykład Akcesor MakeUinteger () mówi
rodzajowy Atrybut kod, jak dostać się do powyższego identyfikatora węzła). Są też „Szachownice”
metody używane do sprawdzania poprawności wartości pod kątem ograniczeń zakresu, takich jak maksimum i
minimalne dozwolone wartości.

Kiedy użytkownicy chcą tworzyć węzły, zwykle wywołują jakąś formę Utwórz obiekt (),:

Ptr n = UtwórzObiekt ();

lub bardziej abstrakcyjnie, używając fabryki obiektów, możesz utworzyć plik Node obiekt bez parzystości
znajomość konkretnego typu C++:

fabryka ObjectFactory;
const std::string typeId = "ns3::węzeł'';
fabryka.SetTypeId (identyfikator typu);
Ptr węzeł = fabryka. Utwórz ();

Obie te metody powodują, że w pełni zainicjowane atrybuty są dostępne w
wynikły przedmiot instancje.

Następnie omówimy, w jaki sposób atrybuty (wartości powiązane ze zmiennymi składowymi lub funkcjami
klasa) są podłączone do powyższego TypId.

Atrybuty
Celem systemu atrybutów jest zorganizowanie dostępu do wewnętrznych obiektów składowych a
symulacja. Cel ten powstaje, ponieważ zazwyczaj w symulacji użytkownicy wycinają i
wkleić/zmodyfikować istniejące skrypty symulacji lub użyć konstrukcji symulacyjnych wyższego poziomu,
ale często będą zainteresowani badaniem lub śledzeniem określonych zmiennych wewnętrznych. Dla
na przykład przypadki użycia, takie jak:

· "I chcieć do wyśledzić dotychczasowy Pakiety on dotychczasowy bezprzewodowy Interfejs tylko on dotychczasowy drugim dostęp punkt."

· "I chcieć do wyśledzić dotychczasowy wartość of dotychczasowy TCP przekrwienie okno (każdy czas it zmiany) on a
szczególny TCP gniazdo elektryczne."

· "I chcieć a zrzucać of cała kolekcja wartości że były używany in my symulacja."

Podobnie, użytkownicy mogą chcieć szczegółowego dostępu do zmiennych wewnętrznych w symulacji lub
może chcieć zasadniczo zmienić początkową wartość używaną dla określonego parametru we wszystkich
tworzonych później obiektów. Wreszcie, użytkownicy mogą chcieć wiedzieć, jakie zmienne można ustawić
i możliwe do odzyskania w konfiguracji symulacyjnej. To nie jest tylko do bezpośredniej symulacji
interakcja w wierszu poleceń; rozważyć również (przyszły) graficzny interfejs użytkownika, który
chciałby mieć możliwość udostępnienia funkcji, dzięki której użytkownik mógłby kliknąć prawym przyciskiem myszy na węźle
płótno i zobacz hierarchiczną, zorganizowaną listę parametrów, które można ustawić na
node i jego składowych obiektów składowych oraz tekst pomocy i wartości domyślne dla każdego z nich
parametr.

Definiowanie Atrybuty
Zapewniamy użytkownikom sposób na dostęp do wartości głęboko w systemie, bez konieczności drążenia
akcesorów (wskaźników) przez system i przejść przez łańcuchy wskaźników, aby się do nich dostać. Rozważ a
klasa Upuść Kolejkę Ogona który ma zmienną składową, która jest liczbą całkowitą bez znaku m_maxPakiety;
ta zmienna członkowska kontroluje głębokość kolejki.

Jeśli spojrzymy na deklarację nt Upuść Kolejkę Ogona, widzimy, co następuje:

klasa DropTailQueue: kolejka publiczna {
publiczny:
statyczny TypeId GetTypeId (pusty);
...

prywatny:
std::kolejka > m_pakiety;
uint32_t m_maxPackets;
};

Rozważmy rzeczy, które użytkownik może chcieć zrobić z wartością m_maxPakiety:

· Ustaw domyślną wartość dla systemu, tak aby za każdym razem, gdy nowy Upuść Kolejkę Ogona jest tworzone,
ten element członkowski jest inicjowany z wartością domyślną.

· Ustaw lub pobierz wartość z już utworzonej kolejki.

Powyższe rzeczy zazwyczaj wymagają podania Zestaw () oraz Get () funkcje i niektóre rodzaje
globalna wartość domyślna.

W ns-3 system atrybutów, te definicje wartości i rejestracje funkcji akcesorów
są przenoszone do TypId klasa; na przykład.:

NS_OBJECT_ENSURE_REGISTERED (Kolejka opuszczania ogona);

TypId
DropTailQueue::GetTypeId (puste)
{
static TypeId tid = TypeId ("ns3::DropTailQueue")
.Ustaw Rodzica ()
.AddConstructor ()
.AddAttribute ("Maks.Pakietów",
"Maksymalna liczba pakietów akceptowanych przez tę DropTailQueue.",
UcałkowitaWartość(100),
MakeUintegerAccessor (&DropTailQueue::m_maxPackets),
MakeUintegerChecker ())
;

pora powrotu;
}

Dodaj atrybut () Metoda polega na wykonywaniu wielu rzeczy dla m_maxPakiety wartość:

· Wiązanie (zwykle prywatnej) zmiennej składowej m_maxPakiety do publicznego ciągu znaków
„Maksymalna liczba pakietów”.

· Podanie wartości domyślnej (100 pakietów).

· Udostępnienie tekstu pomocy definiującego znaczenie wartości.

· Zapewnienie „Checkera” (nieużywanego w tym przykładzie), którego można użyć do ustawienia granic na
dopuszczalny zakres wartości.

Kluczową kwestią jest to, że teraz dostępna jest wartość tej zmiennej i jej wartość domyślna
w przestrzeni nazw atrybutów, która jest oparta na ciągach znaków, takich jak „Maksymalna liczba pakietów” oraz TypId Nazwa
smyczki. W następnej sekcji przedstawimy przykładowy skrypt, który pokazuje, jak użytkownicy mogą
manipulować tymi wartościami.

Należy zauważyć, że inicjalizacja atrybutu zależy od makra NS_OBJECT_ENSURE_REGISTERED
(Kolejka Ogonów) być nazwanym; jeśli pominiesz to w nowej implementacji klasy, twój
atrybuty nie zostaną poprawnie zainicjowane.

Chociaż opisaliśmy sposób tworzenia atrybutów, nadal nie opisaliśmy sposobu uzyskiwania dostępu
i zarządzać tymi wartościami. Na przykład nie ma globals.h plik nagłówkowy, gdzie to jest
przechowywane; atrybuty są przechowywane wraz z ich klasami. Pytania, które pojawiają się naturalnie, brzmią: jak
czy użytkownicy łatwo poznają wszystkie atrybuty swoich modeli i jak to robią
uzyskiwać dostęp do tych atrybutów lub dokumentować ich wartości jako część ich rejestru
symulacja?

Szczegółowa dokumentacja rzeczywistych atrybutów zdefiniowanych dla typu oraz globalna lista
wszystkie zdefiniowane atrybuty są dostępne w dokumentacji API. Na resztę tego
W tym dokumencie zademonstrujemy różne sposoby uzyskiwania i ustawiania atrybutu
wartości.

Oprawa Domyślnie Wartości
Konfiguracja::Ustaw domyślne oraz Wiersz poleceń
Przyjrzyjmy się, w jaki sposób skrypt użytkownika może uzyskać dostęp do określonej wartości atrybutu. Mamy zamiar
użyć src/point-to-point/examples/main-attribute-value.cc skrypt do ilustracji, z
usunięto niektóre szczegóły. The główny rozpoczyna się funkcja:

// To jest podstawowy przykład użycia systemu atrybutów do
// ustaw i pobierz wartość w podstawowym systemie; mianowicie niepodpisany
// liczba całkowita maksymalnej liczby pakietów w kolejce
//

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

// Domyślnie atrybut MaxPackets ma wartość 100 pakietów
// (to ustawienie domyślne można zaobserwować w funkcji DropTailQueue::GetTypeId)
//
// Tutaj ustawiamy na 80 pakietów. Możemy użyć jednego z dwóch typów wartości:
// wartość oparta na łańcuchu lub wartość typu U-integer
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", StringValue ("80"));
// Poniższe wywołanie funkcji jest zbędne
Config::SetDefault ("ns3::DropTailQueue::MaxPackets", UintegerValue (80));

// Zezwól użytkownikowi na zastąpienie dowolnych ustawień domyślnych i powyższych
// SetDefaults () w czasie wykonywania, za pomocą argumentów wiersza poleceń
// Na przykład przez "--ns3::DropTailQueue::MaxPackets=80"
cmd wiersza poleceń;
// Zapewnia to jeszcze jeden sposób ustawienia wartości z wiersza poleceń:
cmd.AddValue ("maxPackets", "ns3::DropTailQueue::MaxPackets");
cmd.Parse (argc, argv);

Najważniejszą rzeczą, na którą należy zwrócić uwagę w powyższym, są dwa równoważne wywołania do Konfiguracja::Ustaw domyślne
(). W ten sposób ustawiamy wartość domyślną dla wszystkich później tworzonych instancji
Upuść Kolejkę OgonaS. Ilustrujemy, że dwa rodzaje Wartość: zajęcia, a Wartość ciągu oraz
Wartość liczb całkowitych class, może służyć do przypisania wartości do atrybutu nazwanego przez
"ns3::DropTailQueue::MaxPackets".

Możliwe jest również manipulowanie atrybutami za pomocą Wiersz poleceń; widzieliśmy kilka przykładów
na początku samouczka. W szczególności łatwo jest dodać skrócony argument
imię, takie jak --maxPakiety, dla Atrybutu, który jest szczególnie odpowiedni dla Twojego modelu,
w tym przypadku "ns3::DropTailQueue::MaxPackets". Ma to dodatkową cechę, że
ciąg pomocy dla atrybutu zostanie wydrukowany jako część komunikatu o użyciu skryptu.
Aby uzyskać więcej informacji zobacz Wiersz poleceń Dokumentacja API.

Teraz utworzymy kilka obiektów przy użyciu niskopoziomowego API. Nasze nowo utworzone kolejki będą
nie mieć m_maxPakiety zainicjowany do 100 pakietów, jak zdefiniowano w pliku
DropTailQueue::GetTypeId () funkcji, ale do 80 pakietów, z powodu tego, co zrobiliśmy powyżej
wartości domyślne.:

Ptr n0 = UtwórzObiekt ();

Ptr net0 = Utwórz obiekt ();
n0->DodajUrządzenie (net0);

Ptr q = UtwórzObiekt ();
net0->Dodajkolejkę(q);

W tym momencie stworzyliśmy singiel Node (n0) i pojedynczy Urządzenie PointToPointNet
(net0) i dodał A Upuść Kolejkę Ogona (q) Do net0.

Konstruktorzy, Pomocnicy oraz Fabryka Obiektów
Dowolne kombinacje atrybutów można ustawić i pobrać z pomocnika i niskiego poziomu
Pszczoła; albo od samych konstruktorów:

Ptr p =
Utwórz obiekt z atrybutami
(„MinX”, podwójna wartość (-100.0),
„MinY”, podwójna wartość (-100.0),
„DeltaX”, DoubleValue (5.0),
„DeltaY”, podwójna wartość (20.0),
"Szerokość siatki", UintegerValue (20),
"Typ układu", StringValue ("RowFirst"));

lub z pomocniczych interfejsów API wyższego poziomu, takich jak:

mobilność. SetPositionAllocator
("ns3::GridPositionAllocator",
„MinX”, podwójna wartość (-100.0),
„MinY”, podwójna wartość (-100.0),
„DeltaX”, DoubleValue (5.0),
„DeltaY”, podwójna wartość (20.0),
"Szerokość siatki", UintegerValue (20),
"Typ układu", StringValue ("RowFirst"));

Nie ilustrujemy tego tutaj, ale możesz także skonfigurować plik Fabryka Obiektów z nowymi wartościami
dla określonych atrybutów. Instancje utworzone przez Fabryka Obiektów będzie ich miał
atrybuty ustawione podczas budowy. Jest to bardzo podobne do korzystania z jednego z pomocniczych interfejsów API
dla klasy.

Aby przejrzeć, istnieje kilka sposobów ustawiania wartości atrybutów instancji klas do be
stworzony in dotychczasowy przyszłość:

· Konfiguracja::Ustaw domyślne ()

· Wiersz poleceń::Dodajwartość ()

· Utwórz obiekt z atrybutami<> ()

· Różne pomocnicze API

Ale co, jeśli już utworzyłeś instancję i chcesz zmienić wartość
atrybut? W tym przykładzie, jak możemy manipulować plikiem m_maxPakiety wartość już
skonkretyzowane Upuść Kolejkę Ogona? Oto różne sposoby, aby to zrobić.

Wymiana pieniędzy Wartości
InteligentnyWskaźnik
Załóżmy, że inteligentny wskaźnik (Ptr) do odpowiedniego urządzenia sieciowego jest w zasięgu ręki; w aktualnej
na przykład jest to net0 wskaźnik.

Jednym ze sposobów zmiany wartości jest uzyskanie dostępu do wskaźnika do kolejki bazowej i zmodyfikowanie jej
atrybutów.

Najpierw zauważamy, że możemy uzyskać wskaźnik do (klasy bazowej) kolejka przez dotychczasowy
Urządzenie PointToPointNet atrybuty, gdzie to się nazywa „TxKolejka”:

Wartość wskaźnika tmp;
net0->GetAttribute("TxQueue", tmp);
Ptr txQueue = tmp. GetObject ();

Korzystanie z Pobierz obiekt () funkcji, możemy wykonać bezpieczny rzut w dół do a Upuść Kolejkę Ogona, Gdzie
„Maksymalna liczba pakietów” jest atrybutem:

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

Następnie możemy uzyskać wartość atrybutu w tej kolejce. Wprowadziliśmy opakowanie
Wartość: klasy dla bazowych typów danych, podobnie jak opakowania Java wokół tych typów,
ponieważ system atrybutów przechowuje wartości serializowane do łańcuchów, a nie różne typy.
Tutaj wartość atrybutu jest przypisana do a Wartość liczb całkowitychi Get () metoda na to
wartość tworzy (rozpakowane) uint32_t.:

UintegerWartość graniczna;
dtq->GetAttribute ("MaxPackets", limit);
NS_LOG_INFO ("1. dtq limit: " << limit.Get () << " pakiety");

Zauważ, że powyższy downcast nie jest tak naprawdę potrzebny; moglibyśmy uzyskać atrybut
wartość bezpośrednio z txKolejka, który jest przedmiot:

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

Ustawmy teraz inną wartość (60 pakietów):

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

Config Przestrzeń nazw ścieżka
Alternatywnym sposobem uzyskania atrybutu jest użycie przestrzeni nazw konfiguracji. Tutaj,
ten atrybut znajduje się na znanej ścieżce w tej przestrzeni nazw; to podejście jest przydatne, jeśli jedno
nie ma dostępu do podstawowych wskaźników i chciałby skonfigurować określony
atrybut z pojedynczą instrukcją.:

Config::Set ("/NodeList/0/DeviceList/0/TxQueue/MaxPackets",
UcałkowitaWartość(25));
txQueue->GetAttribute("MaxPackets", limit);
NS_LOG_INFO ("4. Limit txQueue zmieniony przez przestrzeń nazw: "
<< limit.Get () << " pakiety");

Ścieżka konfiguracji często ma postać ".../
nazwa>/ /.../ / " odnosić się do konkretnej instancji za pomocą indeksu an
przedmiot w pojemniku. W tym przypadku pierwszym kontenerem jest lista wszystkich Nodes; ten
drugi kontener to lista wszystkich Urządzenie sieciowes na wybranych Node. Wreszcie
ścieżka konfiguracji zwykle kończy się kolejnym atrybutem elementu członkowskiego, w tym przypadku
„Maksymalna liczba pakietów” atrybut „TxKolejka” wybranych Urządzenie sieciowe.

Mogliśmy również użyć symboli wieloznacznych, aby ustawić tę wartość dla wszystkich węzłów i wszystkich urządzeń sieciowych
(co w tym prostym przykładzie ma taki sam efekt jak poprzedni Konfiguracja::Ustaw ()):

Config::Set ("/NodeList/*/DeviceList/*/TxQueue/MaxPackets",
UcałkowitaWartość(15));
txQueue->GetAttribute("MaxPackets", limit);
NS_LOG_INFO ("5. Limit txQueue zmieniony przez wieloznaczną przestrzeń nazw: "
<< limit.Get () << " pakiety");

przedmiot Imię i nazwisko Usługi
Innym sposobem na uzyskanie dostępu do atrybutu jest skorzystanie z narzędzia obsługi nazw obiektów. The
Usługa nazw obiektów umożliwia nam dodawanie elementów do przestrzeni nazw konfiguracyjnych pod nazwą
"/Nazwy/" path ze zdefiniowanym przez użytkownika ciągiem nazwy. To podejście jest przydatne, jeśli nie
mają dostęp do podstawowych wskaźników i trudno jest określić wymagane
konkretna ścieżka przestrzeni nazw konfiguracji.

Nazwy::Dodaj ("serwer", n0);
Nazwy::Dodaj("serwer/eth0", net0);

...

Config::Set ("/Nazwy/serwer/eth0/TxQueue/MaxPackets", UintegerValue (25));

Tutaj dodaliśmy elementy ścieżki "serwer" oraz "eth0" pod "/Nazwy/" wtedy przestrzeń nazw
użył wynikowej ścieżki konfiguracji do ustawienia atrybutu.

Zobacz Nazwy obiektów, aby uzyskać pełniejsze omówienie ns-3 przestrzeń nazw konfiguracji.

Wdrożenie Szczegóły
Wartość: Zajęcia
Czytelnicy zwrócą uwagę na TypWartość klasy, które są podklasami Wartość atrybutu baza
klasa. Można je traktować jako klasy pośrednie, które są używane do konwersji z surowego
typy do Wartość atrybutus, które są używane przez system atrybutów. Przypomnij sobie, że to
baza danych zawiera obiekty wielu typów serializowane do łańcuchów. Konwersje do tego typu
można to zrobić za pomocą klasy pośredniej (takiej jak Wartość całkowitalub Podwójna wartość dla
liczby zmiennoprzecinkowe) lub przez smyczki. Bezpośrednia niejawna konwersja typów do
Wartość atrybutu nie jest tak naprawdę praktyczny. Tak więc w powyższym użytkownicy mają wybór użycia
ciągi znaków lub wartości:

p->Set ("cwnd", StringValue ("100")); // ustawiacz oparty na łańcuchu znaków
p->Set ("cwnd", IntegerValue (100)); // seter oparty na liczbach całkowitych

System udostępnia kilka makr, które pomagają użytkownikom zadeklarować i zdefiniować nową wartość AttributeValue
podklasy dla nowych typów, które chcą wprowadzić do systemu atrybutów:

· ATTRIBUTE_HELPER_HEADER

· ATTRIBUTE_HELPER_CPP

Więcej informacji można znaleźć w dokumentacji interfejsu API tych konstrukcji.

Inicjalizacji Zamówienie
Atrybuty w systemie nie mogą zależeć od stanu żadnego innego atrybutu w tym
system. Dzieje się tak, ponieważ kolejność inicjalizacji atrybutów nie jest określona ani
wymuszone przez system. Konkretny tego przykład można zobaczyć w konfiguracji automatycznej
programy takie jak Magazyn konfiguracji. Chociaż dany model może to tak ułożyć, że Attributes
są inicjowane w określonej kolejności, może zadecydować o tym inny automatyczny konfigurator
niezależnie, aby zmienić Atrybuty, na przykład w kolejności alfabetycznej.

Z powodu tego niespecyficznego uporządkowania żaden Atrybut w systemie nie może być zależny
na jakimkolwiek innym Atrybucie. W związku z tym ustawiacze atrybutów nigdy nie mogą zawieść z powodu stanu
innego Atrybutu. Żaden ustawiacz atrybutów nie może zmienić (ustawić) żadnej innej wartości atrybutu jako a
wyniku zmiany jego wartości.

Jest to bardzo silne ograniczenie i są przypadki, w których Atrybuty muszą być ustawione
konsekwentnie, aby umożliwić prawidłowe działanie. W tym celu zezwalamy na sprawdzanie spójności
jeśli chodzi o komunikację i motywację dotychczasowy atrybut is używany (cf. NS_ASSERT_MSG or NS_ABORT_MSG).

Ogólnie rzecz biorąc, kod atrybutu do przypisania wartości do podstawowych zmiennych składowych klasy
jest wykonywany po zbudowaniu obiektu. Ale co, jeśli potrzebujesz przypisanych wartości
przed wykonaniem ciała konstruktora, ponieważ potrzebujesz ich w logice
konstruktor? Jest na to sposób, używany np. w klasie Magazyn konfiguracji: dzwonić
ObjectBase::ConstructSelf () w sposób następujący:

ConfigStore::ConfigStore ()
{
ObjectBase::ConstructSelf (AttributeConstructionList());
// kontynuuj z konstruktorem.
}

Pamiętaj, że obiekt i wszystkie jego klasy pochodne muszą również implementować a PobierzInstanceTypeId
() metoda. W przeciwnym razie ObjectBase::ConstructSelf () nie będzie w stanie przeczytać
atrybuty.

Dodawanie Atrybuty
ns-3 system umieści pewną liczbę wartości wewnętrznych w systemie atrybutów, ale
niewątpliwie użytkownicy będą chcieli to rozszerzyć, aby wybrać te, które przegapiliśmy, lub dodać swoje
własne klasy do systemu.

Istnieją trzy typowe przypadki użycia:

· Udostępnianie istniejącego członka danych klasy jako atrybutu, jeśli jeszcze nie jest.

· Tworzenie nowej klasy zdolnej do wystawiania niektórych członków danych jako atrybutów poprzez nadanie jej TypeId.

· Tworzenie Wartość atrybutu podklasę dla nowej klasy, aby można było uzyskać do niej dostęp jako plik
Atrybut.

Istniejący Członek Zmienna
Rozważ tę zmienną w Gniazdo Tcp:

uint32_t m_cWnd; // Okno przeciążenia

Załóżmy, że ktoś pracujący z TCP chciał uzyskać lub ustawić wartość tej zmiennej
za pomocą systemu metadanych. Gdyby nie zostało to już dostarczone przez ns-3, użytkownik mógł zadeklarować
następujący dodatek w systemie metadanych środowiska wykonawczego (do pliku GetTypeId() definicja dla
Gniazdo Tcp):

.AddAttribute („Okno przeciążenia”,
"Okno przeciążenia TCP (bajty)",
UcałkowitaWartość(1),
MakeUintegerAccessor (&TcpSocket::m_cWnd),
MakeUintegerChecker ())

Teraz użytkownik ze wskaźnikiem do a Gniazdo Tcp instancja może wykonywać operacje, takie jak
ustawienie i uzyskanie wartości bez konieczności jawnego dodawania tych funkcji.
Ponadto można zastosować kontrolę dostępu, taką jak umożliwienie odczytu parametru i
nie zapisane lub można zastosować sprawdzanie granic dopuszczalnych wartości.

Nowości Klasa TypId
Tutaj omawiamy wpływ na użytkownika, który chce dodać nową klasę ns-3. Co
dodatkowe rzeczy muszą być zrobione, aby umożliwić mu przechowywanie atrybutów?

Załóżmy, że nasza nowa klasa, tzw ns3::Moja mobilność, jest rodzajem modelu mobilności. Pierwszy,
klasa powinna dziedziczyć po swojej klasie nadrzędnej, ns3::Model mobilności, w moja-mobilność.h
plik nagłówkowy:

przestrzeń nazw ns3 {

klasa MyClass: publiczny model mobilności
{

Wymaga to zadeklarowania PobierzTypeId () funkcjonować. Jest to jednowierszowa funkcja publiczna
deklaracja:

publiczny:
/ **
* Zarejestruj ten typ.
* \return Obiekt TypeId.
*/
statyczny TypeId GetTypeId (pusty);

Przedstawiliśmy już, co a TypId definicja będzie wyglądać jak w pliku my-mobility.cc
plik implementacyjny:

NS_OBJECT_ENSURE_REGISTERED (Moja mobilność);

TypId
MyMobility::GetTypeId (puste)
{
static TypeId tid = TypeId ("ns3::MyMobility")
.Ustaw Rodzica ()
.SetGroupName („Mobilność”)
.AddConstructor ()
.AddAttribute("Granice",
"Granice obszaru do rejsu.",
wartość prostokąta (prostokąt (0.0, 0.0, 100.0, 100.0)),
MakeRectangleAccessor (&MyMobility::m_bounds),
MakeRectangleChecker ())
.AddAttribute("Czas",
"Zmień bieżący kierunek i prędkość po ruchu dla tego opóźnienia.",
Wartość czasu (sekundy (1.0)),
MakeTimeAccessor (&MyMobility::m_modeTime),
MakeTimeChecker ())
// etc (więcej parametrów).
;
pora powrotu;
}

Jeśli nie chcemy tworzyć podklasy z istniejącej klasy, w pliku nagłówkowym po prostu dziedziczymy
od ns3::Obiekt, aw pliku obiektowym ustawiamy klasę nadrzędną na ns3::Obiekt w
.Ustaw Rodzica ().

Typowe błędy tutaj obejmują:

· Nie dzwonię NS_OBJECT_ENSURE_REGISTERED ()

· Nie dzwoniąc do Ustaw Rodzica () metody lub wywołując ją z niewłaściwym typem.

· Nie dzwoniąc do Dodaj Konstruktor () metody lub wywołując ją z niewłaściwym typem.

· Wprowadzenie błędu pisarskiego w nazwie TypId w jego konstruktorze.

· Nieużywanie w pełni kwalifikowanej nazwy typu C++ otaczającej klasy C++ jako nazwy
TypId, Należy pamiętać, że "ns3::" jest wymagane.

Żaden z tych błędów nie może zostać wykryty przez ns-3 codebase, więc użytkownicy powinni to sprawdzić
dokładnie kilka razy, że zrobili to dobrze.

Nowości Wartość atrybutu Typ
Z perspektywy użytkownika, który pisze nową klasę w systemie i chce, żeby taka była
dostępne jako atrybut, jest głównie kwestia zapisu konwersji do/z
łańcuchy znaków i wartości atrybutów. Większość z tego można skopiować/wkleić za pomocą kodu z makrą. Dla
na przykład rozważ deklarację klasy dla Prostokąt src/mobilność/model katalog:

Nagłówek filet
/ **
* \krótki prostokąt 2d
*/
klasa Prostokąt
{
...

podwójny xMin;
podwójny xMax;
podwójna yMin;
podwójny yMaks;
};

Aby to zrobić, należy dodać jedno wywołanie makra i dwóch operatorów pod deklaracją klasy
zamień prostokąt w wartość użyteczną przez Atrybut system:

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

ATTRIBUTE_HELPER_HEADER (prostokąt);

Wdrożenie filet
W definicji klasy (. DC plik) kod wygląda następująco:

ATTRIBUTE_HELPER_CPP (prostokąt);

std::ostream &
operator << (std::ostream &os, const Rectangle &rectangle)
{
os << prostokąt.xMin << "|" << prostokąt.xMax << "|" << prostokąt.yMin << "|"
<< prostokąt.yMax;
powrót do systemu operacyjnego;
}
std::istream &
operator >> (std::istream &is, Prostokąt &prostokąt)
{
znak c1, c2, c3;
to >> prostokąt.xMin >> c1 >> prostokąt.xMax >> c2 >> prostokąt.yMin >> c3
>> prostokąt.yMax;
jeśli (c1 != '|' ||
c2 != '|' ||
c3 != '|')
{
is.setstate (std::ios_base::failbit);
}
powrót jest;
}

Te operatory strumieni po prostu konwertują z ciągu reprezentującego Rectangle
("xMin|xMaks|yMin|yMaks") do bazowego prostokąta. Modelarz musi je określić
operatory i łańcuchową reprezentację składni instancji nowej klasy.

Magazyn konfiguracji
Wartości dla ns-3 atrybuty mogą być przechowywane w pliku tekstowym ASCII lub XML i ładowane do pliku
przebieg symulacji w przyszłości. Ta funkcja jest znana jako ns-3 Magazyn konfiguracji. The Magazyn konfiguracji is
wyspecjalizowana baza danych dla wartości atrybutów i wartości domyślnych.

Chociaż jest to oddzielnie obsługiwany moduł w src/config-store/ katalog, my
udokumentować to tutaj ze względu na jego wyłączną zależność od ns-3 podstawowy moduł i atrybuty.

Możemy zbadać ten system na przykładzie z
src/config-store/examples/config-store-save.cc.

Po pierwsze, wszyscy użytkownicy Magazyn konfiguracji musi zawierać następujące oświadczenie:

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

Następnie ten program dodaje przykładowy obiekt Przykład konfiguracji aby pokazać, jak system jest rozszerzony:

class ConfigExample: obiekt publiczny
{
publiczny:
statyczny TypeId GetTypeId (void) {
static TypeId tid = TypeId ("ns3::A")
.Ustaw Rodzica ()
.AddAttribute("TestInt16", "tekst pomocy",
Wartość całkowita (-2),
MakeIntegerAccessor (&A::m_int16),
MakeIntegerChecker ())
;
pora powrotu;
}
int16_t m_int16;
};

NS_OBJECT_ENSURE_REGISTERED (przykład konfiguracji);

Następnie używamy podsystemu Config do nadpisania ustawień domyślnych na kilka sposobów:

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

Ptr a_obj = UtwórzObiekt ();
NS_ABORT_MSG_UNLESS (a_obj->m_int16 == -5,
„Nie można ustawić atrybutu liczby całkowitej ConfigExample za pomocą Config::SetDefault”);

Ptr a2_obj = UtwórzObiekt ();
a2_obj->SetAttribute("TestInt16", IntegerValue (-3));
Wartość całkowita iv;
a2_obj->GetAttribute("TestInt16", iv);
NS_ABORT_MSG_UNLESS (iv. Get () == -3,
„Nie można ustawić atrybutu liczby całkowitej ConfigExample za pomocą SetAttribute”);

Następna instrukcja jest konieczna, aby upewnić się, że (jeden z) utworzonych obiektów jest zrootowany
w przestrzeni nazw konfiguracji jako instancja obiektu. Zwykle dzieje się tak, gdy ty
agregować obiekty do a ns3::węzeł or ns3::kanał przykład, ale tutaj, ponieważ pracujemy
na poziomie rdzenia musimy utworzyć nowy główny obiekt przestrzeni nazw:

Config::RegisterRootNamespaceObject (a2_obj);

Pisanie
Następnie chcemy wyprowadzić magazyn konfiguracji. Przykłady pokazują, jak to zrobić na dwie części
formaty, XML i surowy tekst. W praktyce krok ten należy wykonać tuż przed wywołaniem
Symulator::Uruchom () aby zapisać ostateczną konfigurację tuż przed uruchomieniem symulacji.

Istnieją trzy atrybuty, które regulują zachowanie ConfigStore: "Tryb",
"Nazwa pliku", "Format pliku". Tryb (domyślny "Żaden") konfiguruje, czy ns-3 powinien
wczytać konfigurację z wcześniej zapisanego pliku (określ „Tryb = ładowanie”) lub zapisz go w pliku
(sprecyzować „Tryb = Zapisz”). Nazwa pliku (domyślnie "") to miejsce, w którym ConfigStore powinien przeczytać lub
napisz jego dane. Format pliku (domyślnie „surowy tekst”) określa, czy format ConfigStore
jest zwykłym tekstem lub Xml („Format pliku=Xml”)

Przykład pokazuje:

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Zapisz"));
ConfigStore dane wyjścioweKonfiguracja;
outputConfig.ConfigureDefaults();
outputConfig.ConfigureAttributes();

// Wyjście magazynu konfiguracji do formatu txt
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.txt"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("RawText"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Zapisz"));
ConfigStore wyjście Config2;
OutputConfig2.ConfigureDefaults ();
OutputConfig2.ConfigureAttributes ();

Symulator::Uruchom ();

Symulator::Zniszcz ();

Zwróć uwagę na umieszczenie tych stwierdzeń tuż przed Symulator::Uruchom () komunikat.
To wyjście rejestruje wszystkie wartości tuż przed rozpoczęciem symulacji (czyli.
po zakończeniu całej konfiguracji).

Po uruchomieniu możesz otworzyć plik atrybuty-wyjściowe.txt plik i zobacz:

domyślnie ns3::RealtimeSimulatorImpl::SynchronizationMode "BestEffort"
domyślnie ns3::RealtimeSimulatorImpl::HardLimit "+100000000.0ns"
domyślnie ns3::PcapFileWrapper::CaptureSize "65535"
domyślnie ns3::PacketSocket::RcvBufSize "131072"
domyślnie ns3::ErrorModel::IsEnabled „prawda”
domyślnie ns3::RateErrorModel::ErrorUnit "EU_BYTE"
domyślnie ns3::RateErrorModel::ErrorRate "0"
domyślnie ns3::RateErrorModel::RanVar "Uniform:0:1"
domyślnie ns3::DropTailQueue::Tryb „Pakiety”
domyślnie ns3::DropTailQueue::MaxPackets "100"
domyślnie ns3::DropTailQueue::MaxBytes "6553500"
domyślnie ns3::Application::StartTime "+0.0ns"
domyślnie ns3::Application::StopTime "+0.0ns"
domyślnie ns3::ConfigStore::Tryb „Zapisz”
domyślnie ns3::ConfigStore::Filename "output-attributes.txt"
domyślnie ns3::ConfigStore::FileFormat "RawText"
domyślnie ns3::ConfigExample::TestInt16 "-5"
globalny RngSeed „1”
globalny RngRun „1”
global SimulatorImplementationType "ns3::DefaultSimulatorImpl"
globalny typ harmonogramu „ns3::MapScheduler”
globalna suma kontrolna włączona „fałsz”
wartość /$ns3::ConfigExample/TestInt16 "-3"

Powyżej przedstawiono wszystkie domyślne wartości atrybutów modułu podstawowego.
Następnie wszystkie wartości dla ns-3 rejestrowane są wartości globalne. Wreszcie wartość tzw
wystąpienie Przykład konfiguracji który został zakorzeniony w przestrzeni nazw konfiguracji. W
real ns-3 program, wyświetliłoby się znacznie więcej modeli, atrybutów i wartości domyślnych.

Wersja XML istnieje również w atrybuty-wyjściowe.xml:




























Plik ten można zarchiwizować wraz ze skryptem symulacji i danymi wyjściowymi.

Czytający
Następnie omówimy konfigurowanie symulacji przez zapisany plik konfiguracyjny wejścia. Tam są
kilka kluczowych różnic w porównaniu z pisaniem ostatecznej konfiguracji symulacji.
Najpierw musimy umieścić takie stwierdzenia na początku programu, przed
zapisywane są instrukcje konfiguracji symulacji (tak więc wartości są rejestrowane przed
stosowane w budownictwie obiektowym).

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Załaduj"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigZapisz dane wejścioweConfig;
inputConfig.ConfigureDefaults ();

Następnie zauważ, że ładowanie wejściowych danych konfiguracyjnych jest ograniczone do domyślnego atrybutu (czyli.
nie wystąpienia) wartości i wartości globalne. Wartości wystąpień atrybutów nie są obsługiwane
ponieważ na tym etapie symulacji, zanim jakiekolwiek obiekty zostaną zbudowane, nie ma ich
wokół takich instancji obiektów. (Uwaga: przyszłe ulepszenia magazynu konfiguracji mogą ulec zmianie
to zachowanie).

Po drugie, podczas gdy wyjście Magazyn konfiguracji state wyświetli listę wszystkiego w bazie danych, plik
plik wejściowy musi zawierać tylko określone wartości, które mają zostać zastąpione. Więc jeden sposób użycia
ta klasa do konfiguracji pliku wejściowego służy do generowania początkowej konfiguracji przy użyciu
wyjście ("Zapisać") "Tryb" opisany powyżej, wyodrębnij z tego pliku konfiguracyjnego tylko plik
elementy, które chcesz zmienić, i przenieś te minimalne elementy do nowego pliku konfiguracyjnego
które można następnie bezpiecznie edytować i wczytywać w kolejnym przebiegu symulacji.

Podczas Magazyn konfiguracji obiekt jest tworzony, jego atrybuty "Nazwa pliku", "Tryb",
"Format pliku" też trzeba ustawić przez wiersz poleceń lub przez deklaracje programowe.

Czytanie/pisanie Przykład
Jako bardziej skomplikowany przykład załóżmy, że chcemy czytać w konfiguracji
defaults z pliku wejściowego o nazwie input-defaults.xmli wypisz otrzymany wynik
atrybuty do oddzielnego pliku o nazwie atrybuty-wyjściowe.xml.:

#include "ns3/config-store-module.h"
...
int główny (...)
{

Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("input-defaults.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Załaduj"));
Config::SetDefault ("ns3::ConfigStore::FileFormat", StringValue ("Xml"));
ConfigZapisz dane wejścioweConfig;
inputConfig.ConfigureDefaults ();

//
// Zezwól użytkownikowi na zastąpienie dowolnych ustawień domyślnych i powyższego Bind () at
// czas wykonywania, poprzez argumenty wiersza poleceń
//
cmd wiersza poleceń;
cmd.Parse (argc, argv);

// konfiguracja topologii
...

// Wywołaj tuż przed wejściem do Simulator::Run ()
Config::SetDefault ("ns3::ConfigStore::Filename", StringValue ("output-attributes.xml"));
Config::SetDefault ("ns3::ConfigStore::Mode", StringValue ("Zapisz"));
ConfigStore dane wyjścioweKonfiguracja;
outputConfig.ConfigureAttributes();
Symulator::Uruchom ();
}

Magazyn konfiguracji GUI
Istnieje frontend oparty na GTK dla ConfigStore. Dzięki temu użytkownicy mogą używać GUI do
uzyskiwać dostęp i zmieniać zmienne. Zrzuty ekranu tej funkcji są dostępne w |ns3|
Omówienie prezentacja.

Aby korzystać z tej funkcji, należy ją zainstalować libgtk oraz libgtk-dev; przykład Ubuntu
polecenie instalacji to:

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

Aby sprawdzić, czy jest skonfigurowany, czy nie, sprawdź dane wyjściowe kroku:

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

---- Podsumowanie opcjonalnych funkcji NS-3:
Powiązania Pythona: włączone
Obsługa skanowania interfejsu API języka Python: włączona
Integracja NS-3 Click: włączona
GtkConfigStore: nie włączono (biblioteka „gtk+-2.0 >= 2.12” nie została znaleziona)

W powyższym przykładzie nie została włączona, więc nie można jej używać, dopóki nie pojawi się odpowiednia wersja
zainstalowany i:

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

jest powtórzony.

Użycie jest prawie takie samo jak w przypadku wersji nieopartej na GTK, ale ich nie ma Magazyn konfiguracji
zaangażowane atrybuty:

// Wywołaj tuż przed wejściem do Simulator::Run ()
Konfiguracja GtkConfigStore;
config.ConfigureDefaults();
config.ConfigureAttributes();

Teraz, po uruchomieniu skryptu, powinien pojawić się graficzny interfejs użytkownika, umożliwiający otwieranie menu
atrybuty na różnych węzłach/obiektach, a następnie uruchom wykonanie symulacji, kiedy ty
są skończone.

Przyszłość praca
Istnieje kilka możliwych ulepszeń:

· Zapisz unikalny numer wersji z datą i godziną na początku pliku.

· Zapisz gdzieś początkowy rng.

· Spraw, aby każda zmienna losowa serializowała swoje własne początkowe ziarno i ponownie je przeczytaj później.

przedmiot Nazwy
zastępczy publikacji naukowej

Logowanie
ns-3 funkcja rejestrowania może służyć do monitorowania lub debugowania postępu symulacji
programy. Dane wyjściowe rejestrowania można włączyć za pomocą instrukcji programu w pliku Głównym () program lub
za pomocą NS_LOG zmienna środowiskowa.

Instrukcje rejestrowania nie są kompilowane w zoptymalizowane kompilacje ns-3. Aby użyć rejestrowania, jeden
musi zbudować (domyślną) kompilację debugowania ns-3.

Projekt nie gwarantuje, że dane wyjściowe rejestrowania pozostaną niezmienione
czas. Ostrzega się użytkowników przed budowaniem struktur wyjściowych symulacji na podstawie rejestrowania
kodu, ponieważ wyjście i sposób włączenia wyjścia mogą się zmieniać w czasie.

Omówienie
ns-3 instrukcje rejestrowania są zwykle używane do rejestrowania różnych zdarzeń związanych z wykonaniem programu, takich jak
jako wystąpienie zdarzeń symulacyjnych lub użycie określonej funkcji.

Na przykład ten fragment kodu pochodzi z Protokół IPv4L3::IsDestinationAddress():

if (adres == iaddr. GetBroadcast ())
{
NS_LOG_LOGIC ("Dla mnie (adres rozgłoszeniowy interfejsu)");
return true;
}

Jeśli logowanie zostało włączone dla Protokół IPv4L3 składnik o nasileniu LOGIKA or
powyżej (zobacz poniżej o istotności dziennika), zestawienie zostanie wydrukowane; inaczej to
zostanie stłumiony.

Włączanie Wydajność
Istnieją dwa sposoby, w jakie użytkownicy zwykle kontrolują dane wyjściowe dziennika. Pierwszym z nich jest ustawienie
NS_LOG Zmienna środowiskowa; np:

$ NS_LOG="*" ./waf --uruchom najpierw

będzie prowadził drugim samouczek ze wszystkimi wynikami logowania. (specyfika tzw NS_LOG
format zostanie omówiony poniżej).

Można to uczynić bardziej szczegółowym, wybierając poszczególne komponenty:

$ NS_LOG="Ipv4L3Protocol" ./waf --uruchom najpierw

Dane wyjściowe można dodatkowo dostosować za pomocą opcji prefiksu.

Drugim sposobem włączenia rejestrowania jest użycie w programie wyraźnych instrukcji, takich jak in
dotychczasowy drugim program samouczka:

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

(Znaczenie LOG_LEVEL_INFOi inne możliwe wartości zostaną omówione poniżej.)

NS_LOG Składnia
NS_LOG zmienna środowiskowa zawiera listę składników dziennika i opcji. Dziennik
komponenty są oddzielone znakami `:':

$ NS_LOG=" : ...”

Opcje dla każdego składnika dziennika są podane jako flagi po każdym składniku dziennika:

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

Opcje kontrolują ważność i poziom tego składnika oraz to, czy są opcjonalne
należy uwzględnić informacje, takie jak czas symulacji, węzeł symulacji, funkcja
imię i symboliczną dotkliwość.

Zaloguj Komponenty
Ogólnie składnik dziennika odnosi się do pojedynczego kodu źródłowego . DC plik i obejmuje
cały plik.

Niektórzy pomocnicy mają specjalne metody umożliwiające logowanie wszystkich komponentów w module,
obejmujące różne jednostki kompilacji, ale logicznie pogrupowane, takie jak ns-3
kod Wi-Fi:

WifiHelper WifiHelper;
wifiHelper.EnableLogComponents ();

NS_LOG znak wieloznaczny `*' komponentu log włączy wszystkie komponenty.

Aby zobaczyć, jakie składniki dziennika są zdefiniowane, dowolny z nich będzie działał:

$ NS_LOG="lista-druku" ./waf --uruchom...

$ NS_LOG="foo" # token nie pasujący do żadnego składnika dziennika

Pierwszy formularz wydrukuje nazwę i włączone flagi dla wszystkich komponentów dziennika, które są
połączony w; spróbuj z symulator zarysowania. Drugi formularz drukuje wszystkie zarejestrowane dzienniki
komponenty, a następnie zakończ z błędem.

Dotkliwość oraz Poziom Opcje
Poszczególne wiadomości należą do jednej „klasy istotności” ustawionej przez makro tworzące plik
wiadomość. W powyższym przykładzie NS_LOG_LOGIC(..) tworzy wiadomość w LOG_LOGIC
klasa ciężkości.

Następujące klasy istotności są zdefiniowane jako wyliczanie stałe:

┌───────────────┬─────────────────────── ───────────┐
│Klasa ważności │ Znaczenie │
├───────────────┼─────────────────────── ────────── ─┤
LOG_BRAK │ Domyślnie, bez logowania │
├───────────────┼─────────────────────── ────────── ─┤
BŁĄD_LOGU │ Tylko poważne komunikaty o błędach │
├───────────────┼─────────────────────── ────────── ─┤
LOG_WARN │ Komunikaty ostrzegawcze │
├───────────────┼─────────────────────── ────────── ─┤
LOG_DEBUG │ Do użytku podczas debugowania │
├───────────────┼─────────────────────── ────────── ─┤
LOG_INFO │ Informacyjny │
├───────────────┼─────────────────────── ────────── ─┤
FUNKCJA_LOGU │ Śledzenie funkcji │
├───────────────┼─────────────────────── ────────── ─┤
LOG_LOGIC │ Kontroluj śledzenie przepływu w │
│ │ funkcje │
└───────────────┴─────────────────────── ───────────┘

Zazwyczaj chce się widzieć komunikaty w danej klasie istotności oraz wyższy. Odbywa się to przez
definiowanie „poziomów” rejestrowania włączającego:

┌───────────────────┬─────────────────── ────────── ─────┐
│Poziom │ Znaczenie │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_ERROR │ Tylko BŁĄD_LOGU klasa ciężkości │
│ │ wiadomości. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_WARNLOG_WARN i powyżej. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_DEBUGLOG_DEBUG i powyżej. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_INFOLOG_INFO i powyżej. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_FUNCTIONFUNKCJA_LOGU i powyżej. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_LOGICLOG_LOGIC i powyżej. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_LEVEL_ALL │ Wszystkie klasy istotności. │
├───────────────────┼─────────────────── ────────── ─────┤
LOG_ALL │ synonim dla LOG_LEVEL_ALL
└───────────────────┴─────────────────── ───────────────┘

Opcje klasy i poziomu istotności można podać w pliku NS_LOG zmienna środowiskowa wg
te żetony:

┌─────────┬────────────────┐
│Klasa │ Poziom │
├─────────┼────────────────┤
błądpoziom_błąd
├─────────┼────────────────┤
ostrzecpoziom_ostrzeżenie
├─────────┼────────────────┤
debugpoziom_debug
├─────────┼────────────────┤
Informacjeinformacje o poziomie
├─────────┼────────────────┤
funkcjonowaćfunkcja_poziomu
├─────────┼────────────────┤
logikalogika_poziomu
├─────────┼────────────────┤
│ │. poziom_wszystko
│ │. cała kolekcja
│ │. *
└─────────┴────────────────┘

Użycie tokenu klasy istotności umożliwia włączenie komunikatów dziennika tylko w tej istotności. Na przykład,
NS_LOG="*=ostrzeżenie" nie wyświetli komunikatów z ważnością błąd. NS_LOG="*=poziom_debugowania" będzie
komunikaty wyjściowe na poziomach istotności debug i powyżej.

Klasy i poziomy ważności można łączyć za pomocą `|' operator:
NS_LOG="*=poziom_warn|logika" wyświetli komunikaty na poziomach istotności błąd, ostrzec oraz logika.

NS_LOG symbol wieloznaczny poziomu ważności `*' i cała kolekcja są synonimami słowa poziom_wszystko.

W przypadku składników dziennika wymienionych tylko w NS_LOG

$ NS_LOG=" :..."

domyślna dotkliwość to LOG_LEVEL_ALL.

Prefiks Opcje
Szereg prefiksów może pomóc w określeniu, gdzie i kiedy wiadomość pochodzi oraz w jakim miejscu
surowość.

Dostępne opcje prefiksu (np wyliczanie stałe) są

┌─────────────────┬───────────────────── ─────────────┐
│Symbol przedrostka │ Znaczenie │
├─────────────────┼───────────────────── ─────────────┤
LOG_PREFIX_FUNC │ Przedstaw nazwę dzwoniącego │
│ │ funkcja. │
├─────────────────┼───────────────────── ─────────────┤
LOG_PREFIX_TIME │ Przedstaw czas symulacji. │
├─────────────────┼───────────────────── ─────────────┤
LOG_PREFIX_NODE │ Przedstaw identyfikator węzła. │
├─────────────────┼───────────────────── ─────────────┤
LOG_PREFIX_LEVEL │ Przedstaw poziom ważności. │
├─────────────────┼───────────────────── ─────────────┤
LOG_PREFIX_ALL │ Włącz wszystkie prefiksy. │
└─────────────────┴───────────────────── ─────────────┘

Opcje prefiksu zostały pokrótce opisane poniżej.

Opcje można podać w NS_LOG zmienną środowiskową za pomocą tych tokenów:

┌─────────────┬───────────┐
│Token │ Zastępca │
├─────────────┼───────────┤
prefix_funkcjafunkcjonować
├─────────────┼───────────┤
prefiks_czasczas
└─────────────┴───────────┘

węzeł_prefiksuwęzeł
├─────────────┼───────────┤
poziom_przedrostkapoziom
├─────────────┼───────────┤
prefix_allcała kolekcja
│ │. *
└─────────────┴───────────┘

W przypadku składników dziennika wymienionych tylko w NS_LOG

$ NS_LOG=" :..."

domyślne opcje prefiksu to LOG_PREFIX_ALL.

Dotkliwość Prefiks
Do opcji można dołączyć klasę istotności komunikatu poziom_przedrostka or poziom.
Na przykład ta wartość NS_LOG umożliwia logowanie dla wszystkich komponentów dziennika (`*') i wszystkich
klasy ważności (=wszystkie) i poprzedza komunikat klasą istotności (|poziom_przedrostka).

$ NS_LOG="*=all|prefix_level" ./waf --run scratch-symulator
Symulator Scratcha
komunikat o błędzie [BŁĄD].
[OSTRZEŻENIE] komunikat ostrzegawczy
[DEBUGOWANIE] komunikat debugowania
[INFO] komunikat informacyjny
Komunikat funkcji [FUNKCJA].
komunikat logiczny [LOGIC].

Czas Prefiks
Czas symulacji można uwzględnić w opcjach prefiks_czas or czas. Spowoduje to wydrukowanie
czas symulacji w sekundach.

Node Prefiks
Identyfikator węzła symulacji można dołączyć do opcji węzeł_prefiksu or węzeł.

Funkcjonować Prefiks
Do opcji można dołączyć nazwę funkcji wywołującej prefix_funkcja or funkcjonować.

NS_LOG Symbole wieloznaczne
Symbol wieloznaczny składnika dziennika `*' włączy wszystkie składniki. Aby włączyć wszystkie komponenty w a
użycie określonego poziomu ważności *=.

Symbol wieloznaczny opcji poziomu ważności `*' jest synonimem cała kolekcja. To musi nastąpić przed jakimkolwiek
`|' znaki oddzielające opcje. Aby włączyć wszystkie klasy ważności, użyj =*,
or =*|.

Opcja wieloznaczna `*' lub token cała kolekcja włącza wszystkie opcje prefiksu, ale musi wystąpić po a
`|' postać. Aby włączyć określoną klasę lub poziom istotności oraz wszystkie przedrostki, użyj
= |*.

Łączony symbol wieloznaczny opcji ** włącza wszystkie poziomy istotności i wszystkie prefiksy; Na przykład,
=**.

Uber-dzika karta *** włącza wszystkie poziomy ważności i wszystkie prefiksy dla wszystkich komponentów dziennika.
Wszystkie są równoważne:

$ NS_LOG="***" ... $ NS_LOG="*=wszystko|*" ... $ NS_LOG="*=*|wszystko" ...
$ NS_LOG="*=**" ... $ NS_LOG="*=poziom_all|*" ... $ NS_LOG="*=*|prefix_all" ...
$ NS_LOG="*=*|*" ...

Pamiętaj: nawet o banalnych rzeczach symulator zarysowania produkuje ponad 46 tys. linii wyjściowych
NS_LOG="***"!

W jaki sposób do Dodaj zalogowaniu do Twój kod
Dodanie logowania do kodu jest bardzo proste:

1. Wywołaj NS_LOG_COMPONENT_DEFINE (...); makro wewnątrz przestrzeń nazw Ns3.
Utwórz unikalny identyfikator ciągu (zwykle na podstawie nazwy pliku i/lub klasy
zdefiniowany w pliku) i zarejestruj go za pomocą wywołania makra, takiego jak poniżej:

przestrzeń nazw ns3 {

NS_LOG_COMPONENT_DEFINE („Protokół Ipv4L3”);
...

To się rejestruje Protokół IPv4L3 jako składnik dziennika.

(Makro zostało starannie napisane, aby umożliwić włączenie go wewnątrz lub na zewnątrz
przestrzeń nazw Ns3, a użycie będzie się różnić w zależności od bazy kodu, ale pierwotnym zamierzeniem było
zarejestruj to zewnętrzne przestrzeni nazw Ns3 w zasięgu globalnym pliku.)

2. Dodaj instrukcje rejestrowania (wywołania makr) do swoich funkcji i treści funkcji.

Logowanie Makra
Makra rejestrowania i powiązane poziomy ważności to:

┌───────────────┬─────────────────────────── ─┐
│Klasa ważności │ Makro │
├───────────────┼─────────────────────────── ─┤
LOG_BRAK │ (nie jest potrzebne) │
├───────────────┼─────────────────────────── ─┤
BŁĄD_LOGUNS_LOG_ERROR (...);
├───────────────┼─────────────────────────── ─┤
LOG_WARNNS_LOG_WARN (...);
├───────────────┼─────────────────────────── ─┤
LOG_DEBUGNS_LOG_DEBUG (...);
├───────────────┼─────────────────────────── ─┤
LOG_INFONS_LOG_INFO (...);
├───────────────┼─────────────────────────── ─┤
FUNKCJA_LOGUNS_LOG_FUNCTION (...);
├───────────────┼─────────────────────────── ─┤
LOG_LOGICNS_LOG_LOGIC (...);
└───────────────┴─────────────────────────── ─┘

Makra działają jak strumienie wyjściowe, więc możesz wysłać wszystko, do czego możesz wysłać std::out, Dołączył
by << operatorów, dozwolone jest:

void MyClass::Check (wartość int, char * element)
{
NS_LOG_FUNCTION (ten << arg << element);
jeśli (arg > 10)
{
NS_LOG_ERROR („napotkano złą wartość” << wartość <
" podczas sprawdzania " << nazwa << "!");
}
...
}

Należy pamiętać, że NS_LOG_FUNCTION automatycznie wstawia `,' (przecinek-spacja) separator pomiędzy
każdy jego argument. Upraszcza to rejestrowanie argumentów funkcji; po prostu połącz
je z << jak w powyższym przykładzie.

Bezwarunkowy Logowanie
Dla wygody, NS_LOG_UNCOND (...); makro zawsze będzie rejestrować swoje argumenty, nawet jeśli
powiązany składnik dziennika nie jest włączony na żadnym poziomie ważności. To makro nie używa żadnego
opcji przedrostka. Należy pamiętać, że rejestrowanie jest włączone tylko w kompilacjach debugowania; to makro
nie będzie generować danych wyjściowych w zoptymalizowanych kompilacjach.

Wytyczne
· Rozpocznij każdą metodę klasową od NS_LOG_FUNCTION (to << argumenty...); Umożliwia to łatwe
śledzenie wywołań funkcji.

· Z wyjątkiem: nie loguj operatorów ani jawnych konstruktorów kopiujących, ponieważ spowoduje to
nieskończona rekurencja i przepełnienie stosu.

· W przypadku metod bez argumentów użyj tej samej formy: NS_LOG_FUNCTION (Ten);

· Dla funkcji statycznych:

· Używając argumentów NS_LOG_FUNCTION (...); jak normalnie.

· Bez użycia argumentów NS_LOG_FUNCTION_NOARGS ();

· Posługiwać się NS_LOG_ERROR w przypadku poważnych błędów, które prawdopodobnie unieważniają symulację
wykonanie.

· Posługiwać się NS_LOG_WARN w przypadku nietypowych warunków, które można skorygować. Proszę o jakieś wskazówki
co do charakteru problemu i możliwości jego rozwiązania.

· NS_LOG_DEBUG jest zwykle używany w ad Hoc sposób na zrozumienie wykonania modelu.

· Posługiwać się NS_LOG_INFO aby uzyskać dodatkowe informacje na temat wykonania, takie jak rozmiar a
strukturę danych podczas dodawania/usuwania z niej.

· Posługiwać się NS_LOG_LOGIC do śledzenia ważnych gałęzi logicznych w funkcji.

· Sprawdź, czy zmiany w logowaniu nie łamią kodu. Uruchom kilka przykładowych programów za pomocą
wszystkie komponenty dziennika włączone (np NS_LOG="***").

Rysunek kalkowy
Podsystem śledzenia jest jednym z najważniejszych mechanizmów, które należy zrozumieć ns-3, w
w większości przypadków, ns-3 użytkownicy będą mieli genialny pomysł na nową i ulepszoną sieć
funkcja. Aby sprawdzić, czy ten pomysł działa, badacz dokona zmian w pliku
istniejącego systemu, a następnie przeprowadź eksperymenty, aby zobaczyć, jak nowa funkcja zachowuje się podczas gromadzenia danych
statystyki, które rejestrują zachowanie funkcji.

Innymi słowy, cały sens przeprowadzania symulacji polega na wygenerowaniu wyników do dalszych prac
badanie. W ns-3, podsystemem, który umożliwia badaczowi to zrobić, jest śledzenie
podsystem.

Rysunek kalkowy Motywacja
Istnieje wiele sposobów uzyskania informacji z programu. Najprostszym sposobem jest
po prostu bezpośrednio wydrukować informacje na standardowe wyjście, jak w:

#zawierać
...
wew główna ()
{
...
std::cout << "Wartość x to " << x << std::endl;
...
}

Jest to wykonalne w małych środowiskach, ale w miarę zwiększania się liczby symulacji
skomplikowane, kończy się to coraz większą liczbą wydruków oraz zadaniem analizowania i wykonywania
obliczenia na wyjściu stają się coraz trudniejsze.

Kolejną rzeczą do rozważenia jest to, że za każdym razem, gdy potrzebna jest nowa ciekawostka, rdzeń oprogramowania
należy zredagować i wprowadzić kolejny druk. Nie ma ujednoliconego sposobu kontrolowania wszystkiego
tej produkcji, zatem wielkość produkcji ma tendencję do nieograniczonego wzrostu. Ostatecznie,
przepustowość wymagana do prostego wysyłania tych informacji zaczyna ograniczać czas działania
symulacji. Pliki wyjściowe osiągają ogromne rozmiary, a ich analiza staje się plikiem
problemem.

ns-3 zapewnia prosty mechanizm rejestrowania i zapewnia pewną kontrolę nad danymi wyjściowymi poprzez
Zaloguj Komponenty , ale poziom kontroli wcale nie jest bardzo drobnoziarnisty. Logowanie
moduł jest stosunkowo tępym narzędziem.

Pożądane jest posiadanie obiektu, który pozwala dotrzeć do systemu podstawowego i tylko
uzyskać wymagane informacje bez konieczności zmiany i ponownej kompilacji systemu podstawowego. Nawet
lepszy byłby system, który powiadamiałby użytkownika o zmianie interesującego go elementu lub
wydarzyło się ciekawe wydarzenie.

ns-3 system śledzenia został zaprojektowany do pracy w tym kierunku i jest z nim dobrze zintegrowany
podpunkty Attribute i Config umożliwiające stosunkowo proste scenariusze użycia.

Omówienie
Podsystem śledzenia w dużym stopniu opiera się na ns-3 Mechanizmy wywołań zwrotnych i atrybutów. Ty
przed podjęciem takiej próby należy przeczytać i zrozumieć odpowiednie sekcje instrukcji
zrozumieć system śledzenia.

ns-3 system śledzenia opiera się na koncepcji niezależnych źródeł śledzenia i
śledzenie zlewów; wraz z jednolitym mechanizmem łączenia źródeł z odbiornikami.

Źródła śledzenia to podmioty, które mogą sygnalizować zdarzenia zachodzące w symulacji i zapewniać je
dostęp do interesujących danych bazowych. Na przykład źródło śledzenia może wskazywać, kiedy a
pakiet jest odbierany przez urządzenie sieciowe i zapewnia dostęp do zawartości pakietu
zainteresowane zlewy śladowe. Źródło śledzenia może również wskazywać, kiedy występuje interesujący stan
zmiana zachodzi w modelu. Na przykład okno przeciążenia modelu TCP jest liczbą pierwszą
kandydat na źródło śladowe.

Źródła śledzenia same w sobie nie są przydatne; muszą być połączone z innymi fragmentami kodu
które faktycznie robią coś pożytecznego z informacjami dostarczonymi przez źródło. The
Jednostki zużywające informacje o śledzeniu nazywane są ujściami śladów. Źródła śledzenia są
generatorami zdarzeń i pochłaniaczami śladów są konsumenci.

Ten wyraźny podział pozwala na rozproszenie dużej liczby źródeł śladowych
systemu w miejscach, które zdaniem autorów modelu mogą być przydatne. Chyba że użytkownik połączy się z a
śledzenia do jednego z tych źródeł, nic nie jest wysyłane. Układ ten pozwala stosunkowo
niewyrafinowanym użytkownikom możliwość dołączania nowych typów ujścia do istniejących źródeł śledzenia, bez konieczności
wymagające edycji i ponownej kompilacji rdzenia lub modeli symulatora.

Może istnieć zero lub więcej odbiorców zdarzeń śledzenia generowanych przez źródło śledzenia. Można
pomyśl o źródle śledzenia jako o rodzaju łącza informacyjnego typu punkt-wielopunkt.

„Protokół transportowy” dla tego koncepcyjnego łącza punkt-wielopunkt to: ns-3 Callback.

Przypomnijmy sobie z sekcji wywołania zwrotnego, że funkcja wywołania zwrotnego to sposób na umożliwienie wejścia dwóch modułów
system do komunikacji poprzez wywołania funkcji, jednocześnie oddzielając wywołanie
całkowicie funkcję z wywoływanej klasy. Jest to ten sam wymóg, który opisano powyżej
dla systemu śledzenia.

Zasadniczo źródło śladowe is wywołanie zwrotne, w którym można zarejestrować wiele funkcji.
Gdy ujście śledzenia wyraża zainteresowanie otrzymywaniem zdarzeń śledzenia, dodaje wywołanie zwrotne do a
lista wywołań zwrotnych przechowywanych przez źródło śledzenia. Gdy wydarzy się interesujące wydarzenie, ślad
źródło powołuje się na nie operator() podając zero lub więcej parametrów. To mówi źródłu
przejrzyj listę wywołań zwrotnych, wywołując każde z nich po kolei. W ten sposób parametr(y)
są przekazywane do odbiorników śladów, które są po prostu funkcjami.

Najprostszy Przykład
Przydatne będzie omówienie krótkiego przykładu, aby wzmocnić to, co powiedzieliśmy.:

#include „ns3/obiekt.h”
#include „ns3/uinteger.h”
#include „ns3/wartość-prześledzona.h””
#include „ns3/trace-source-accessor.h”

#zawierać

używając przestrzeni nazw ns3;

Pierwszą rzeczą do zrobienia jest dołączenie wymaganych plików. Jak wspomniano powyżej, system śledzenia
intensywnie korzysta z systemów obiektów i atrybutów. Pierwsze dwa obejmują wprowadzenie
deklaracje dla tych systemów. Plik, wartość śledzona.h wprowadza wymagane
deklaracje śledzenia danych zgodne z semantyką wartości.

Ogólnie rzecz biorąc, semantyka wartości oznacza po prostu, że możesz przekazywać obiekt, a nie obiekt
adres. Aby w ogóle używać semantyki wartości, musisz mieć obiekt z an
dostępny powiązany konstruktor kopiujący i operator przypisania. Rozszerzamy wymagania
aby porozmawiać o zestawie operatorów, które są predefiniowane dla typów zwykłych starych danych (POD).
Operator=, operator++, operator--, operator+, operator== itd.

Wszystko to oznacza, że ​​będziesz mógł prześledzić zmiany w obiekcie dokonane za pomocą
tych operatorów.:

klasa MójObiekt: obiekt publiczny
{
publiczny:
statyczny TypeId GetTypeId (pusty)
{
statyczny TypeId tid = TypeId („MójObiekt”)
.SetParent (Obiekt::GetTypeId ())
.Dodaj konstruktora ()
.AddTraceSource („Moja liczba całkowita”,
„Wartość całkowita do śledzenia.”,
MakeTraceSourceAccessor (&MyObject::m_myInt))
;
pora powrotu;
}

MójObiekt () {}
Wartość śledzona m_myInt;
};

Ponieważ system śledzenia jest zintegrowany z atrybutami, a atrybuty współpracują z obiektami,
musi istnieć ns-3 przedmiot dla źródła śladowego, w którym może żyć. Dwie ważne linie
kod to .Dodaj źródło śledzenia i Wartość śledzona deklaracja.

.Dodaj źródło śledzenia udostępnia „haki” służące do łączenia źródła śledzenia z plikiem
świat zewnętrzny. The Wartość śledzona deklaracja zapewnia infrastrukturę, która przeciąża
operatorzy wspomniani powyżej i sterują procesem wywołania zwrotnego.:

unieważnić
IntTrace (Int stara wartość, Int nowa wartość)
{
std::cout << "Prześledzenie" << staraWartość << " do " << nowaWartość << std::endl;
}

To jest definicja ujścia śladu. Odpowiada bezpośrednio funkcji wywołania zwrotnego.
Funkcja ta zostanie wywołana za każdym razem, gdy jeden z operatorów funkcji Wartość śledzona is
wykonany.:

int
main (int argc, char *argv[])
{
Cz mójObiekt = UtwórzObiekt ();

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

mójObiekt->m_myInt = 1234;
}

W tym fragmencie pierwszą rzeczą, którą należy zrobić, jest utworzenie obiektu, w którym
źródło śladu żyje.

Następny krok, tj TraceConnectBez kontekstu, tworzy połączenie pomiędzy śladem
źródło i ujście śladu. Zwróć uwagę na Wykonaj oddzwonienie funkcja szablonu. Przypomnijmy z
Sekcja wywołania zwrotnego, która tworzy wyspecjalizowany funktor odpowiedzialny za udostępnianie
przeciążony operator() używany do „odpalania” wywołania zwrotnego. Przeciążone operatory (++, -- itp.)
użyje tego operator() aby faktycznie wywołać wywołanie zwrotne. The TraceConnectBez kontekstu,
pobiera parametr typu string, który podaje nazwę atrybutu przypisanego do śledzenia
źródło. Pomińmy na razie kwestię kontekstu, gdyż nie jest ona jeszcze istotna.

Na koniec linijka:

mójObiekt->m_myInt = 1234;

należy interpretować jako wezwanie do operator= na zmiennej składowej m_myInt w
liczba całkowita 1234 przekazana jako parametr. Okazuje się, że operator ten jest zdefiniowany (według
Wartość śledzona), aby wykonać wywołanie zwrotne, które zwraca wartość void i przyjmuje dwie wartości całkowite jako
parametry — stara i nowa wartość danej liczby całkowitej. To jest dokładnie
sygnatura funkcji dla funkcji wywołania zwrotnego, którą udostępniliśmy -- IntTrace.

Podsumowując, źródło śledzenia to w istocie zmienna przechowująca listę wywołań zwrotnych. A
Trace Sin to funkcja używana jako cel wywołania zwrotnego. Atrybut i typ obiektu
systemy informacyjne służą do łączenia źródeł śledzenia z odbiornikami śledzenia. The
akt „uderzenia” w źródło śledzenia powoduje wykonanie operatora na źródle śledzenia, który zostaje uruchomiony
oddzwonienia. Powoduje to, że wywołania zwrotne ujścia śledzenia rejestrują zainteresowanie źródłem
wywoływany z parametrami dostarczonymi przez źródło.

Korzystanie z dotychczasowy Config Podsystem do Skontaktuj się do Wyśledzić Źródła
TraceConnectBez kontekstu wywołanie pokazane powyżej w prostym przykładzie jest w rzeczywistości bardzo
rzadko używane w systemie. Bardziej typowo, Config podsystem służy do umożliwienia wyboru
źródło śledzenia w systemie za pomocą tzw config ścieżka.

Na przykład w systemie można znaleźć coś, co wygląda następująco (taken
od przykłady/tcp-large-transfer.cc):

void CwndTracer (uint32_t stara wartość, uint32_t nowa wartość) {}

...

Config::ConnectWithoutContext (
"/NodeList/0/$ns3::TcpL4Protocol/SocketList/0/CongestionWindow",
Wykonaj oddzwonienie (&CwndTracer));

To powinno wyglądać bardzo znajomo. To jest to samo, co w poprzednim przykładzie, z tą różnicą, że
statyczna funkcja składowa klasy Config jest wywoływana zamiast włączonej metody przedmiot;
i zamiast Atrybut name, podawana jest ścieżka.

Pierwszą rzeczą do zrobienia jest przeczytanie ścieżki od tyłu. Ostatni odcinek ścieżki musi być
an Atrybut z przedmiot. W rzeczywistości, jeśli masz wskaźnik do przedmiot to ma
„Okno zatorów” Atrybut poręczny (nazwij to obiekt), możesz napisać to tak samo jak
poprzedni przykład:

void CwndTracer (uint32_t stara wartość, uint32_t nowa wartość) {}

...

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

Okazuje się, że kod dla Config::ConnectWithoutContext robi dokładnie to. Ten
funkcja przyjmuje ścieżkę reprezentującą łańcuch przedmiot wskaźniki i podąża za nimi aż do skutku
dochodzi do końca ścieżki i interpretuje ostatni segment jako Atrybut na ostatnim
obiekt. Przejdźmy przez to, co się dzieje.

Wiodący znak „/” w ścieżce odnosi się do tak zwanej przestrzeni nazw. Jeden z
predefiniowanymi przestrzeniami nazw w systemie konfiguracyjnym jest „NodeList”, która jest listą wszystkich
węzły w symulacji. Pozycje na liście odwołują się do indeksów na listę, tzw
„/NodeList/0” odnosi się do zerowego węzła na liście węzłów utworzonych w wyniku symulacji.
Węzeł ten jest w rzeczywistości a Cz i tak jest podklasą an ns3::Obiekt.

Jak opisano w sekcji Model obiektowy, ns-3 obsługuje model agregacji obiektów. The
następny segment ścieżki zaczyna się od znaku „$”, który wskazuje a Pobierz obiekt zadzwonić powinno być
szukałem następującego typu. Kiedy węzeł jest inicjowany przez
Pomocnik Internetu Stack w węźle agregowana jest pewna liczba interfejsów. Jednym z nich jest
Protokół czwartego poziomu TCP. Typ środowiska wykonawczego tego obiektu protokołu to ns3::Protokół TcpL4”.
Kiedy dotychczasowy ``Pobierz obiekt jest wykonywany, zwraca wskaźnik do obiektu tego typu.

Protokół TcpL4 class definiuje atrybut o nazwie „SocketList”, który jest listą
gniazdka. Każde gniazdo jest właściwie gniazdem ns3::Obiekt z własnym Atrybuty. Przedmioty w
lista gniazd jest odwoływana przez indeks, tak jak w NodeList, więc „SocketList/0”
odnosi się do zerowego gniazda na liście gniazd w węźle zerowym na liście węzłów --
pierwszy węzeł skonstruowany w symulacji.

To gniazdo, którego typ okazuje się być ns3::TcpSocketImpl definiuje atrybut
zwane „CongestionWindow”, które jest Wartość śledzona,
Config::ConnectWithoutContext teraz robi:

obiekt->TraceConnectWithoutContext („CongestionWindow”, MakeCallback (&CwndTracer));

używając wskaźnika obiektu z „SocketList/0”, który tworzy połączenie między śladami
źródło zdefiniowane w gnieździe wywołania zwrotnego -- CwndTracer.

Teraz za każdym razem, gdy wprowadzana jest zmiana w pliku Wartość śledzona reprezentujące zatory
oknie w gnieździe TCP, zostanie wykonane zarejestrowane wywołanie zwrotne i funkcja
CwndTracer będzie nazywane drukowaniem starych i nowych wartości przeciążenia TCP
okno.

Korzystanie z dotychczasowy Rysunek kalkowy API
Istnieją trzy poziomy interakcji z systemem śledzenia:

· Początkujący użytkownik może łatwo kontrolować, które obiekty biorą udział w śledzeniu;

· Użytkownicy średniozaawansowani mogą rozszerzyć system śledzenia, aby zmodyfikować wygenerowany format wyjściowy
lub wykorzystaj istniejące źródła śledzenia na różne sposoby, bez modyfikowania rdzenia
symulator;

· Zaawansowani użytkownicy mogą modyfikować rdzeń symulatora, aby dodać nowe źródła śledzenia i ujścia.

Korzystanie z Wyśledzić Pomocnicy
ns-3 pomocnicy śledzenia zapewniają bogate środowisko do konfigurowania i wybierania różnych
śledzić zdarzenia i zapisywać je do plików. W poprzednich rozdziałach przede wszystkim „Budynek
Topologie”, widzieliśmy kilka odmian metod pomocniczych śledzenia zaprojektowanych do użytku
wewnątrz innych pomocników (urządzeń).

Być może pamiętasz, że widziałeś niektóre z tych odmian:

pointToPoint.EnablePcapAll („drugi”);
pointToPoint.EnablePcap („drugi”, p2pNodes.Get (0)->GetId (), 0);
csma.EnablePcap („trzeci”, csmaDevices.Get (0), true);
pointToPoint.EnableAsciiAll (ascii.CreateFileStream („myfirst.tr”));

Jednak może nie być oczywiste, że istnieje spójny model dla wszystkich
metody związane ze śledzeniem znalezione w systemie. Poświęcimy teraz trochę czasu i przyjrzymy się temu
na „dużym obrazie”.

Obecnie istnieją dwa główne przypadki użycia pomocników śledzenia w ns-3: Pomocnicy urządzeń
i pomocnicy protokołu. Pomocnicy urządzeń przyglądają się problemowi określenia, które ślady powinny
być włączone poprzez węzeł, parę urządzeń. Możesz na przykład określić ten plik pcap
śledzenie powinno być włączone na konkretnym urządzeniu w określonym węźle. Wynika to z
ns-3 model koncepcyjny urządzenia, a także modele koncepcyjne różnych urządzeń
pomocnicy. W związku z tym utworzone pliki podążają za a
- - Konwencja nazewnictwa.

Pomocnicy protokołu przyglądają się problemowi określenia, które ślady powinny być włączone
para protokół i interfejs. Wynika to z ns-3 model koncepcyjny stosu protokołów,
a także modele koncepcyjne pomocników stosu internetowego. Oczywiście pliki śledzenia
powinien podążać za A - - Konwencja nazewnictwa.

Dlatego pomocnicy śledzenia w naturalny sposób podlegają dwuwymiarowej taksonomii. Tam są
subtelności, które uniemożliwiają wszystkim czterem klasom zachowanie identyczne, ale mimo to staramy się
aby wszystkie działały możliwie podobnie; i jeśli to możliwe, istnieją analogi dla
wszystkie metody we wszystkich klasach.

┌────────────────┬──────┬───────┐
│ │ pcap │ ascii │
├────────────────┼──────┼───────┤
│Pomocnik urządzenia │ │ │
├────────────────┼──────┼───────┤
│Pomocnik protokołu │ │ │
└────────────────┴──────┴───────┘

Stosujemy podejście zwane a mieszanie aby dodać funkcję śledzenia do naszych klas pomocniczych. A
mieszanie to klasa zapewniająca funkcjonalność dziedziczoną przez podklasę.
Dziedziczenie z miksu nie jest uważane za formę specjalizacji, ale w rzeczywistości jest na to sposobem
zbieraj funkcjonalność.

Rzućmy okiem na wszystkie cztery te przypadki i ich odpowiedniki mixiny.

Czapka Rysunek kalkowy Urządzenie Pomocnicy
Celem tych pomocników jest ułatwienie dodawania spójnej funkcji śledzenia pcap do pliku
ns-3 urządzenie. Chcemy, aby wszystkie różne warianty śledzenia pcap działały tak samo
wszystkie urządzenia, więc metody tych pomocników są dziedziczone przez pomocników urządzeń. Spójrz
at src/network/helper/trace-helper.h jeśli chcesz śledzić dyskusję, jednocześnie ją przeglądając
prawdziwy kod.

Klasa PcapHelperForDevice jest mieszanie zapewnia wysoki poziom funkcjonalności użytkowania
śledzenie pcap w pliku ns-3 urządzenie. Każde urządzenie musi implementować jedną metodę wirtualną
odziedziczone z tej klasy.:

wirtualna pustka EnablePcapInternal (prefiks std::string, Ptr nd, bool rozwiązły) = 0;

Sygnatura tej metody odzwierciedla spojrzenie na sytuację skupione na urządzeniu
poziom. Wszystkie metody publiczne dziedziczone z klasy PcapUserHelperForDevice zmniejszyć do
wywoływanie tej metody implementacji zależnej od pojedynczego urządzenia. Na przykład najniższy poziom
metoda pcap,:

void EnablePcap (przedrostek std::string, cz nd, bool rozwiązły = fałsz, bool jawna nazwa_pliku = fałsz);

wywoła implementację urządzenia WłączPcapInternal bezpośrednio. Wszystkie inne publiczne pcap
metody śledzenia opierają się na tej implementacji, aby zapewnić dodatkowy poziom użytkownika
funkcjonalność. Dla użytkownika oznacza to, że zrobią to wszyscy pomocnicy urządzeń w systemie
mieć dostępne wszystkie metody śledzenia pcap; i wszystkie te metody będą działać w ten sam sposób
sposób na różnych urządzeniach, jeśli urządzenie je implementuje WłączPcapInternal prawidłowo.

Czapka Rysunek kalkowy Urządzenie Pomocnik Metody
void EnablePcap (przedrostek std::string, cz drugi,
bool rozwiązły = fałsz, bool jawna nazwa pliku = fałsz);
void EnablePcap (std::string przedrostek, std::string ndName,
bool rozwiązły = fałsz, bool jawna nazwa pliku = fałsz);
void EnablePcap (przedrostek std::string, NetDeviceContainer d,
bool rozwiązły = fałsz);
void EnablePcap (przedrostek std::string, NodeContainer n,
bool rozwiązły = fałsz);
void EnablePcap (prefiks std::string, identyfikator węzła uint32_t, identyfikator urządzenia uint32_t,
bool rozwiązły = fałsz);
void EnablePcapAll (std::string przedrostek, bool promiscuous = false);

W każdej z powyższych metod istnieje domyślny parametr o nazwie bezładny że
domyślnie ustawiona jest wartość false. Parametr ten wskazuje, że ślad nie powinien być gromadzony
tryb rozwiązły. Jeśli chcesz, aby Twoje ślady obejmowały cały ruch widziany przez urządzenie
(i jeśli urządzenie obsługuje tryb rozwiązły) po prostu dodaj parametr true do dowolnego z
dzwoni powyżej. Na przykład,:

Cz II;
...
pomocnik.EnablePcap („przedrostek”, nd, prawda);

umożliwi przechwytywanie w trybie rozwiązłym na Urządzenie sieciowe określone przez nd.

Pierwsze dwie metody zawierają także domyślny parametr o nazwie jawna nazwa pliku to będzie
omówimy poniżej.

Zachęcamy do zapoznania się z Doxygenem na zajęciach PcapHelperForDevice aby znaleźć szczegóły
z tych metod; ale podsumowując...

Możesz włączyć śledzenie pcap na określonej parze węzeł/urządzenie sieciowe, podając plik
Cz do WłączPcap metoda. Plik Cz jest niejawne, ponieważ urządzenie sieciowe
musi należeć do dokładnie jednego Node. Na przykład,:

Cz II;
...
pomocnik.EnablePcap („przedrostek”, nd);

Możesz włączyć śledzenie pcap na określonej parze węzeł/urządzenie sieciowe, podając plik
std::string reprezentujący ciąg usługi nazwy obiektu do pliku WłączPcap metoda. Plik
Cz jest sprawdzany z ciągu nazw. Ponownie, jest ukryte, ponieważ
nazwane urządzenie sieciowe musi należeć dokładnie do jednego Node. Na przykład,:

Nazwy::Dodaj („serwer” ...);
Nazwy::Dodaj („serwer/eth0” ...);
...
pomocnik.EnablePcap („prefiks”, „serwer/ath0”);

Możesz włączyć śledzenie pcap dla kolekcji par węzeł/urządzenie sieciowe, podając plik
Kontener urządzeń sieciowych. Dla każdego Urządzenie sieciowe w kontenerze sprawdzany jest typ. Dla każdego
urządzenie odpowiedniego typu (tego samego typu, którym zarządza pomocnik urządzenia), śledzenie jest
włączony. Ponownie, jest ukryte, ponieważ znalezione urządzenie sieciowe musi należeć dokładnie do
pierwszej Node. Na przykład,:

NetDeviceContainer d = ...;
...
pomocnik.EnablePcap („przedrostek”, d);

Możesz włączyć śledzenie pcap dla kolekcji par węzeł/urządzenie sieciowe, podając plik
Kontener węzłów. Dla każdego Node Kontener węzłów jest załączony Urządzenia sieciowe są iterowane.
Dla każdego Urządzenie sieciowe dołączony do każdego węzła w kontenerze, typ tego urządzenia to
sprawdzony. Dla każdego urządzenia odpowiedniego typu (tego samego typu, którym zarządza urządzenie
pomocnik), śledzenie jest włączone.:

Kontener węzłów n;
...
pomocnik.EnablePcap („przedrostek”, n);

Możesz włączyć śledzenie pcap na podstawie identyfikatora węzła i identyfikatora urządzenia, a także jawnie
Ptr, Każdy Node w systemie ma całkowity identyfikator węzła i każde urządzenie podłączone do węzła
ma całkowity identyfikator urządzenia.:

pomocnik.EnablePcap („przedrostek”, 21, 1);

Na koniec możesz włączyć śledzenie pcap dla wszystkich urządzeń w systemie tego samego typu co
zarządzane przez pomocnika urządzenia.:

pomocnik.EnablePcapAll („przedrostek”);

Czapka Rysunek kalkowy Urządzenie Pomocnik Nazwa pliku Wybór
W powyższych opisach metod zawarta jest konstrukcja pełnej nazwy pliku
sposób realizacji. Zgodnie z konwencją, pcap śledzi w pliku ns-3 system ma postać
- identyfikator>- identyfikator>.pcap

Jak wspomniano wcześniej, każdy węzeł w systemie będzie miał przypisany przez system identyfikator węzła; I
każde urządzenie będzie miało indeks interfejsu (zwany także identyfikatorem urządzenia) w stosunku do swojego węzła.
Domyślnie zatem plik śledzenia pcap tworzony jest w wyniku włączenia śledzenia na pierwszym
byłoby urządzenie węzła 21 używające przedrostka „prefiks”. przedrostek-21-1.pcap.

Zawsze możesz skorzystać z tzw ns-3 usługę nazw obiektów, aby było to bardziej jasne. Na przykład, jeśli
używasz usługi nazw obiektów, aby przypisać nazwę „serwer” do węzła 21, co daje wynikowy plik pcap
nazwa pliku śledzenia zostanie automatycznie zmieniona na prefiks-serwer-1.pcap i jeśli również przypiszesz
nazwij „eth0” do urządzenia, nazwa pliku pcap automatycznie to odbierze i będzie
o nazwie prefiks-serwer-eth0.pcap.

Na koniec dwie z metod pokazanych powyżej:

void EnablePcap (przedrostek std::string, cz nd, bool rozwiązły = fałsz, bool jawna nazwa_pliku = fałsz);
void EnablePcap (przedrostek std::string, std::string ndName, bool rozwiązły = fałsz, bool jawna nazwa pliku = fałsz);

mają domyślny parametr o nazwie jawna nazwa pliku. Po ustawieniu wartości true ten parametr
wyłącza mechanizm automatycznego uzupełniania nazw plików i umożliwia utworzenie jawnego pliku
Nazwa pliku. Ta opcja jest dostępna tylko w metodach, które umożliwiają śledzenie pcap na pliku
pojedyncze urządzenie.

Na przykład, aby pomóc pomocnikowi urządzenia utworzyć pojedynczy rozwiązły plik pcap
przechwyć plik o określonej nazwie (mój-pcap-plik.pcap) na danym urządzeniu można:

Cz II;
...
pomocnik.EnablePcap („mój-pcap-plik.pcap”, nd, prawda, prawda);

Pierwszy prawdziwy Parametr włącza śledzenie w trybie promiscious, a drugi informuje pomocnika
interpretować prefiks parametr jako pełna nazwa pliku.

Ascii Rysunek kalkowy Urządzenie Pomocnicy
Zachowanie pomocnika śledzenia ascii mieszanie jest zasadniczo podobny do wersji pcap.
Spójrz na src/network/helper/trace-helper.h jeśli chcesz śledzić dyskusję
patrząc na prawdziwy kod.

Klasa AsciiTraceHelperForDevice dodaje wysoką funkcjonalność do używania ASCII
śledzenie do klasy pomocniczej urządzenia. Podobnie jak w przypadku pcap, każde urządzenie musi implementować a
pojedyncza metoda wirtualna dziedziczona ze śladu ASCII mieszanie.:

wirtualna pustka EnableAsciiInternal (cz strumień, przedrostek std::string, cz drugie) = 0;

Sygnatura tej metody odzwierciedla spojrzenie na sytuację skupione na urządzeniu
poziom; a także fakt, że pomocnik może zapisywać do współdzielonego strumienia wyjściowego. Wszystkie z
publiczne metody związane ze śledzeniem ascii, dziedziczone z klasy AsciiTraceHelperForDevice
ograniczyć się do wywoływania tej metody implementacji zależnej od pojedynczego urządzenia. Na przykład
metody śledzenia ASCII najniższego poziomu,:

void EnableAscii (przedrostek std::string, cz II);
void WłączAscii (Cz strumień, cz II);

wywoła implementację urządzenia WłączAsciiInternal bezpośrednio, zapewniając albo a
prawidłowy przedrostek lub strumień. Wszystkie inne publiczne metody śledzenia ASCII będą na nich opierać się
funkcje niskiego poziomu zapewniające dodatkową funkcjonalność na poziomie użytkownika. Co to oznacza dla
użytkownika jest to, że wszyscy pomocnicy urządzeń w systemie będą mieli wszystkie metody śledzenia ASCII
dostępny; i wszystkie te metody będą działać w ten sam sposób na różnych urządzeniach, jeśli urządzenia
wdrożenia WłączAsciiInternal prawidłowo.

Ascii Rysunek kalkowy Urządzenie Pomocnik Metody
void EnableAscii (przedrostek std::string, cz II);
void WłączAscii (Cz strumień, cz II);

void EnableAscii (przedrostek std::string, std::string ndName);
void WłączAscii (Cz strumień, std::string ndName);

void EnableAscii (przedrostek std::string, NetDeviceContainer d);
void WłączAscii (Cz strumień, NetDeviceContainer d);

void EnableAscii (przedrostek std::string, NodeContainer n);
void WłączAscii (Cz strumień, NodeContainer n);

void EnableAscii (przedrostek std::string, identyfikator węzła uint32_t, identyfikator urządzenia uint32_t);
void WłączAscii (Cz strumień, uint32_t identyfikator węzła, uint32_t identyfikator urządzenia);

void EnableAsciiAll (przedrostek std::string);
void WłączAsciiAll (cz strumień);

Zachęcamy do zapoznania się z Doxygenem na zajęciach TraceHelperForDevice znaleźć
szczegóły tych metod; ale podsumowując...

Dostępnych jest dwa razy więcej metod śledzenia ASCII niż pcap
rysunek kalkowy. Dzieje się tak, ponieważ oprócz modelu w stylu pcap znajdują się ślady każdego z nich
unikalna para węzeł/urządzenie jest zapisywana w unikalnym pliku, wspieramy model, w którym następuje śledzenie
informacje dotyczące wielu par węzeł/urządzenie są zapisywane we wspólnym pliku. Oznacza to, że
- - mechanizm generowania nazw plików został zastąpiony mechanizmem umożliwiającym
odnoszą się do wspólnego pliku; a liczba metod API jest podwojona, aby umożliwić wszystkie
kombinacje.

Podobnie jak w przypadku śledzenia pcap, możesz włączyć śledzenie ASCII dla określonej pary węzeł/urządzenie sieciowe
dostarczając Cz do WłączAscii metoda. Plik Cz jest ukryte od
urządzenie sieciowe musi należeć dokładnie do jednego Node. Na przykład,:

Cz II;
...
pomocnik.EnableAscii („przedrostek”, nd);

W takim przypadku do pliku śledzenia ascii nie są zapisywane żadne konteksty śledzenia, ponieważ tak by było
zbędny. System wybierze nazwę pliku, który ma zostać utworzony, stosując takie same zasady jak
opisane w sekcji pcap, z tą różnicą, że plik będzie miał zamiast tego przyrostek „.tr”.
„.pcap”.

Jeśli chcesz włączyć śledzenie ASCII na więcej niż jednym urządzeniu sieciowym i wysłać wszystkie ślady
do pojedynczego pliku, możesz to również zrobić, używając obiektu jako odniesienia do pojedynczego pliku:

Cz nd1;
Cz nd2;
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
pomocnik.EnableAscii (strumień, nd1);
pomocnik.EnableAscii (strumień, nd2);

W takim przypadku konteksty śledzenia są zapisywane w pliku śledzenia ASCII, ponieważ są wymagane
aby ujednoznacznić ślady z obu urządzeń. Należy pamiętać, że ponieważ użytkownik jest całkowicie
podając nazwę pliku, dla zachowania spójności ciąg powinien zawierać rozszerzenie „.tr”.

Możesz włączyć śledzenie ASCII dla konkretnej pary węzeł/urządzenie sieciowe, podając plik
std::string reprezentujący ciąg usługi nazwy obiektu do pliku WłączPcap metoda. Plik
Cz jest sprawdzany z ciągu nazw. Ponownie, jest ukryte, ponieważ
nazwane urządzenie sieciowe musi należeć dokładnie do jednego Node. Na przykład,:

Nazwy::Dodaj („klient” ...);
Nazwy::Dodaj („klient/eth0” ...);
Nazwy::Dodaj („serwer” ...);
Nazwy::Dodaj („serwer/eth0” ...);
...
pomocnik.EnableAscii („przedrostek”, „klient/eth0”);
pomocnik.EnableAscii („przedrostek”, „serwer/eth0”);

Spowodowałoby to utworzenie dwóch plików o nazwach prefiks-klient-eth0.tr oraz prefiks-serwer-eth0.tr w
ślady dla każdego urządzenia w odpowiednim pliku śledzenia. Ponieważ wszystkie pliki EnableAscii
funkcje są przeciążone, aby pobrać opakowanie strumienia, możesz również użyć tego formularza:

Nazwy::Dodaj („klient” ...);
Nazwy::Dodaj („klient/eth0” ...);
Nazwy::Dodaj („serwer” ...);
Nazwy::Dodaj („serwer/eth0” ...);
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
helper.EnableAscii (strumień, "klient/eth0");
pomocnik.EnableAscii (strumień, "serwer/eth0");

Spowodowałoby to utworzenie pojedynczego pliku śledzenia o nazwie nazwa-pliku-śledzenia.tr który zawiera wszystko
zdarzenia śledzenia dla obu urządzeń. Zdarzenia zostaną ujednoznacznione na podstawie kontekstu śledzenia
smyczki.

Możesz włączyć śledzenie ASCII dla kolekcji par węzeł/urządzenie sieciowe, podając plik
Kontener urządzeń sieciowych. Dla każdego Urządzenie sieciowe w kontenerze sprawdzany jest typ. Dla każdego
urządzenie odpowiedniego typu (tego samego typu, którym zarządza pomocnik urządzenia), śledzenie jest
włączony. Ponownie, jest ukryte, ponieważ znalezione urządzenie sieciowe musi należeć dokładnie do
pierwszej Node. Na przykład,:

NetDeviceContainer d = ...;
...
pomocnik.EnableAscii („przedrostek”, d);

Spowodowałoby to utworzenie pewnej liczby plików śledzenia ASCII, z których każdy będzie następujący
the - - konwencja .tr. Łącząc wszystkie ślady w plik a
pojedynczy plik realizuje się podobnie jak w powyższych przykładach:

NetDeviceContainer d = ...;
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
pomocnik.EnableAscii (strumień, d);

Możesz włączyć śledzenie ASCII dla kolekcji par węzeł/urządzenie sieciowe, podając plik
Kontener węzłów. Dla każdego Node Kontener węzłów jest załączony Urządzenia sieciowe są iterowane.
Dla każdego Urządzenie sieciowe dołączony do każdego węzła w kontenerze, typ tego urządzenia to
sprawdzony. Dla każdego urządzenia odpowiedniego typu (tego samego typu, którym zarządza urządzenie
pomocnik), śledzenie jest włączone.:

Kontener węzłów n;
...
pomocnik.EnableAscii („przedrostek”, n);

Spowodowałoby to utworzenie pewnej liczby plików śledzenia ASCII, z których każdy będzie następujący
the - - konwencja .tr. Łącząc wszystkie ślady w plik a
pojedynczy plik realizuje się podobnie jak w powyższych przykładach:

Możesz włączyć śledzenie pcap na podstawie identyfikatora węzła i identyfikatora urządzenia, a także jawnie
Ptr, Każdy Node w systemie ma całkowity identyfikator węzła i każde urządzenie podłączone do węzła
ma całkowity identyfikator urządzenia.:

pomocnik.EnableAscii („przedrostek”, 21, 1);

Oczywiście ślady można połączyć w jeden plik, jak pokazano powyżej.

Na koniec możesz włączyć śledzenie pcap dla wszystkich urządzeń w systemie tego samego typu co
zarządzane przez pomocnika urządzenia.:

pomocnik.EnableAsciiAll („przedrostek”);

Spowodowałoby to utworzenie wielu plików śledzenia ASCII, po jednym dla każdego urządzenia
system typu zarządzanego przez pomocnika. Wszystkie te pliki będą zgodne z rozszerzeniem
- - konwencja .tr. Połączenie wszystkich śladów w jeden
plik wykonuje się podobnie jak w powyższych przykładach.

Ascii Rysunek kalkowy Urządzenie Pomocnik Nazwa pliku Wybór
W powyższych opisach metod w stylu przedrostków zawarta jest konstrukcja kompletna
nazwy plików według metody implementacji. Zgodnie z konwencją ślady ascii w pliku ns-3 systemu są
formy - identyfikator>- identyfikator>.tr.

Jak wspomniano wcześniej, każdy węzeł w systemie będzie miał przypisany przez system identyfikator węzła; I
każde urządzenie będzie miało indeks interfejsu (zwany także identyfikatorem urządzenia) w stosunku do swojego węzła.
Domyślnie plik śledzenia ASCII tworzony jest w wyniku włączenia śledzenia na pierwszym
urządzenie węzła 21, używając przedrostka „prefiks”, byłoby przedrostek-21-1.tr.

Zawsze możesz skorzystać z tzw ns-3 usługę nazw obiektów, aby było to bardziej jasne. Na przykład, jeśli
korzystasz z usługi nazw obiektów, aby przypisać nazwę „serwer” do węzła 21, co daje wynik
nazwa pliku śledzenia ascii zostanie automatycznie zmieniona na prefiks-serwer-1.tr i jeśli również przypiszesz
nazwę „eth0” do urządzenia, nazwa pliku śledzenia ascii automatycznie ją pobierze
i nazywać się prefiks-serwer-eth0.tr.

Czapka Rysunek kalkowy Protokół Pomocnicy
Celem tych mixiny ma na celu ułatwienie dodania spójnego narzędzia śledzenia pcap do
protokoły. Chcemy, aby wszystkie różne wersje śledzenia pcap działały tak samo we wszystkich
protokoły, więc metody tych pomocników są dziedziczone przez pomocników stosu. Spojrzeć na
src/network/helper/trace-helper.h jeśli chcesz śledzić dyskusję, jednocześnie ją przeglądając
prawdziwy kod.

W tej sekcji będziemy ilustrować metody stosowane w protokole IPv4, Do
określ ślady w podobnych protokołach, po prostu podstaw odpowiedni typ. Na przykład,
użyć Cz zamiast a Cz i zadzwoń WłączPcapIpv6 zamiast WłączPcapIpv4.

Klasa PcapHelperForIpv4 zapewnia wysoki poziom funkcjonalności śledzenia pcap
IPv4 protokół. Każdy pomocnik protokołu umożliwiający te metody musi implementować pojedynczy
metoda wirtualna odziedziczona z tej klasy. Będzie osobna implementacja dla
IPv6, ale jedyna różnica będzie dotyczyć nazw metod i podpisów.
Aby rozróżnić klasę, wymagane są różne nazwy metod IPv4 od IPv6 które są jednym i drugim
wywodzące się z klasy przedmioti metody, które mają ten sam podpis.:

wirtualna pustka EnablePcapIpv4Internal (prefiks std::string, Ptr interfejs ipv4, uint4_t) = 32;

Sygnatura tej metody odzwierciedla podejście zorientowane na protokół i interfejs
sytuacja na tym poziomie. Wszystkie metody publiczne dziedziczone z klasy PcapHelperForIpv4
ogranicz się do wywoływania tej metody implementacji zależnej od pojedynczego urządzenia. Na przykład
metoda pcap najniższego poziomu,:

void EnablePcapIpv4 (przedrostek std::string, cz interfejs ipv4, uint4_t);

wywoła implementację urządzenia WłączPcapIpv4Internal bezpośrednio. Wszystkie inne publiczne
Metody śledzenia pcap opierają się na tej implementacji, aby zapewnić dodatkowy poziom użytkownika
funkcjonalność. Dla użytkownika oznacza to, że zrobią to wszyscy pomocnicy protokołów w systemie
mieć dostępne wszystkie metody śledzenia pcap; i wszystkie te metody będą działać w ten sam sposób
sposób przez protokoły, jeśli pomocnik implementuje WłączPcapIpv4Internal prawidłowo.

Czapka Rysunek kalkowy Protokół Pomocnik Metody
Metody te zaprojektowano tak, aby działały w korespondencji jeden do jednego z metodą Node- I
Urządzenie sieciowe- centryczne wersje wersji urządzeń. Zamiast Node oraz Urządzenie sieciowe đôi
ograniczeń, używamy ograniczeń protokołu i interfejsu.

Należy pamiętać, że podobnie jak w wersji urządzenia istnieje sześć metod:

void EnablePcapIpv4 (przedrostek std::string, cz interfejs ipv4, uint4_t);
void EnablePcapIpv4 (przedrostek std::string, std::string ipv4Name, interfejs uint32_t);
void EnablePcapIpv4 (przedrostek std::string, Ipv4InterfaceContainer c);
void EnablePcapIpv4 (przedrostek std::string, NodeContainer n);
void EnablePcapIpv4 (std::string prefix, uint32_t nodeid, interfejs uint32_t);
void EnablePcapIpv4All (przedrostek std::string);

Zachęcamy do zapoznania się z Doxygenem na zajęciach PcapHelperForIpv4 aby znaleźć szczegóły
z tych metod; ale podsumowując...

Można włączyć śledzenie pcap dla określonej pary protokół/interfejs, podając plik
Cz oraz Interfejs do WłączPcap metoda. Na przykład,:

Cz ipv4 = węzeł->GetObject ();
...
pomocnik.EnablePcapIpv4 („prefiks”, ipv4, 0);

Możesz włączyć śledzenie pcap na określonej parze węzeł/urządzenie sieciowe, podając plik
std::string reprezentujący ciąg usługi nazwy obiektu do pliku WłączPcap metoda. Plik
Cz jest sprawdzany z ciągu nazw. Na przykład,:

Nazwy::Dodaj („serverIPv4” ...);
...
pomocnik.EnablePcapIpv4 („przedrostek”, „serwerIpv4”, 1);

Można włączyć śledzenie pcap dla kolekcji par protokołów/interfejsów, podając plik
Kontener interfejsu IPv4. Dla każdego IPv4 / para interfejsów w kontenerze typ protokołu
sprawdzone. Dla każdego protokołu odpowiedniego typu (tego samego typu, którym zarządza
pomocnika urządzenia), śledzenie jest włączone dla odpowiedniego interfejsu. Na przykład,:

węzły NodeContainer;
...
Urządzenia NetDeviceContainer = urządzenieHelper.Install (węzły);
...
Pomocnik adresu IPv4 ipv4;
ipv4.SetBase („10.1.1.0”, „255.255.255.0”);
Interfejsy Ipv4InterfaceContainer = ipv4.Assign (urządzenia);
...
helper.EnablePcapIpv4 („przedrostek”, interfejsy);

Można włączyć śledzenie pcap dla kolekcji par protokołów/interfejsów, podając plik
Kontener węzłów. Dla każdego Node Kontener węzłów znaleziono odpowiedni protokół. Dla
każdego protokołu, jego interfejsy są wyliczane, a na wynikach włączone jest śledzenie
pary. Na przykład,:

Kontener węzłów n;
...
pomocnik.EnablePcapIpv4 („przedrostek”, n);

Możesz włączyć śledzenie pcap również na podstawie identyfikatora węzła i interfejsu. W tym przypadku,
identyfikator węzła jest tłumaczony na a Cz i odpowiedni protokół jest sprawdzany w
węzeł. Wynikowy protokół i interfejs służą do określenia wynikowego śledzenia
źródło.:

pomocnik.EnablePcapIpv4 („przedrostek”, 21, 1);

Na koniec możesz włączyć śledzenie pcap dla wszystkich interfejsów w systemie wraz z powiązanymi
protokół jest tego samego typu, co zarządzany przez pomocnika urządzenia.:

pomocnik.EnablePcapIpv4All („przedrostek”);

Czapka Rysunek kalkowy Protokół Pomocnik Nazwa pliku Wybór
We wszystkich powyższych opisach metod ukryta jest konstrukcja kompletna
nazwy plików według metody implementacji. Zgodnie z konwencją, ślady pcap pobierane dla urządzeń w
dotychczasowy ns-3 system ma postać - identyfikator>- identyfikator>.pcap, W przypadku
śladów protokołu, istnieje zgodność jeden do jednego pomiędzy protokołami i Węzły. Jest
bo protokół Obiekty są agregowane Node Obiekty. Ponieważ nie ma protokołu globalnego
id w systemie, w nazewnictwie plików używamy odpowiedniego identyfikatora węzła. Dlatego istnieje
możliwość kolizji nazw plików w automatycznie wybranych nazwach plików śledzenia. Dla tego
powodu, konwencja nazw plików została zmieniona dla śladów protokołu.

Jak wspomniano wcześniej, każdy węzeł w systemie będzie miał przypisany przez system identyfikator węzła.
Ponieważ istnieje zgodność jeden do jednego między instancjami protokołu a instancjami węzła
używamy identyfikatora węzła. Każdy interfejs ma identyfikator interfejsu powiązany z protokołem. Używamy
Konwencji " -N -I .pcap” do nazewnictwa plików śledzenia w
pomocnicy protokołu.

Dlatego domyślnie plik śledzenia pcap tworzony jest w wyniku włączenia śledzenia
interfejs 1 protokołu Ipv4 węzła 21 z przedrostkiem „prefiks” będzie
„przedrostek-n21-i1.pcap”.

Zawsze możesz skorzystać z tzw ns-3 usługę nazw obiektów, aby było to bardziej jasne. Na przykład, jeśli
korzystasz z usługi nazw obiektów, aby przypisać nazwę „serverIpv4” do Ptr na węźle
21, nazwa wynikowego pliku śledzenia pcap zostanie automatycznie zmieniona na:
„prefiks-nserverIpv4-i1.pcap”.

Ascii Rysunek kalkowy Protokół Pomocnicy
Zachowanie pomocników śledzenia ascii jest zasadniczo podobne do przypadku pcap. Weź
patrzeć na src/network/helper/trace-helper.h jeśli chcesz śledzić dyskusję
patrząc na prawdziwy kod.

W tej sekcji będziemy ilustrować metody stosowane w protokole IPv4, Do
określ ślady w podobnych protokołach, po prostu podstaw odpowiedni typ. Na przykład,
użyć Cz zamiast a Cz i zadzwoń WłączAsciiIpv6 zamiast
WłączAsciiIpv4.

Klasa AsciiTraceHelperForIpv4 dodaje wysoką funkcjonalność do używania ASCII
śledzenie do pomocnika protokołu. Każdy protokół umożliwiający te metody musi implementować a
pojedyncza metoda wirtualna odziedziczona z tej klasy.:

wirtualna pustka EnableAsciiIpv4Internal (cz strumień, przedrostek std::string,
Cz interfejs ipv4, uint4_t) = 32;

Sygnatura tej metody odzwierciedla podejście zorientowane na protokół i interfejs
sytuacja na tym poziomie; a także fakt, że pomocnik może pisać do udostępnionego
strumień wyjściowy. Wszystkie metody publiczne dziedziczone z klasy
PcapAndAsciiTraceHelperForIpv4 ogranicz się do nazywania tego pojedynczego urządzenia zależnym
metoda realizacji. Na przykład metody śledzenia ASCII najniższego poziomu:

void EnableAsciiIpv4 (przedrostek std::string, cz interfejs ipv4, uint4_t);
void WłączAsciiIpv4 (Cz strumień, cz interfejs ipv4, uint4_t);

wywoła implementację urządzenia WłączAsciiIpv4Internal bezpośrednio, zapewniając albo
przedrostek lub strumień. Wszystkie inne publiczne metody śledzenia ASCII będą na nich opierać się
funkcje niskiego poziomu zapewniające dodatkową funkcjonalność na poziomie użytkownika. Co to oznacza dla
użytkownika jest to, że wszyscy pomocnicy urządzeń w systemie będą mieli wszystkie metody śledzenia ASCII
dostępny; i wszystkie te metody będą działać w ten sam sposób w różnych protokołach, jeśli
protokoły wdrażają WłączAsciiIpv4Internal prawidłowo.

Ascii Rysunek kalkowy Urządzenie Pomocnik Metody
void EnableAsciiIpv4 (przedrostek std::string, cz interfejs ipv4, uint4_t);
void WłączAsciiIpv4 (Cz strumień, cz interfejs ipv4, uint4_t);

void EnableAsciiIpv4 (przedrostek std::string, std::string ipv4Name, interfejs uint32_t);
void WłączAsciiIpv4 (Cz strumień, std::string ipv4Name, interfejs uint32_t);

void EnableAsciiIpv4 (przedrostek std::string, Ipv4InterfaceContainer c);
void WłączAsciiIpv4 (Cz strumień, kontener interfejsu Ipv4 c);

void EnableAsciiIpv4 (std::string przedrostek, NodeContainer n);
void WłączAsciiIpv4 (Cz strumień, NodeContainer n);

void EnableAsciiIpv4 (przedrostek std::string, identyfikator węzła uint32_t, identyfikator urządzenia uint32_t);
void WłączAsciiIpv4 (Cz strumień, identyfikator węzła uint32_t, interfejs uint32_t);

void EnableAsciiIpv4All (przedrostek std::string);
void WłączAsciiIpv4All (cz strumień);

Zachęcamy do zapoznania się z Doxygenem na zajęciach PcapAndAsciiHelperForIpv4 znaleźć
szczegóły tych metod; ale podsumowując...

Dostępnych jest dwa razy więcej metod śledzenia ASCII niż pcap
rysunek kalkowy. Dzieje się tak, ponieważ oprócz modelu w stylu pcap znajdują się ślady każdego z nich
unikalna para protokół/interfejs jest zapisywana w unikalnym pliku, wspieramy model, w którym
informacje o śledzeniu dla wielu par protokół/interfejs są zapisywane we wspólnym pliku. Ten
oznacza, że -N - zastąpiono mechanizm generowania nazw plików
poprzez mechanizm odwoływania się do wspólnego pliku; a liczba metod API jest podwojona
zezwalaj na wszystkie kombinacje.

Podobnie jak w przypadku śledzenia pcap, możesz włączyć śledzenie ASCII dla określonego protokołu/interfejsu
sparuj, podając a Cz oraz Interfejs do WłączAscii metoda. Na przykład,:

Cz ipv4;
...
pomocnik.EnableAsciiIpv4 („prefiks”, ipv4, 1);

W takim przypadku do pliku śledzenia ascii nie są zapisywane żadne konteksty śledzenia, ponieważ tak by było
zbędny. System wybierze nazwę pliku, który ma zostać utworzony, stosując takie same zasady jak
opisane w sekcji pcap, z tą różnicą, że plik będzie miał zamiast tego przyrostek „.tr”.
„.pcap”.

Jeśli chcesz włączyć śledzenie ASCII na więcej niż jednym interfejsie i wysłać do niego wszystkie ślady
pojedynczy plik, możesz to również zrobić, używając obiektu odnoszącego się do pojedynczego pliku. My
mam już coś podobnego w powyższym przykładzie „cwnd”:

Cz protokół4 = węzeł1->GetObject ();
Cz protokół4 = węzeł2->GetObject ();
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
pomocnik.EnableAsciiIpv4 (strumień, protokół1, 1);
pomocnik.EnableAsciiIpv4 (strumień, protokół2, 1);

W takim przypadku konteksty śledzenia są zapisywane w pliku śledzenia ASCII, ponieważ są wymagane
aby ujednoznacznić ślady z dwóch interfejsów. Należy pamiętać, że ponieważ użytkownik jest całkowicie
podając nazwę pliku, dla zachowania spójności ciąg powinien zawierać rozszerzenie „.tr”.

Można włączyć śledzenie ASCII dla określonego protokołu, podając plik std::string
reprezentujący ciąg usługi nazwy obiektu do pliku WłączPcap metoda. Plik Cz is
podniósł wzrok znad ciągu nazw. The w wynikowych nazwach plików jest ukryte ponieważ
istnieje korespondencja jeden do jednego między instancjami protokołu a węzłami, na przykład:

Nazwy::Dodaj („węzeł1Ipv4” ...);
Nazwy::Dodaj („węzeł2Ipv4” ...);
...
pomocnik.EnableAsciiIpv4 („przedrostek”, „węzeł1Ipv4”, 1);
pomocnik.EnableAsciiIpv4 („przedrostek”, „węzeł2Ipv4”, 1);

Spowodowałoby to powstanie dwóch plików o nazwach „prefix-nnode1Ipv4-i1.tr” i
„prefix-nnode2Ipv4-i1.tr” ze śladami dla każdego interfejsu w odpowiednim pliku śledzenia.
Ponieważ wszystkie funkcje EnableAscii są przeciążone w celu pobrania opakowania strumienia, możesz to zrobić
użyj również tego formularza:

Nazwy::Dodaj („węzeł1Ipv4” ...);
Nazwy::Dodaj („węzeł2Ipv4” ...);
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
pomocnik.EnableAsciiIpv4 (strumień, „node1Ipv4”, 1);
pomocnik.EnableAsciiIpv4 (strumień, „node2Ipv4”, 1);

Spowodowałoby to utworzenie pojedynczego pliku śledzenia o nazwie „nazwa-pliku-śladu.tr”, który zawierałby wszystkie
zdarzenia śledzenia dla obu interfejsów. Zdarzenia zostaną ujednoznacznione na podstawie kontekstu śledzenia
smyczki.

Można włączyć śledzenie ASCII dla kolekcji par protokołów/interfejsów, podając plik
Kontener interfejsu IPv4. Dla każdego protokołu odpowiedniego typu (tego samego typu, który jest zarządzany
przez pomocnika urządzenia), śledzenie jest włączone dla odpowiedniego interfejsu. Ponownie,
jest dorozumiana, ponieważ istnieje zgodność jeden do jednego pomiędzy każdym protokołem i
jego węzeł. Na przykład,:

węzły NodeContainer;
...
Urządzenia NetDeviceContainer = urządzenieHelper.Install (węzły);
...
Pomocnik adresu IPv4 ipv4;
ipv4.SetBase („10.1.1.0”, „255.255.255.0”);
Interfejsy Ipv4InterfaceContainer = ipv4.Assign (urządzenia);
...
...
pomocnik.EnableAsciiIpv4 („przedrostek”, interfejsy);

Spowodowałoby to utworzenie pewnej liczby plików śledzenia ASCII, z których każdy będzie następujący
the -N -I konwencja .tr. Łącząc wszystkie ślady w plik a
pojedynczy plik realizuje się podobnie jak w powyższych przykładach:

węzły NodeContainer;
...
Urządzenia NetDeviceContainer = urządzenieHelper.Install (węzły);
...
Pomocnik adresu IPv4 ipv4;
ipv4.SetBase („10.1.1.0”, „255.255.255.0”);
Interfejsy Ipv4InterfaceContainer = ipv4.Assign (urządzenia);
...
Cz strumień = asciiTraceHelper.CreateFileStream („nazwa-pliku-śladu.tr”);
...
helper.EnableAsciiIpv4 (strumień, interfejsy);

Możesz włączyć śledzenie ASCII dla kolekcji par protokołów/interfejsów, podając plik
Kontener węzłów. Dla każdego Node Kontener węzłów znaleziono odpowiedni protokół. Dla
każdego protokołu, jego interfejsy są wyliczane, a na wynikach włączone jest śledzenie
pary. Na przykład,:

Kontener węzłów n;
...
pomocnik.EnableAsciiIpv4 („przedrostek”, n);

Spowodowałoby to utworzenie pewnej liczby plików śledzenia ASCII, z których każdy będzie następujący
the - - konwencja .tr. Łącząc wszystkie ślady w plik a
pojedynczy plik realizuje się podobnie jak w powyższych przykładach:

Możesz włączyć śledzenie pcap również na podstawie identyfikatora węzła i identyfikatora urządzenia. W tym przypadku,
identyfikator węzła jest tłumaczony na a Cz i odpowiedni protokół jest sprawdzany w
węzeł. Wynikowy protokół i interfejs służą do określenia wynikowego śledzenia
źródło.:

pomocnik.EnableAsciiIpv4 („przedrostek”, 21, 1);

Oczywiście ślady można połączyć w jeden plik, jak pokazano powyżej.

Na koniec możesz włączyć śledzenie ASCII dla wszystkich interfejsów w systemie wraz z powiązanymi
protokół jest tego samego typu, co zarządzany przez pomocnika urządzenia.:

pomocnik.EnableAsciiIpv4All („przedrostek”);

Spowodowałoby to utworzenie wielu plików śledzenia ASCII, po jednym dla każdego interfejsu
w systemie związany z protokołem typu zarządzanego przez pomocnika. Wszystkie te pliki
będzie podążał za -N -I
do jednego pliku odbywa się podobnie jak w powyższych przykładach.

Ascii Rysunek kalkowy Urządzenie Pomocnik Nazwa pliku Wybór
W powyższych opisach metod w stylu przedrostków zawarta jest konstrukcja kompletna
nazwy plików według metody implementacji. Zgodnie z konwencją ślady ascii w pliku ns-3 systemu są
z formularza” - - .tr."

Jak wspomniano wcześniej, każdy węzeł w systemie będzie miał przypisany przez system identyfikator węzła.
Ponieważ istnieje korespondencja jeden do jednego między protokołami i węzłami, używamy identyfikatora węzła
w celu zidentyfikowania tożsamości protokołu. Każdy interfejs w danym protokole będzie miał
indeks interfejsu (zwany także po prostu interfejsem) w odniesieniu do jego protokołu. Domyślnie,
następnie plik śledzenia ascii utworzony w wyniku włączenia śledzenia na pierwszym urządzeniu
węzeł 21, używając przedrostka „przedrostek”, będzie miał postać „przedrostek-n21-i1.tr”. Użyj przedrostka do
ujednoznacznić wiele protokołów na węzeł.

Zawsze możesz skorzystać z tzw ns-3 usługę nazw obiektów, aby było to bardziej jasne. Na przykład, jeśli
używasz usługi nazw obiektów, aby przypisać nazwę „serverIpv4” do protokołu w węźle
21, a także określ interfejs pierwszy, nazwa wynikowego pliku śledzenia ASCII zostanie automatycznie wyświetlona
stać się „prefix-nserverIpv4-1.tr”.

Rysunek kalkowy realizacja detale
Dane
W tym rozdziale opisano strukturę gromadzenia danych ns-3 (DCF), która zapewnia
możliwości pozyskiwania danych generowanych przez modele w symulatorze, do wykonywania on-line
redukcję i przetwarzanie danych oraz łączenie surowych lub przekształconych danych w różne dane wyjściowe
formaty.

Framework obsługuje obecnie samodzielne uruchomienia ns-3, które nie są zależne od żadnego zewnętrznego
kontrola wykonania programu. Można do niego zaczepić przedmioty dostarczane przez DCF ns-3 wyśledzić
źródła umożliwiające przetwarzanie danych.

Kod źródłowy klas znajduje się w katalogu źródło/statystyki.

Rozdział ten jest zorganizowany w następujący sposób. Po pierwsze, przegląd architektury
przedstawione. Następnie zaprezentowane są pomocniki dla tych klas; to wstępne leczenie
powinno umożliwiać podstawowe wykorzystanie ram gromadzenia danych w wielu przypadkach użycia. Użytkownicy, którzy
chcą produkować poza zakresem obecnych pomocników lub którzy chcą tworzyć
własne obiekty gromadzenia danych, należy przeczytać pozostałą część rozdziału, która brzmi
szczegółowo o wszystkich podstawowych typach obiektów DCF i zapewnia kodowanie niskiego poziomu
przykłady.

Wnętrze
DCF składa się z trzech podstawowych klas:

· uczciwy to mechanizm służący do instrumentowania i kontrolowania wyników symulacji
wykorzystywane do monitorowania ciekawych wydarzeń. Generuje dane wyjściowe w postaci jednego lub większej liczby ns-3
źródła śladowe. Obiekty sondy są podłączone do jednego lub więcej śladów Zlewozmywaki (nazywa
Kolekcjonerzy), które przetwarzają próbki on-line i przygotowują je do druku.

· Kolektor zużywa dane wygenerowane przez jeden lub więcej obiektów Probe. To działa
transformacje danych, takie jak normalizacja, redukcja i obliczanie
podstawowe statystyki. Obiekty zbierające nie generują danych, które są bezpośrednio wysyłane przez
bieg ns-3; zamiast tego wysyłają dane do obiektu innego typu, zwanego
Agregator, który pełni tę funkcję. Zazwyczaj moduły zbierające wysyłają swoje dane do formatu
również w formie źródeł śladowych, umożliwiających łączenie kolektorów szeregowo.

· Agregator to punkt końcowy danych zebranych przez sieć sond i kolektorów.
Głównym zadaniem Agregatora jest porządkowanie danych i ich odpowiedników
metadane do różnych formatów wyjściowych, takich jak zwykłe pliki tekstowe, pliki arkuszy kalkulacyjnych lub
bazy danych.

Wszystkie trzy klasy zapewniają możliwość dynamicznego włączania i wyłączania
przez całą symulację.

Dowolny samodzielny ns-3 przebieg symulacji wykorzystujący DCF zazwyczaj tworzy co najmniej jeden
wystąpienie każdej z trzech powyższych klas.
[image] Przegląd struktury gromadzenia danych.UNINDENT

Ogólny przepływ przetwarzania danych jest przedstawiony w Dane przegląd.
Po lewej stronie bieg ns-3 pokazano symulację. W trakcie prowadzenia
symulacji dane są udostępniane za pomocą modeli za pośrednictwem źródeł śledzenia lub w inny sposób.
Diagram przedstawia, że ​​do tych źródeł śledzenia można podłączyć sondy w celu odbioru danych
asynchronicznie lub sondy mogą odpytywać o dane. Dane są następnie przekazywane do obiektu zbierającego
który przekształca dane. Na koniec agregator można podłączyć do wyjść układu
Collector do generowania wykresów, plików lub baz danych.
[image] Agregacja struktury gromadzenia danych.UNINDENT

Odmianę powyższego rysunku przedstawiono w Dane zbiór.
Ta druga figura ilustruje, że obiekty DCF mogą być w pewien sposób połączone ze sobą łańcuchem
że obiekty położone niżej pobierają dane wejściowe z wielu obiektów położonych wyżej. Figura
koncepcyjnie pokazuje, że wiele sond może generować dane wyjściowe, które są podawane do jednej
kolektor; na przykład kolektor generujący stosunek dwóch liczników
zazwyczaj pozyskuje dane każdego licznika z oddzielnych sond. Wielu kolekcjonerów może również
zasilają pojedynczy agregator, który (jak sama nazwa wskazuje) może gromadzić wiele danych
strumienie do włączenia do pojedynczego wykresu, pliku lub bazy danych.

Dane Pomocnicy
Pełną elastyczność ram gromadzenia danych zapewnia wzajemne połączenie
sond, kolektorów i agregatorów. Wykonanie wszystkich tych połączeń prowadzi do
wiele instrukcji konfiguracyjnych w programach użytkownika. Dla ułatwienia użycia niektóre z najczęstszych
operacje można łączyć i kapsułkować w funkcjach pomocniczych. Ponadto niektóre
wypowiedzi obejmujące ns-3 źródła śledzenia nie mają powiązań w języku Python ze względu na ograniczenia w
wiązania.

Dane Pomocnicy Omówienie
W tej sekcji przedstawiamy przegląd niektórych klas pomocniczych, które zostały stworzone
ułatwić konfigurację struktury gromadzenia danych dla niektórych typowych przypadków użycia. The
pomocnicy umożliwiają użytkownikom tworzenie typowych operacji za pomocą tylko kilku instrukcji w języku C++ lub
Programy w Pythonie. Ale ta łatwość obsługi ma znacznie niższą cenę
elastyczność, jaką może zapewnić konfiguracja niskopoziomowa, oraz konieczność jawnego kodowania
obsługa nowych typów sond w pomocnikach (aby obejść problem opisany poniżej).

Nacisk na obecne pomoce polega na gromadzeniu danych ns-3 źródła śledzenia
wykresy gnuplot lub pliki tekstowe, bez wysokiego stopnia dostosowywania wyników lub statystyk
przetwarzanie (wstępnie). Ponadto zastosowanie jest ograniczone do dostępnych typów sond w
ns-3. W dalszej części tej dokumentacji omówimy bardziej szczegółowo tworzenie nowych plików
Typy sond, a także szczegóły dotyczące łączenia sond, kolektorów i agregatorów
w niestandardowych aranżacjach.

Do chwili obecnej zaimplementowano dwa pomocniki gromadzenia danych:

· GnuplotHelper

· Pomocnik plików

Pomocnik Gnuplot
GnuplotHelper jest klasą pomocniczą służącą do tworzenia plików wyjściowych używanych do tworzenia gnuplotów. The
ogólnym celem jest zapewnienie użytkownikom możliwości szybkiego tworzenia wykresów na podstawie wyeksportowanych danych
in ns-3 źródła śladowe. Domyślnie wykonywana jest minimalna ilość transformacji danych;
celem jest wygenerowanie wykresów z jak najmniejszą liczbą (domyślnych) instrukcji konfiguracyjnych
możliwe.

Pomocnik Gnuplot Omówienie
Na koniec symulacji GnuplotHelper utworzy 3 różne pliki:

· Oddzielony spacjami plik danych gnuplot

· Plik kontrolny gnuplot

· Skrypt powłoki do generowania gnuplot

Do utworzenia wykresów potrzebne są dwie instrukcje konfiguracyjne. Pierwszy
instrukcja konfiguruje wykres (nazwę pliku, tytuł, legendy i typ wyniku, gdzie wyjście
typ domyślnie jest ustawiony na PNG, jeśli nie został określony):

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

Drugie stwierdzenie łączy interesujące źródło śledzenia:

void PlotProbe (const std::string &typeId,
const std::string i ścieżka,
const std::string &probeTraceSource,
const std::string &title);

Argumenty są następujące:

· Identyfikator typu: The ns-3 TypeId sondy

· ścieżka: Ścieżka w ns-3 konfiguracji do jednego lub większej liczby źródeł śledzenia

·probeTraceSource: Które wyjście sondy (same w sobie źródło śledzenia) powinno zostać wykreślone

· tytuł: Tytuł powiązany ze zbiorami danych (w legendzie gnuplot)

Wariant powyższego narzędzia PlotProbe polega na określeniu piątego opcjonalnego argumentu sterującego
gdzie w fabule umieszczony jest klucz (legenda).

W pełni działający przykład (z siódmy.cc) pokazano poniżej:

// Utwórz pomocnika gnuplot.
GnuplotHelper plotHelper;

// Skonfiguruj wykres.
// Skonfiguruj wykres. Pierwszym argumentem jest prefiks nazwy pliku
// dla wygenerowanych plików wyjściowych. Drugi, trzeci i czwarty
// argumentami są odpowiednio tytuł wykresu, etykiety osi x i osi y
plotHelper.ConfigurePlot („liczba bajtów siódmego pakietu”,
"Liczba bajtów pakietu a czas",
„Czas (sekundy)”,
„Liczba bajtów pakietu”,
„png”);

// Określ typ sondy, ścieżkę źródła śledzenia (w przestrzeni nazw konfiguracji) i
// sonduj wyjściowe źródło śledzenia („OutputBytes”) do wykreślenia. Czwarty argument
// określa nazwę etykiety serii danych na wykresie. Ostatni
// argument formatuje wykres, określając, gdzie należy umieścić klucz.
plotHelper.PlotProbe (typ sondy,
ścieżka śledzenia,
„Bajty wyjściowe”,
„Liczba bajtów pakietu”,
GnuplotAggregator::KEY_BELOW);

W tym przykładzie Typ sondy oraz Ścieżka śledzenia są następujące (dla protokołu IPv4):

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

„probeType” jest kluczowym parametrem umożliwiającym działanie tego pomocnika. Ten TypeId musi być zarejestrowany
w systemie, a podpis na ścieżce śledzenia sondy musi odpowiadać podpisowi śladu
źródło, do którego jest podłączony. Typy sond są wstępnie zdefiniowane dla wielu typów danych
odpowiadających ns-3 prześledzone wartości i dla kilku innych sygnatur źródeł śledzenia, takich jak
źródło śledzenia „Tx”. Protokół ns3::Ipv4L3 class.

Należy pamiętać, że określona ścieżka źródła śledzenia może zawierać symbole wieloznaczne. W tym przypadku wielokrotność
zbiory danych są naniesione na jeden wykres; po jednym dla każdej dopasowanej ścieżki.

Głównym efektem końcowym będą trzy pliki:

liczba bajtów siódmego pakietu.dat
siódmy-pakiet-byte-count.plt
siódmy pakiet-bajtów.sh

W tym momencie użytkownicy mogą albo ręcznie edytować plik .plt w celu dalszych dostosowań, albo
po prostu uruchom go przez gnuplot. Działanie sh siódmy pakiet-bajtów.sh po prostu kieruje fabułą
poprzez gnuplot, jak pokazano poniżej.
[image] Wykres 2-D Gnuplot Utworzony przez Seventh.cc Przykład..UNINDENT

Można zauważyć, że kluczowe elementy (legenda, tytuł, umiejscowienie legendy, xlabel, ylabel,
i ścieżka do danych) są umieszczane na wykresie. Ponieważ odbyły się dwa mecze
podaną ścieżkę konfiguracji, pokazane zostaną dwie serie danych:

· Liczba bajtów pakietu-0 odpowiada /NodeList/0/$ns3::Ipv4L3Protocol/Tx

· Liczba bajtów pakietu-1 odpowiada /NodeList/1/$ns3::Ipv4L3Protocol/Tx

Pomocnik Gnuplot Skonfiguruj działkę
Pomocnik Gnuplot Skonfiguruj działkę() Funkcja może służyć do konfigurowania wykresów.

Ma następujący prototyp:

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

Posiada następujące argumenty:

┌───────────────────────────────┬───┬─────── ────────── ─────────────────┐
│Argument │ Opis │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nazwa plików powiązanych z gnuplot do │
│ │ pisz bez rozszerzenia. │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│title │ Ciąg tytułowy fabuły używany dla │
│ │ tę fabułę. │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│xLegend │ Legenda dla x poziomego │
│ │ oś. │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│yLegend │ Legenda pionu y │
│ │ oś. │
└────────────────────────────────────┴─────── ────────── ─────────────────┘

│terminalType │ Ciąg ustawiający typ terminala dla │
│ │ wyjście. Domyślny terminal │
│ │ typ to „png”. │
└────────────────────────────────────┴─────── ────────── ─────────────────┘

Pomocnik Gnuplot Skonfiguruj działkę() funkcja konfiguruje w tym celu parametry związane z wykresem
pomocnika gnuplot, tak aby utworzył plik danych gnuplot oddzielonych spacjami o nazwie
OutputFileNameWithoutExtension + „.dat”, plik kontrolny gnuplot o nazwie
OutputFileNameWithoutExtension + „.plt” i skrypt powłoki do generowania gnuplotu o nazwie
nazwapliku wyjściowegoBez rozszerzenia + „.sh”.

Przykład użycia tej funkcji można zobaczyć w pliku siódmy.cc kod opisany powyżej
gdzie użyto go w następujący sposób:

plotHelper.ConfigurePlot („liczba bajtów siódmego pakietu”,
"Liczba bajtów pakietu a czas",
„Czas (sekundy)”,
„Liczba bajtów pakietu”,
„png”);

Pomocnik Gnuplot PlotSonda
Pomocnik Gnuplot Sonda wykresu() Funkcja może być używana do wykreślania wartości generowanych przez sondy.

Ma następujący prototyp:

void PlotProbe (const std::string &typeId,
const std::string i ścieżka,
const std::string &probeTraceSource,
const std::string &title,
wyliczenie GnuplotAggregator::KeyLocation keyLocation = GnuplotAggregator::KEY_INSIDE);

Posiada następujące argumenty:

┌─────────────────┬───────────────────── ─────────────┐
│Argument │ Opis │
├─────────────────┼───────────────────── ─────────────┤
│typeId │ Identyfikator typu sondy │
│ │ stworzony przez tego pomocnika. │
├─────────────────┼───────────────────── ─────────────┤
│ścieżka │ Skonfiguruj ścieżkę dostępu do śledzenia │
│ │ źródło. │
├─────────────────┼───────────────────── ─────────────┤
│probeTraceSource │ Źródło śledzenia sondy do │
│ │ dostęp. │
├─────────────────┼───────────────────── ─────────────┤
│tytuł │ Tytuł, z którym ma być powiązany │
│ │ ten zbiór danych │
├─────────────────┼───────────────────── ─────────────┤
│keyLocation │ Lokalizacja klucza w │
│ │ fabuła. Domyślna lokalizacja to │
│ │ wewnątrz. │
└─────────────────┴───────────────────── ─────────────┘

Pomocnik Gnuplot Sonda wykresu() Funkcja wykreśla zbiór danych wygenerowany przez zaczepienie ns-3
śledzenia źródła za pomocą sondy utworzonej przez pomocnika, a następnie wykreślenie wartości z pliku
źródło sondyTrace. Zbiór danych będzie miał podany tytuł i będzie się składał z
„newValue” przy każdym znaczniku czasu.

Jeśli ścieżka konfiguracji ma więcej niż jedno dopasowanie w systemie ze względu na symbol wieloznaczny, wówczas
dla każdego dopasowania zostanie wykreślony jeden zbiór danych. Tytuły zbiorów danych będą oznaczone przyrostkiem
dopasowane znaki dla każdego symbolu wieloznacznego w ścieżce konfiguracji, oddzielone spacjami. Dla
na przykład, jeśli proponowany tytuł zbioru danych to ciąg „bajty” i występują dwa symbole wieloznaczne
w ścieżce, wówczas możliwe będą tytuły zbiorów danych, takie jak „bajty-0 0” lub „bajty-12 9” jako
etykiety zestawów danych, które są wykreślane.

Przykład użycia tej funkcji można zobaczyć w pliku siódmy.cc kod opisany powyżej
gdzie został użyty (z podstawieniem zmiennej) w następujący sposób:

plotHelper.PlotProbe („ns3::Ipv4PacketProbe”,
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
„Bajty wyjściowe”,
„Liczba bajtów pakietu”,
GnuplotAggregator::KEY_BELOW);

Inne Przykłady
gnuplot Pomocnik Przykład
Nieco prostszy przykład niż siódmy.cc przykład można znaleźć w
src/stats/examples/gnuplot-helper-example.cc. Poniższy dwuwymiarowy wykres gnup został utworzony przy użyciu
przykład.
[image] Gnuplot 2-D Utworzony przez gnuplot-helper-example.cc Przykład..UNINDENT

W tym przykładzie istnieje obiekt Emiter, który zwiększa swój licznik zgodnie z a
Poissona, a następnie emituje wartość licznika jako źródło śledzenia.

Cz emiter = UtwórzObiekt ();
Nazwy::Dodaj („/Nazwy/Emiter”, emiter);

Należy pamiętać, że ponieważ w poniższej ścieżce nie ma symboli wieloznacznych, był tylko 1 strumień danych
narysowane na fabule. Ten pojedynczy strumień danych na wykresie jest po prostu oznaczony jako „Liczba emiterów”,
bez dodatkowych przyrostków, jakie można by zobaczyć, gdyby na ścieżce znajdowały się symbole wieloznaczne.

// Utwórz pomocnika gnuplot.
GnuplotHelper plotHelper;

// Skonfiguruj wykres.
plotHelper.ConfigurePlot („przykład-pomocnika-gnuplot”,
„Liczba emiterów w funkcji czasu”,
„Czas (sekundy)”,
„Liczba emiterów”,
„png”);

// Wykreśl wartości wygenerowane przez sondę. Ścieżka, którą zapewniamy
// pomaga ujednoznacznić źródło śledzenia.
plotHelper.PlotProbe („ns3::Uinteger32Probe”,
"/Nazwy/Emiter/Licznik",
"Wyjście",
„Liczba emiterów”,
GnuplotAggregator::KEY_INSIDE);

Pomocnik plików
FileHelper to klasa pomocnicza używana do umieszczania wartości danych w pliku. Ogólnym celem jest
aby zapewnić użytkownikom możliwość szybkiego tworzenia sformatowanych plików tekstowych na podstawie wyeksportowanych danych
in ns-3 źródła śladowe. Domyślnie wykonywana jest minimalna ilość transformacji danych;
celem jest wygenerowanie plików z tak małą liczbą (domyślnych) instrukcji konfiguracyjnych, jak
możliwe.

Pomocnik plików Omówienie
FileHelper utworzy 1 lub więcej plików tekstowych na koniec symulacji.

FileHelper może tworzyć 4 różne typy plików tekstowych:

· Sformatowany

· Oddzielone spacjami (domyślnie)

· Oddzielone przecinkami

· Zakładka oddzielona

Sformatowane pliki używają ciągów formatu w stylu C i funkcji sprintf() do drukowania
wartości w zapisywanym pliku.

Poniższy plik tekstowy z 2 kolumnami sformatowanych wartości o nazwie
siódmy pakiet-bajtów-0.txt został stworzony przy użyciu nowego kodu, który został dodany do
oryginalny ns-3 Przykładowy kod tutoriala. Wyświetlanych jest tylko pierwsze 10 linii tego pliku
tutaj dla zwięzłości.

Czas (sekundy) = 1.000e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.004e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.004e+00 Liczba bajtów pakietu = 576
Czas (sekundy) = 1.009e+00 Liczba bajtów pakietu = 576
Czas (sekundy) = 1.009e+00 Liczba bajtów pakietu = 576
Czas (sekundy) = 1.015e+00 Liczba bajtów pakietu = 512
Czas (sekundy) = 1.017e+00 Liczba bajtów pakietu = 576
Czas (sekundy) = 1.017e+00 Liczba bajtów pakietu = 544
Czas (sekundy) = 1.025e+00 Liczba bajtów pakietu = 576
Czas (sekundy) = 1.025e+00 Liczba bajtów pakietu = 544

...

Następujący inny plik tekstowy z 2 kolumnami sformatowanych wartości o nazwie
siódmy pakiet-bajtów-1.txt został również utworzony przy użyciu tego samego nowego kodu, który został dodany
oryginalny ns-3 Przykładowy kod tutoriala. Wyświetlanych jest tylko pierwsze 10 linii tego pliku
tutaj dla zwięzłości.

Czas (sekundy) = 1.002e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.007e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.013e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.020e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.028e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.036e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.045e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.053e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.061e+00 Liczba bajtów pakietu = 40
Czas (sekundy) = 1.069e+00 Liczba bajtów pakietu = 40

...

Nowy kod dodany w celu utworzenia dwóch plików tekstowych znajduje się poniżej. Więcej szczegółów dot
ten interfejs API zostanie omówiony w dalszej części.

Należy pamiętać, że ponieważ w ścieżce znajdowały się 2 dopasowania symbolu wieloznacznego, powstały 2 oddzielne pliki tekstowe
zostały stworzone. Pierwszy plik tekstowy o nazwie „siódmy pakiet bajtów-0.txt”,
odpowiada dopasowaniu wieloznacznemu z „*” zastąpionym przez „0”. Drugi plik tekstowy,
o nazwie „siódmy pakiet bajtów-1.txt” odpowiada dopasowaniu wieloznacznemu z
„*” zastąpiono „1”. Należy również pamiętać, że wywołanie funkcji to Napiszsondę() da
komunikat o błędzie, jeśli nie ma dopasowań dla ścieżki zawierającej symbole wieloznaczne.

// Utwórz plik pomocniczy.
PlikHelper plikHelper;

// Skonfiguruj plik do zapisania.
fileHelper.ConfigureFile („liczba bajtów siódmego pakietu”,
FileAggregator::FORMATTED);

// Ustaw etykiety dla tego sformatowanego pliku wyjściowego.
fileHelper.Set2dFormat („Czas (sekundy) = %.3e\tLiczba bajtów pakietu = %.0f”);

// Zapisz wartości wygenerowane przez sondę.
plikHelper.WriteProbe („ns3::Ipv4PacketProbe”,
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
„Bajty wyjściowe”);

Pomocnik plików Skonfiguruj plik
Pomocnik plików Plik konfiguracyjny() Funkcja może być używana do konfiguracji plików tekstowych.

Ma następujący prototyp:

void ConfigureFile (const std::string &outputFileNameWithoutExtension,
wyliczenie FileAggregator::FileType fileType = FileAggregator::SPACE_SEPARATED);

Posiada następujące argumenty:

┌───────────────────────────────┬───┬─────── ────────── ─────────────────┐
│Argument │ Opis │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│outputFileNameWithoutExtension │ Nazwa pliku wyjściowego do zapisu │
│ │ bez przedłużenia. │
├────────────────────────────────────┼─────── ────────── ─────────────────┤
│fileType │ Typ pliku do zapisania. │
│ │ domyślnym typem pliku jest spacja │
│ │ oddzielone. │
└────────────────────────────────────┴─────── ────────── ─────────────────┘

Pomocnik plików Plik konfiguracyjny() Funkcja konfiguruje parametry związane z plikiem tekstowym dla
pomocnika pliku, aby utworzył plik o nazwie OutputFileNameWithoutExtension plus
możliwe dodatkowe informacje z dopasowań z symbolami wieloznacznymi plus „.txt” z wartościami drukowanymi jako
określony przez typ pliku. Domyślny typ pliku jest rozdzielany spacjami.

Przykład użycia tej funkcji można zobaczyć w pliku siódmy.cc kod opisany powyżej
gdzie użyto go w następujący sposób:

fileHelper.ConfigureFile („liczba bajtów siódmego pakietu”,
FileAggregator::FORMATTED);

Pomocnik plików NapiszSonda
Pomocnik plików Napiszsondę() Funkcja może służyć do zapisywania wartości generowanych przez sondy do
pliki tekstowe.

Ma następujący prototyp:

void WriteProbe (const std::string &typeId,
const std::string i ścieżka,
const std::string &probeTraceSource);

Posiada następujące argumenty:

┌─────────────────┬───────────────────── ─────────────┐
│Argument │ Opis │
├─────────────────┼───────────────────── ─────────────┤
│typeId │ Identyfikator typu sondy, który ma być │
│ │ stworzony. │
├─────────────────┼───────────────────── ─────────────┤
│ścieżka │ Skonfiguruj ścieżkę dostępu do śledzenia │
│ │ źródło. │
├─────────────────┼───────────────────── ─────────────┤
│probeTraceSource │ Źródło śledzenia sondy do │
│ │ dostęp. │
└─────────────────┴───────────────────── ─────────────┘

Pomocnik plików Napiszsondę() Funkcja tworzy wyjściowe pliki tekstowe wygenerowane przez zaczepienie
źródło śledzenia ns-3 za pomocą sondy utworzonej przez pomocnika, a następnie zapisanie wartości z pliku
źródło sondyTrace. Nazwy plików wyjściowych będą miały tekst przechowywany w zmiennej składowej
m_outputFileNameWithoutExtension plus „.txt” i będzie składać się z „newValue” w każdym
znak czasu.

Jeśli ścieżka konfiguracji ma więcej niż jedno dopasowanie w systemie ze względu na symbol wieloznaczny, wówczas
dla każdego dopasowania zostanie utworzony jeden plik wyjściowy. Nazwy plików wyjściowych będą zawierać rozszerzenie
tekst w m_outputFileNameWithoutExtension plus dopasowane znaki dla każdego z
symbole wieloznaczne w ścieżce konfiguracji oddzielone myślnikami i „.txt”. Na przykład, jeśli wartość
w m_outputFileNameWithoutExtension znajduje się ciąg „liczba bajtów pakietu” i są dwa
symbole wieloznaczne w ścieżce, a następnie wypisz nazwy plików, takie jak „liczba bajtów pakietu-0-0.txt” lub
„packet-byte-count-12-9.txt” będzie możliwe jako nazwy plików, które zostaną utworzone.

Przykład użycia tej funkcji można zobaczyć w pliku siódmy.cc kod opisany powyżej
gdzie użyto go w następujący sposób:

plikHelper.WriteProbe („ns3::Ipv4PacketProbe”,
"/NodeList/*/$ns3::Ipv4L3Protocol/Tx",
„Bajty wyjściowe”);

Inne Przykłady
filet Pomocnik Przykład
Nieco prostszy przykład niż siódmy.cc przykład można znaleźć w
src/stats/examples/file-helper-example.cc. W tym przykładzie użyto tylko FileHelper.

Poniższy plik tekstowy z 2 kolumnami sformatowanych wartości o nazwie plik-pomocnik-przykład.txt
został stworzony na przykładzie. Pokazano tutaj tylko pierwsze 10 linii tego pliku
zwięzłość.

Czas (sekundy) = 0.203 Liczba = 1
Czas (sekundy) = 0.702 Liczba = 2
Czas (sekundy) = 1.404 Liczba = 3
Czas (sekundy) = 2.368 Liczba = 4
Czas (sekundy) = 3.364 Liczba = 5
Czas (sekundy) = 3.579 Liczba = 6
Czas (sekundy) = 5.873 Liczba = 7
Czas (sekundy) = 6.410 Liczba = 8
Czas (sekundy) = 6.472 Liczba = 9
...

W tym przykładzie istnieje obiekt Emiter, który zwiększa swój licznik zgodnie z a
Poissona, a następnie emituje wartość licznika jako źródło śledzenia.

Cz emiter = UtwórzObiekt ();
Nazwy::Dodaj („/Nazwy/Emiter”, emiter);

Należy pamiętać, że ponieważ w poniższej ścieżce nie ma żadnych symboli wieloznacznych, był tylko 1 plik tekstowy
Utworzony. Ten pojedynczy plik tekstowy nosi po prostu nazwę „file-helper-example.txt” i nie zawiera żadnych dodatków
sufiksy, tak jakbyś widział, gdyby na ścieżce znajdowały się symbole wieloznaczne.

// Utwórz plik pomocniczy.
PlikHelper plikHelper;

// Skonfiguruj plik do zapisania.
fileHelper.ConfigureFile („przykład-pomocnika-pliku”,
FileAggregator::FORMATTED);

// Ustaw etykiety dla tego sformatowanego pliku wyjściowego.
fileHelper.Set2dFormat („Czas (sekundy) = %.3e\tCount = %.0f”);

// Zapisz wartości wygenerowane przez sondę. Ścieżka, którą my
// zapewnianie pomaga w ujednoznacznieniu źródła śledzenia.
plikHelper.WriteProbe („ns3::Uinteger32Probe”,
"/Nazwy/Emiter/Licznik",
"Wyjście");

Zakres oraz Ograniczenia
Obecnie tylko te sondy zostały zaimplementowane i połączone z GnuplotHelper i
do FileHelpera:

· BooleanProbe

· Podwójna sonda

· Uinteger8Probe

· Uinteger16Probe

· Uinteger32Probe

· Sonda Czasowa

· PacketProbe

· ApplicationPacketProbe

· Ipv4PacketProbe

Dlatego te sondy są jedynymi dostępnymi identyfikatorami TypeId, w których można ich używać Sonda wykresu() oraz
Napiszsondę().

W następnych kilku sekcjach omówimy każdy z podstawowych typów obiektów (sonda, kolektor,
i Agregator) bardziej szczegółowo i pokaż, jak można je ze sobą połączyć za pomocą
API niższego poziomu.

sondy
W tej sekcji szczegółowo opisano funkcje udostępniane przez klasę Probe obiektowi an ns-3
symulację i podaje przykłady kodowania ich w programie. Ta sekcja jest przeznaczona dla
użytkownicy zainteresowani tworzeniem symulacji za pomocą ns-3 narzędzi i korzystania z Danych
Collection Framework, którego częścią jest klasa Probe, służący do generowania danych wyjściowych
wyniki ich symulacji.

uczciwy Omówienie
Obiekt Probe należy połączyć ze zmienną z symulacji, której wartości
w całym eksperymencie są istotne dla użytkownika. Sonda zarejestruje, co było
wartości przyjęte przez zmienną w trakcie symulacji i przekazać te dane do innej
członek ram gromadzenia danych. Chociaż jest to poza zakresem tej sekcji
omówić, co dzieje się po wygenerowaniu sygnału wyjściowego przez sondę, wystarczy powiedzieć, że przez
po zakończeniu symulacji użytkownik będzie miał szczegółowe informacje jakie były wartości
przechowywane wewnątrz zmiennej badanej podczas symulacji.

Zazwyczaj sonda jest podłączona do ns-3 źródło śledzenia. W ten sposób za każdym razem, gdy
źródło śledzenia eksportuje nową wartość, sonda wykorzystuje tę wartość (i eksportuje ją w dół).
do innego obiektu poprzez własne źródło śledzenia).

Sonda może być traktowana jako swego rodzaju filtr źródeł śladów. Główne powody
możliwe podłączenie do sondy, a nie bezpośrednio do źródła śledzenia, są następujące:

· Sondy mogą być dynamicznie włączane i wyłączane podczas symulacji za pomocą wywołań Włączać()
oraz Wyłączyć(). Na przykład wysyłanie danych może zostać wyłączone podczas
faza rozgrzewki symulacji.

· Sondy mogą wykonywać operacje na danych w celu wyodrębnienia wartości z bardziej skomplikowanych
Struktury; na przykład wyświetlenie wartości rozmiaru pakietu z odebranego pakietu ns3::Packet.

· Sondy rejestrują nazwę w przestrzeni nazw ns3::Config (używając Imiona::Dodaj ()) więc ten drugi
przedmioty mogą się do nich odnosić.

· Sondy zapewniają metodę statyczną, która pozwala manipulować sondą według nazwy, np
co się robi w ns2measure [Cic06]

Stat::put („my_metric”, identyfikator, próbka);

Odpowiednikiem ns-3 powyższego kodu ns2measure jest np

DoubleProbe::SetValueByPath („/ścieżka/do/sonda”, przykład);

Tworzenie
Należy pamiętać, że nie można utworzyć obiektu klasy bazowej Probe, ponieważ jest to baza abstrakcyjna
class, czyli posiada czysto wirtualne funkcje, które nie zostały zaimplementowane. Obiekt
typ DoubleProbe, który jest podklasą klasy Probe, zostanie tutaj utworzony w celu pokazania
co musi być zrobione.

Deklaruje się DoubleProbe w pamięci dynamicznej, używając klasy inteligentnego wskaźnika (Ptr ). Do
utwórz DoubleProbe w pamięci dynamicznej z inteligentnymi wskaźnikami, wystarczy wywołać metodę
ns-3 metoda UtwórzObiekt():

Cz moja sonda = Utwórz obiekt ();

Powyższa deklaracja tworzy DoubleProbes przy użyciu domyślnych wartości swoich atrybutów.
W klasie DoubleProbe znajdują się cztery atrybuty; dwa w obiekcie klasy bazowej
DataCollectionObject i dwa w klasie bazowej Probe:

· „Nazwa” (DataCollectionObject), wartość StringValue

· „Włączone” (DataCollectionObject), wartość logiczna

· „Start” (sonda), wartość TimeValue

· „Stop” (sonda), wartość TimeValue

Można ustawić takie atrybuty podczas tworzenia obiektu, stosując następującą metodę:

Cz myprobe = CreateObjectWithAttributes (
„Nazwa”, StringValue („mojasonda”),
„Włączone”, BooleanValue (false),
„Start”, wartość czasu (sekundy (100.0)),
„Stop”, Wartość czasu (sekundy (1000.0)));

Start i Stop to zmienne czasowe, które określają interwał działania sondy. The
Sonda wyprowadzi dane tylko wtedy, gdy bieżący czas symulacji będzie się w nich mieścił
interwał. Specjalna wartość czasu 0 sekund dla Stop wyłączy ten atrybut (tj
nie wyłączaj sondy przez całą symulację). Enabled to flaga włączająca sondę lub
wyłączone i musi być ustawione na true, aby sonda mogła eksportować dane. Nazwa to nazwa obiektu
w ramach DCF.

Importowanie oraz eksportowanie dane
ns-3 Źródła śledzenia są silnie typowane, więc mechanizmy podłączania sond do śladu
source i do eksportowania danych należą do jego podklas. Na przykład domyślny
dystrybucja ns-3 udostępnia klasę DoubleProbe zaprojektowaną do podpinania się do śladu
źródło eksportujące podwójną wartość. Następnie szczegółowo opiszemy działanie DoubleProbe i
następnie omów, w jaki sposób użytkownik może zdefiniować inne klasy Probe.

Podwójna sonda Omówienie
DoubleProbe łączy się z podwójną wartością ns-3 źródło śledzenia i sam eksportuje plik a
różne, podwójnie cenione ns-3 źródło śledzenia.

Poniższy kod, zaczerpnięty z src/stats/examples/double-probe-example.cc, pokazuje podstawowe
operacje podłączenia DoubleProbe do symulacji, w której sonduje on licznik
eksportowane przez obiekt emitera (klasa Emiter).

Cz emiter = UtwórzObiekt ();
Nazwy::Dodaj („/Nazwy/Emiter”, emiter);
...

Cz sonda1 = Utwórz obiekt ();

// Podłącz sondę do licznika emitera
bool podłączony = sonda1->ConnectByObject („Licznik”, emiter);

Poniższy kod sprawdza ten sam licznik wyeksportowany przez ten sam obiekt emitera. Ten
DoubleProbe używa jednak ścieżki w przestrzeni nazw konfiguracji, aby utworzyć plik
połączenie. Należy zauważyć, że emiter zarejestrował się w przestrzeni nazw konfiguracji po
zostało stworzone; w przeciwnym razie ConnectByPath nie będzie działać.

Cz sonda2 = Utwórz obiekt ();

// Uwaga, tutaj nie jest sprawdzana żadna zwracana wartość
sonda2->ConnectByPath („/Nazwy/Emiter/Licznik”);

Następna sonda DoubleProbe pokazana poniżej będzie miała ustawioną wartość przy użyciu ścieżki
przestrzeń nazw konfiguracji. Należy pamiętać, że tym razem DoubleProbe zarejestrował się w
przestrzeni nazw konfiguracji po jej utworzeniu.

Cz sonda3 = Utwórz obiekt ();
sonda3->SetName („Statycznie dostępna sonda”);

// Musimy dodać go do bazy danych konfiguracji
Nazwy::Add („/Nazwy/Sondy”, sonda3->GetName (), sonda3);

Funkcja Count() emitera może teraz ustawić wartość dla tej sondy DoubleProbe jako
następuje:

unieważnić
Emiter::Count (puste)
{
...
m_licznik += 1.0;
DoubleProbe::SetValueByPath („/Names/StaticallyAccessedProbe”, m_counter);
...
}

Powyższy przykład pokazuje, że kod wywołujący Probe nie musi mieć jawnego kodu
odniesienie do Probe, ale może kierować ustawieniem wartości poprzez przestrzeń nazw Config.
Jest to podobne pod względem funkcjonalności do Statystyka::Umieść metoda wprowadzona w artykule ns2measure
[Cic06] i pozwala użytkownikom tymczasowo wstawiać instrukcje Probe, takie jak printf oświadczenia
w ramach istniejącego ns-3 modele. Należy pamiętać, że aby móc używać w tym przypadku DoubleProbe
przykład taki, potrzebne były 2 rzeczy:

1. w przykładowym pliku .cc zawarto plik nagłówkowy modułu statystyk

2. przykład został uzależniony od modułu statystyk w jego pliku wscript.

Analogiczne czynności należy wykonać, aby dodać kolejne Sondy w innych miejscach w pliku ns-3
podstawa kodu.

Wartości dla DoubleProbe można również ustawić za pomocą funkcji DoubleProbe::SetValue(),
podczas gdy wartości dla DoubleProbe można uzyskać za pomocą tej funkcji
DoubleProbe::GetValue().

DoubleProbe eksportuje podwójne wartości do swojego źródła śledzenia „Output”; obiekt położony poniżej
można podłączyć do tego odbiornik śledzenia (NotifyViaProbe) w następujący sposób:

podłączony = sonda1->TraceConnect („Wyjście”, sonda1->GetName (), MakeCallback (&NotifyViaProbe));

Inne sondy
Oprócz DoubleProbe dostępne są również następujące sondy:

· Uinteger8Probe łączy się z ns-3 źródło śledzenia eksportujące plik uint8_t.

· Uinteger16Probe łączy się z ns-3 źródło śledzenia eksportujące plik uint16_t.

· Uinteger32Probe łączy się z ns-3 źródło śledzenia eksportujące plik uint32_t.

· PacketProbe łączy się z ns-3 źródło śledzenia eksportujące pakiet.

· ApplicationPacketProbe łączy się z ns-3 źródło śledzenia eksportujące pakiet i gniazdo
adres.

· Ipv4PacketProbe łączy się z ns-3 źródło śledzenia eksportujące pakiet, obiekt IPv4 i
interfejs.

Tworzenie nowych uczciwy typy
Aby utworzyć nowy typ sondy, należy wykonać następujące kroki:

· Upewnij się, że nowa klasa Probe wywodzi się z klasy bazowej Probe.

· Upewnij się, że czyste funkcje wirtualne, z których dziedziczy Twoja nowa klasa Probe
Zaimplementowano klasę bazową sondy.

· Znajdź istniejącą klasę Probe, która korzysta ze źródła śledzenia najbliższego typowi
typ źródła śledzenia, którego będzie używać Twoja sonda.

· Skopiuj plik nagłówkowy istniejącej klasy Probe (.h) i plik implementacyjny (.cc) do dwóch
nowe pliki o nazwach pasujących do Twojej nowej sondy.

· Zastąp typy, argumenty i zmienne w skopiowanych plikach odpowiednimi
wpisz dla swojej sondy.

· Wprowadź niezbędne modyfikacje, aby kod się skompilował i aby zachowywał się tak, jakbyś chciał
lubić.

Przykłady
Dwa przykłady zostaną tutaj szczegółowo omówione:

· Przykład podwójnej sondy

· Przykład wykresu pakietów IPv4

Podwójne Geometryczne uczciwy Przykład
Przykład podwójnej sondy został omówiony wcześniej. Przykładowy program można znaleźć
in src/stats/examples/double-probe-example.cc. Podsumowując to, co dzieje się w tym programie,
istnieje emiter, który eksportuje licznik, którego wartość zwiększa się zgodnie z procesem Poissona.
W szczególności pokazane zostały dwa sposoby emisji danych:

1. poprzez śledzoną zmienną podłączoną do jednej sondy:

Wartość śledzona m_licznik; // zwykle będzie to typ całkowity

2. poprzez licznik, którego wartość jest przesyłana do drugiej sondy, do której odwołuje się jej nazwa
system konfiguracji:

unieważnić
Emiter::Count (puste)
{
NS_LOG_FUNCTION (to);
NS_LOG_DEBUG („Liczenie w „ << Simulator::Now ().GetSeconds ());
m_licznik += 1.0;
DoubleProbe::SetValueByPath („/Names/StaticallyAccessedProbe”, m_counter);
Symulator::Schedule (Sekundy (m_var->GetValue ()), &Emitter::Count, to);
}

Przyjrzyjmy się sondzie dokładniej. Sondy mogą otrzymywać swoje wartości w postaci wielokrotności
sposoby:

1. poprzez bezpośredni dostęp sondy do źródła śledzenia i podłączenie do niego źródła śledzenia

2. przez sondę uzyskującą dostęp do źródła śledzenia poprzez przestrzeń nazw konfiguracji i łączącą się z:
ślad do niego

3. przez kod wywołujący jawnie wywołujący sondę Ustalić wartość() metoda

4. przez kod wywołujący jawnie wywołujący Ustaw wartość według ścieżki
(„/ścieżka/przez/konfiguracja/przestrzeń nazw”, ...)

Oczekuje się, że dwie pierwsze techniki będą najpowszechniejsze. Również w przykładzie
Pokazane jest zaczepienie normalnej funkcji wywołania zwrotnego, jak to zwykle się robi ns-3, To
funkcja wywołania zwrotnego nie jest powiązana z obiektem Probe. Nazwiemy ten przypadek 0) poniżej.

// To jest funkcja testująca podłączenie surowej funkcji do źródła śledzenia
unieważnić
NotifyViaTraceSource (kontekst std::string, podwójna stara wartość, podwójna nowa wartość)
{
NS_LOG_DEBUG („kontekst: „ << kontekst << „stary” << stara wartość << „nowy” << nowa wartość);
}

Najpierw należy skonfigurować emiter:

Cz emiter = UtwórzObiekt ();
Nazwy::Dodaj („/Nazwy/Emiter”, emiter);

// Obiekt Emitter nie jest powiązany z węzłem ns-3, więc
// nie uruchomi się automatycznie, więc musimy to zrobić sami
Symulator::Harmonogram (sekundy (0.0), &Emiter::Start, emiter);

Różne sondy DoubleProbe współdziałają z emiterem w przykładzie pokazanym poniżej.

Przypadek 0):

// Poniżej przedstawiono typową funkcjonalność bez sondy
// (połącz funkcję ujścia ze źródłem śledzenia)
//
podłączony = emitter->TraceConnect („Licznik”, „przykładowy kontekst”, MakeCallback (&NotifyViaTraceSource));
NS_ASSERT_MSG (połączony, „Źródło śledzenia nie jest podłączone”);

przypadek 1):

//
// Sonda1 zostanie podłączona bezpośrednio do obiektu źródłowego śledzenia emitera
//

// sonda1 zostanie podłączona do źródła śledzenia emitera
Cz sonda1 = Utwórz obiekt ();
// nazwa sondy może służyć jako kontekst w śledzeniu
sonda1->SetName („ObjectProbe”);

// Podłącz sondę do licznika emitera
podłączony = sonda1->ConnectByObject („Licznik”, emiter);
NS_ASSERT_MSG (połączony, „Źródło śledzenia nie jest podłączone do sondy 1”);

przypadek 2):

//
// Sonda2 zostanie podłączona do obiektu źródłowego śledzenia emitera przez
// dostęp do niego poprzez nazwę ścieżki w bazie danych Config
//

// Utwórz kolejną podobną sondę; połączy się to poprzez ścieżkę konfiguracji
Cz sonda2 = Utwórz obiekt ();
sonda2->SetName („PathProbe”);

// Uwaga, tutaj nie jest sprawdzana żadna zwracana wartość
sonda2->ConnectByPath („/Nazwy/Emiter/Licznik”);

przypadek 4) (przypadek 3 nie jest pokazany w tym przykładzie):

//
// Probe3 zostanie wywołana przez emiter bezpośrednio poprzez
// metoda statyczna SetValueByPath().
//
Cz sonda3 = Utwórz obiekt ();
sonda3->SetName („Statycznie dostępna sonda”);
// Musimy dodać go do bazy danych konfiguracji
Nazwy::Add („/Nazwy/Sondy”, sonda3->GetName (), sonda3);

Na koniec przykład pokazuje, jak można podłączyć sondy w celu wygenerowania danych wyjściowych:

// Sama sonda powinna generować dane wyjściowe. Kontekst, który zapewniamy
// do tej sondy (w tym przypadku nazwa sondy) pomoże ujednoznacznić
// źródło śledzenia
podłączony = sonda3->TraceConnect („Wyjście”,
"/Nazwy/Sondy/Statycznie dostępna sonda/Wyjście",
Wykonaj oddzwonienie (&NotifyViaProbe));
NS_ASSERT_MSG (podłączony, „Źródło śledzenia nie jest podłączone do wyjścia sondy3”);

Poniższe wywołanie zwrotne jest w tym przykładzie podłączone do Probe w celach ilustracyjnych;
normalnie Sonda byłaby podłączona do obiektu Zbieracza.

// To jest funkcja testująca podłączenie go do wyjścia sondy
unieważnić
NotifyViaProbe (kontekst std::string, podwójna stara wartość, podwójna nowa wartość)
{
NS_LOG_DEBUG („kontekst: „ << kontekst << „stary” << stara wartość << „nowy” << nowa wartość);
}

IPv4 Paczka Wątek Przykład
Przykład wykresu pakietów IPv4 jest oparty na piątym przykładzie.cc z pliku ns-3 Instruktaż. To
można znaleźć src/stats/examples/ipv4-packet-plot-example.cc.

węzeł 0 węzeł 1
+-++ +----------------+
| ns-3 TCP | | ns-3 TCP |
+-++ +----------------+
| 10.1.1.1 | | 10.1.1.2 |
+-++ +----------------+
| punkt-punkt | | punkt-punkt |
+-++ +----------------+
| |
+----------------------------------+

Przyjrzymy się tylko sondzie, ponieważ pokazuje ona, że ​​sondy mogą również rozpakowywać wartości z
struktur (w tym przypadku pakietów) i zgłaszać te wartości raczej jako dane wyjściowe źródła śledzenia
niż tylko przesyłanie danych tego samego typu.

Istnieją inne aspekty tego przykładu, które zostaną wyjaśnione w dalszej części dokumentacji.
Eksportowane są dwa typy danych: sam pakiet (Wydajność) i liczba
liczba bajtów w pakiecie (Bajty wyjściowe).

TypId
Ipv4PacketProbe::GetTypeId ()
{
statyczny TypeId tid = TypeId („ns3::Ipv4PacketProbe”)
.Ustaw Rodzica ()
.Dodaj konstruktora ()
.AddTraceSource („Wyjście”,
„Pakiet plus jego obiekt IPv4 i interfejs, które służą jako dane wyjściowe dla tej sondy”,
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_output))
.AddTraceSource („Bajty wyjściowe”,
„Liczba bajtów w pakiecie”,
MakeTraceSourceAccessor (&Ipv4PacketProbe::m_outputBytes))
;
pora powrotu;
}

Kiedy moduł śledzenia sondy odbierze pakiet, a sonda jest włączona, zostanie wysłany
pakiet na nim Wydajność śledzenia, ale wyświetli również liczbę bajtów w pliku
Bajty wyjściowe źródło śledzenia.

unieważnić
Ipv4PacketProbe::TraceSink (cz pakiet, cz interfejs ipv4, uint4_t)
{
NS_LOG_FUNCTION (ten << pakiet << ipv4 << interfejs);
jeśli (jest włączone ())
{
m_pakiet = pakiet;
m_ipv4 = ipv4;
m_interfejs = interfejs;
m_output (pakiet, ipv4, interfejs);

uint32_t packageSizeNew = pakiet->GetSize ();
m_outputBytes (m_packetSizeStary, PackageSizeNew);
m_packetSizeOld = rozmiar pakietuNowy;
}
}

Referencje
[Cic06]
Claudio Cicconetti, Enzo Mingozzi, Giovanni Stea, „Zintegrowane ramy dla
Umożliwianie efektywnego gromadzenia danych i analizy statystycznej za pomocą ns2, warsztaty nt
ns-2 (WNS2), Piza, Włochy, październik 2006.

Kolekcjonerzy
Ta sekcja jest symbolem zastępczym zawierającym szczegółowe informacje na temat funkcjonalności udostępnianych przez moduł Kolekcjoner
klasa do ns-3 symulację i podaje przykłady kodowania ich w programie.

Uwaga: Od wersji ns-3.18 moduły Collectors są wciąż w fazie rozwoju i nie są jeszcze dostarczane jako część
ram.

Agregatory
W tej sekcji szczegółowo opisano funkcje udostępniane przez klasę Aggregator dla pliku an ns-3
symulacja. Ta sekcja jest przeznaczona dla użytkowników zainteresowanych tworzeniem symulacji za pomocą programu
ns-3 narzędzi i korzystania z Data Collection Framework, którego klasą jest Aggregator
część, w celu wygenerowania danych wyjściowych z wynikami symulacji.

Agregator Omówienie
W tym celu obiekt Aggregator powinien być podłączony do jednego lub większej liczby źródeł śledzenia
odbierać dane wejściowe. Agregatory są punktem końcowym danych gromadzonych przez sieć
Sondy i kolektory podczas symulacji. Zabranie ich jest zadaniem agregatora
wartości i przekształcić je w ostateczny format wyjściowy, taki jak zwykłe pliki tekstowe,
arkusze kalkulacyjne, wykresy lub bazy danych.

Zazwyczaj agregator jest podłączony do jednego lub większej liczby kolektorów. W ten sposób, kiedykolwiek
źródła śledzenia zbieraczy eksportują nowe wartości, agregator może przetworzyć wartość w ten sposób
że można go użyć w ostatecznym formacie wyjściowym, w którym wartości danych będą znajdować się po
symulacja.

Zwróć uwagę na następujące informacje dotyczące agregatorów:

· Agregatory mogą być dynamicznie włączane i wyłączane podczas symulacji za pomocą wywołań
Włączać() oraz Wyłączyć(). Na przykład agregowanie danych może zostać wyłączone podczas
faza rozgrzewki symulacji, co oznacza, że ​​wartości te nie zostaną uwzględnione w finale
medium wyjściowe.

· Agregatory otrzymują dane od Kolekcjonerów poprzez wywołania zwrotne. Kiedy powiązany jest Kolekcjoner
do agregatora, wykonywane jest wywołanie TraceConnect w celu ustalenia śledzenia agregatora
metoda ujścia jako wywołanie zwrotne.

Do chwili obecnej wdrożono dwa Agregatory:

· GnuplotAgregator

· Agregator plików

Agregator Gnuplot
GnuplotAggregator tworzy pliki wyjściowe używane do tworzenia gnuplotów.

Na koniec symulacji GnuplotAggregator utworzy 3 różne pliki:

· Oddzielony spacjami plik danych gnuplot

· Plik kontrolny gnuplot

· Skrypt powłoki do generowania gnuplot

Tworzenie
Zostanie tutaj utworzony obiekt typu GnuplotAggregator, aby pokazać, co należy zrobić.

Jeden deklaruje GnuplotAggregator w pamięci dynamicznej przy użyciu klasy inteligentnego wskaźnika
(Cz ). Aby utworzyć GnuplotAggregator w pamięci dynamicznej z inteligentnymi wskaźnikami, wystarczy
musi zadzwonić do ns-3 metoda UtwórzObiekt(). Poniższy kod z
src/stats/examples/gnuplot-aggregator-example.cc pokazuje jak to zrobić:

string fileNameWithoutExtension = "agregator-gnuplot";

// Utwórz agregator.
Cz agregator =
Utwórz obiekt (nazwa pliku bez rozszerzenia);

Pierwszym argumentem konstruktora fileNameWithoutExtension jest nazwa pliku
pliki powiązane z gnuplot do zapisu bez rozszerzenia. Ten GnuplotAggregator utworzy plik
plik danych gnuplot oddzielonych spacjami o nazwie „gnuplot-aggregator.dat”, plik kontrolny gnuplot
o nazwie „gnuplot-aggregator.plt” i skrypt powłoki do generowania gnuplot o nazwie +
„gnuplot-agregator.sh”.

Utworzony gnuplot może mieć swój klucz w 4 różnych lokalizacjach:

· Brak klucza

· Klucz wewnątrz działki (domyślnie)

· Klucz nad działką

· Klucz pod fabułą

Następujące wartości wyliczeniowe lokalizacji klucza gnuplot mogą określać położenie klucza:

enumLokalizacja klucza {
BRAK KLUCZA,
KLUCZ_WEWNĄTRZ,
KLUCZ_POWYŻEJ,
KLUCZ_PONIŻEJ
};

Jeśli pożądane byłoby mieć klucz poniżej, a nie domyślną pozycję wewnątrz, to
możesz wykonać następujące czynności.

agregator->SetKeyLocation(GnuplotAggregator::KEY_BELOW);

Przykłady
Jeden przykład zostanie szczegółowo omówiony tutaj:

· Przykład agregatora Gnuplot

gnuplot Agregator Przykład
Przykład ćwiczenia GnuplotAggregator można znaleźć w
src/stats/examples/gnuplot-aggregator-example.cc.

Na podstawie przykładu utworzono następujący dwuwymiarowy wykres gnuplot.
[image] Gnuplot 2-D Utworzony przez gnuplot-aggregator-example.cc Przykład..UNINDENT

Ten kod z przykładu pokazuje, jak skonstruować GnuplotAggregator, jak to omówiono
powyżej.

unieważnij Utwórz 2dPlot ()
{
using namespace std;

string fileNameWithoutExtension = "agregator-gnuplot";
string plotTitle = "Wykres agregatora Gnuplot";
string plotXAxisHeading = "Czas (sekundy)";
string plotYAxisHeading = "Podwójne wartości";
string plotDatasetLabel = "Wartości danych";
string datasetContext = "Zbiór danych/Kontekst/Ciąg";

// Utwórz agregator.
Cz agregator =
Utwórz obiekt (nazwa pliku bez rozszerzenia);

Ustawione są różne atrybuty GnuplotAggregator, w tym zestaw danych 2-D, który będzie
wykreślone.

// Ustaw właściwości agregatora.
agregator->SetTerminal („png”);
agregator->SetTitle (plotTitle);
agregator->SetLegend (plotXAxisHeading, plotYAxisHeading);

// Dodaj zestaw danych do agregatora.
agregator->Add2dDataset (datasetContext, plotDatasetLabel);

// agregator musi być włączony
agregator->Włącz ();

Następnie obliczane są wartości 2-D i każda z nich jest indywidualnie zapisywana w pliku
GnuplotAggregator korzystający z Zapis2d() funkcja.

podwójny czas;
podwójna wartość;

// Utwórz zestaw danych 2-D.
for (czas = -5.0; czas <= +5.0; czas += 1.0)
{
// Oblicz krzywą 2-D
//
/ / 2
// wartość = czas .
//
wartość = czas * czas;

// Dodaj ten punkt do wykresu.
agregator->Write2d (datasetContext, czas, wartość);
}

// Wyłącz rejestrowanie danych dla agregatora.
agregator->Wyłącz ();
}

Agregator plików
FileAggregator wysyła otrzymane wartości do pliku.

FileAggregator może tworzyć 4 różne typy plików:

· Sformatowany

· Oddzielone spacjami (domyślnie)

· Oddzielone przecinkami

· Zakładka oddzielona

Sformatowane pliki używają ciągów formatu w stylu C i funkcji sprintf() do drukowania
wartości w zapisywanym pliku.

Tworzenie
Zostanie tutaj utworzony obiekt typu FileAggregator, aby pokazać, co należy zrobić.

Jeden deklaruje FileAggregator w pamięci dynamicznej przy użyciu klasy inteligentnego wskaźnika (Ptr ).
Aby utworzyć FileAggregator w pamięci dynamicznej z inteligentnymi wskaźnikami, wystarczy wywołać
dotychczasowy ns-3 metoda UtwórzObiekt. Poniższy kod z
src/stats/examples/file-agregator-example.cc pokazuje jak to zrobić:

string nazwa_pliku = "wartości-sformatowane-agregator-plików.txt";

// Utwórz agregator, który będzie miał sformatowane wartości.
Cz agregator =
Utwórz obiekt (nazwa pliku, FileAggregator::FORMATTED);

Pierwszym argumentem konstruktora, nazwa_pliku, jest nazwa pliku do zapisania; the
drugi argument, fileType, to typ pliku do zapisania. Ten FileAggregator utworzy plik
plik o nazwie „file-aggregator-formatted-values.txt” z wydrukowanymi wartościami zgodnie z określeniem
fileType, czyli w tym przypadku sformatowany.

Dozwolone są następujące wartości wyliczeniowe typu pliku:

wyliczenie Typ pliku {
FORMATOWANY,
SPACE_SEPARATED,
ODDZIELONE PRZECINKAMI,
TAB_SEPARATED
};

Przykłady
Jeden przykład zostanie szczegółowo omówiony tutaj:

· Przykład agregatora plików

filet Agregator Przykład
Przykład ćwiczący FileAggregator można znaleźć w
src/stats/examples/file-agregator-example.cc.

Poniższy plik tekstowy zawierający 2 kolumny wartości oddzielonych przecinkami został utworzony przy użyciu metody
przykład.

-5,25
-4,16
-3,9
-2,4
-1,1
0,0
1,1
2,4
3,9
4,16
5,25

Ten kod z przykładu pokazuje, jak skonstruować FileAggregator, jak omówiono
powyżej.

void Utwórz plik oddzielony przecinkami ()
{
using namespace std;

string nazwa_pliku = "agregator plików-oddzielonych przecinkami.txt";
string datasetContext = "Zbiór danych/Kontekst/Ciąg";

// Utwórz agregator.
Cz agregator =
Utwórz obiekt (nazwa pliku, FileAggregator::COMMA_SEPARATED);

Atrybuty FileAggregator są ustawione.

// agregator musi być włączony
agregator->Włącz ();

Następnie obliczane są wartości 2-D i każda z nich jest indywidualnie zapisywana w pliku
FileAggregator za pomocą Zapis2d() funkcja.

podwójny czas;
podwójna wartość;

// Utwórz zestaw danych 2-D.
for (czas = -5.0; czas <= +5.0; czas += 1.0)
{
// Oblicz krzywą 2-D
//
/ / 2
// wartość = czas .
//
wartość = czas * czas;

// Dodaj ten punkt do wykresu.
agregator->Write2d (datasetContext, czas, wartość);
}

// Wyłącz rejestrowanie danych dla agregatora.
agregator->Wyłącz ();
}

Poniższy plik tekstowy z 2 kolumnami sformatowanych wartości został również utworzony przy użyciu metody
przykład.

Czas = -5.000e+00 Wartość = 25
Czas = -4.000e+00 Wartość = 16
Czas = -3.000e+00 Wartość = 9
Czas = -2.000e+00 Wartość = 4
Czas = -1.000e+00 Wartość = 1
Czas = 0.000e+00 Wartość = 0
Czas = 1.000e+00 Wartość = 1
Czas = 2.000e+00 Wartość = 4
Czas = 3.000e+00 Wartość = 9
Czas = 4.000e+00 Wartość = 16
Czas = 5.000e+00 Wartość = 25

Ten kod z przykładu pokazuje, jak skonstruować FileAggregator, jak omówiono
powyżej.

void UtwórzFormattedFile ()
{
using namespace std;

string nazwa_pliku = "wartości-sformatowane-agregator-plików.txt";
string datasetContext = "Zbiór danych/Kontekst/Ciąg";

// Utwórz agregator, który będzie miał sformatowane wartości.
Cz agregator =
Utwórz obiekt (nazwa pliku, FileAggregator::FORMATTED);

Ustawiono atrybuty FileAggregator, w tym ciąg formatu w stylu C, który ma być używany.

// Ustaw format wartości.
agregator->Set2dFormat („Czas = %.3e\tValue = %.0f”);

// agregator musi być włączony
agregator->Włącz ();

Następnie obliczane są wartości 2-D i każda z nich jest indywidualnie zapisywana w pliku
FileAggregator za pomocą Zapis2d() funkcja.

podwójny czas;
podwójna wartość;

// Utwórz zestaw danych 2-D.
for (czas = -5.0; czas <= +5.0; czas += 1.0)
{
// Oblicz krzywą 2-D
//
/ / 2
// wartość = czas .
//
wartość = czas * czas;

// Dodaj ten punkt do wykresu.
agregator->Write2d (datasetContext, czas, wartość);
}

// Wyłącz rejestrowanie danych dla agregatora.
agregator->Wyłącz ();
}

Adaptery
W tej sekcji szczegółowo opisano funkcje udostępniane przez klasę Adapter dla pliku ns-3
symulacja. Ta sekcja jest przeznaczona dla użytkowników zainteresowanych tworzeniem symulacji za pomocą programu
ns-3 narzędzi i korzystania z Data Collection Framework, którego częścią jest klasa Adapter,
do generowania danych wyjściowych z wynikami symulacji.

Uwaga: termin „adapter” można również zapisać jako „adapter”; wybraliśmy wyrównaną pisownię
ze standardem C++.

Adapter Omówienie
Adapter służy do tworzenia połączeń pomiędzy różnymi typami obiektów DCF.

Do chwili obecnej wdrożono jeden Adapter:

· Adapter TimeSeries

Czas Seria Adapter
TimeSeriesAdaptor umożliwia sondom łączenie się bezpośrednio z agregatorami bez ich potrzeby
Kolekcjoner pomiędzy.

Oba zaimplementowane pomocniki DCF wykorzystują TimeSeriesAdaptors w celu przeprowadzenia sondowania
wartości różnych typów i wypisuje bieżący czas plus wartość po przeliczeniu obu
do dwójek.

Rolą klasy TimeSeriesAdaptor jest rola adaptera, który przyjmuje wartość surową
sonduje dane różnych typów i generuje krotkę dwóch wartości typu double. Pierwsza to A
znacznik czasu, który może być ustawiony na różne rozdzielczości (np. sekundy, milisekundy itp.) w
przyszłość, ale obecnie zakodowana na stałe w sekundach. Drugim jest konwersja A
wartość inną niż podwójna na wartość podwójną (prawdopodobnie z utratą precyzji).

Zakres/ograniczenia
W tej sekcji omówiono zakres i ograniczenia Ram gromadzenia danych.

Obecnie w DCF zaimplementowano jedynie następujące sondy:

· BooleanProbe

· Podwójna sonda

· Uinteger8Probe

· Uinteger16Probe

· Uinteger32Probe

· Sonda Czasowa

· PacketProbe

· ApplicationPacketProbe

· Ipv4PacketProbe

Obecnie w DCF nie są dostępne żadne moduły zbierające, chociaż moduł BasicStatsCollector jest niepełny
opon wyścigowych.

Obecnie w DCF zaimplementowano tylko te Agregatory:

· GnuplotAgregator

· Agregator plików

Obecnie w DCF zaimplementowano tylko ten Adapter:

Adapter szeregów czasowych.

Przyszłość Maksymalna wysokość
W tej sekcji omówiono przyszłe prace, które należy wykonać w ramach ram gromadzenia danych.

Oto kilka rzeczy, które należy jeszcze zrobić:

· Podłącz więcej źródeł śledzenia ns-3 kod, aby uzyskać więcej wartości z symulatora.

· Wdrożenie większej liczby typów sond niż obecnie.

· Zaimplementuj więcej niż tylko pojedynczy, aktualny moduł zbierający 2-D, BasicStatsCollector.

· Wdrażaj więcej agregatorów.

· Implementuj więcej niż tylko adaptery.

Statystyczny
W tym rozdziale przedstawiono prace nad gromadzeniem danych symulacyjnych i ramami statystycznymi dla
ns-3.

Kod źródłowy ram statystycznych znajduje się w katalogu źródło/statystyki.

Gole
Podstawowe cele tego wysiłku są następujące:

· Zapewnij funkcjonalność rejestrowania, obliczania i prezentowania danych i statystyk do analizy
symulacji sieci.

· Zwiększ wydajność symulacji, zmniejszając potrzebę generowania obszernych logów śledzenia
celu gromadzenia danych.

· Włącz kontrolę symulacji poprzez statystyki online, np. zakończenie symulacji lub
powtarzanie prób.

Pochodne cele cząstkowe i inne cechy docelowe obejmują:

· Integracja z istniejącym systemem śledzenia ns-3 jako podstawowa struktura oprzyrządowania
wewnętrznego silnika symulacyjnego, np. stosów sieciowych, urządzeń sieciowych i kanałów.

· Umożliwienie użytkownikom korzystania ze struktur statystycznych bez konieczności stosowania śledzenia
pomimo napiętego harmonogramu

· Pomaganie użytkownikom w tworzeniu, agregowaniu i analizowaniu danych w wielu próbach.

· Obsługa oprzyrządowania utworzonego przez użytkownika, np. zdarzeń specyficznych dla aplikacji i
środki.

· Niskie obciążenie pamięci i procesora, gdy pakiet nie jest używany.

· Maksymalne wykorzystanie istniejących narzędzi analitycznych i wyjściowych. Ramy mogą
dostarczają podstawowych statystyk, ale skupiają się na zbieraniu danych i ich tworzeniu
dostępne do manipulacji w ustalonych narzędziach.

· Ewentualna obsługa dystrybucji niezależnych replik jest ważna, ale nie uwzględniona
w pierwszej rundzie funkcji.

Omówienie
Ramy statystyczne obejmują następujące funkcje:

· Podstawowa struktura i dwa podstawowe moduły zbierające dane: licznik i wartość min./maks./średnia/ogółem
obserwator.

· Rozszerzenia ułatwiające pracę z czasami i pakietami.

· Wynik w postaci zwykłego tekstu w formacie OMNet++.

· Wyjście z bazy danych za pomocą SQLite, samodzielny, lekki i wydajny silnik SQL.

· Obowiązkowe i otwarte metadane do opisywania przebiegów i pracy z nimi.

· Przykład oparty na teoretycznym eksperymencie badania właściwości NS-3
domyślna wydajność sieci Wi-Fi ad hoc. Zawiera następujące elementy:

· Konstruuje dwuwęzłową sieć WiFi ad hoc, w której węzły znajdują się w sparametryzowanej odległości
niezależnie.

· Aplikacje źródła i ujścia ruchu UDP o nieco innym zachowaniu i
haki pomiarowe niż klasy magazynowe.

· Gromadzenie danych z rdzenia NS-3 za pomocą istniejących sygnałów śledzenia, w szczególności danych nt
ramki przesyłane i odbierane przez obiekty WiFi MAC.

· Oprzyrządowanie niestandardowych aplikacji poprzez podłączenie nowych sygnałów śledzenia do statystyki
framework, a także poprzez bezpośrednie aktualizacje. Rejestrowana jest informacja o łącznej liczbie pakietów
wysłane i odebrane, przesłane bajty i całkowite opóźnienie.

· Przykład użycia znaczników pakietów do śledzenia opóźnień typu end-to-end.

· Prosty skrypt sterujący, który uruchamia szereg prób eksperymentu przy różnych parametrach
odległości i wysyła zapytanie do wynikowej bazy danych w celu utworzenia wykresu za pomocą GNUPlot.

Do zrobienia
Elementy o wysokim priorytecie obejmują:

· Włączenie kodu statystycznego online, np. dla przedziałów ufności efektywnych pod względem pamięci.

· Przepisy w modułach zbierających dane dotyczące kończenia przebiegów, tj. po przekroczeniu progu lub
zaufanie zostało spełnione.

· Moduły zbierające dane do rejestrowania próbek w czasie i wyprowadzania ich do różnych formatów.

· Zademonstruj pisanie prostego kleju do cyklicznych zdarzeń, aby regularnie odpytywać jakąś wartość.

Każde z nich powinno okazać się łatwe do włączenia do obecnych ram.

Podejście
Ramy opierają się na następujących podstawowych zasadach:

· Jedna próba eksperymentalna jest prowadzona przez jedną instancję programu symulacyjnego, czy to w
równolegle lub szeregowo.

· Skrypt sterujący wykonuje instancje symulacji, zmieniając w razie potrzeby parametry.

· Dane są gromadzone i przechowywane w celu wykreślenia i analizy przy użyciu zewnętrznych skryptów i
istniejących narzędzi.

· Działania w obrębie rdzenia ns-3 podejmowane są poprzez połączenie struktury statystycznej z istniejącą
sygnały śledzenia.

· Sygnały śledzenia lub bezpośrednia manipulacja strukturą mogą być wykorzystywane do oprzyrządowania niestandardowego
kod symulacji.

Te podstawowe elementy struktury i ich interakcje są przedstawione w
następujący rysunek. [obraz]

Przykład
W tej sekcji opisano proces konstruowania eksperymentu w ramach i
generując z niego dane do analizy (wykresy), demonstrując jednocześnie strukturę i API
droga.

Question
„Jaka jest (symulowana) wydajność urządzeń sieciowych WiFi NetDevices ns-3 (przy użyciu domyślnego
ustawienia)? Jak daleko od siebie mogą znajdować się węzły bezprzewodowe w symulacji, zanim nie będzie to możliwe
komunikować się niezawodnie?''

· Hipoteza: W oparciu o wiedzę o rzeczywistym działaniu, węzły powinny się komunikować
dość dobrze, w odległości co najmniej 100 m od siebie. Komunikacja na odległość większą niż 200 m nie powinna być
wykonalny.

Chociaż nie jest to zbyt częste pytanie w kontekście symulacji, jest to ważna właściwość
które twórcy symulacji powinni posiadać podstawową wiedzę. Jest to również powszechne
badanie przeprowadzone na działającym sprzęcie.

Symulacja Program
Pierwszą rzeczą do zrobienia podczas wdrażania tego eksperymentu jest opracowanie symulacji
program. Kod tego przykładu można znaleźć w przykłady/stats/wifi-example-sim.cc.
Wykonuje następujące główne kroki.

· Deklarowanie parametrów i analizowanie linii poleceń za pomocą ns3::Wiersz poleceń.

podwójna odległość = 50.0;
format ciągu („OMNet++”);
eksperyment ciągowy („test odległości Wi-Fi”);
strategia ciągów („domyślne Wi-Fi”);
ciąg identyfikatora uruchomienia;

cmd wiersza poleceń;
cmd.AddValue("odległość", "Odległość od umieszczenia węzłów (w metrach).", odległość);
cmd.AddValue("format", "Format używany do wyprowadzania danych.", format);
cmd.AddValue("eksperyment", "Identyfikator eksperymentu.", eksperyment);
cmd.AddValue("strategia", "Identyfikator strategii.", strategia);
cmd.AddValue("run", "Identyfikator uruchomienia.", runID);
cmd.Parse (argc, argv);

· Tworzenie węzłów i stosów sieciowych przy użyciu ns3::Kontener węzłów, ns3::WiFiHelper,
ns3::InternetStackHelper.

węzły NodeContainer;
węzły.Utwórz(2);

WifiHelper Wi-Fi;
wifi.SetMac("ns3::AdhocWifiMac");
wifi.SetPhy("ns3::WifiPhy");
NetDeviceContainer węzełUrządzenia = wifi.Install(węzły);

InternetStackHelper Internet;
internet.Install(węzły);
Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase("192.168.0.0", "255.255.255.0");
ipAddrs.Assign(urządzenia węzła);

· Pozycjonowanie węzłów za pomocą ns3::Helper mobilności. Domyślnie węzły są statyczne
mobilność i nie porusza się, ale musi znajdować się w odpowiedniej odległości od siebie. Tam są
kilka sposobów, aby to zrobić; robi się to tutaj za pomocą ns3::ListPositionAllocator, który rysuje
pozycje z danej listy.

Mobilność Mobilność pomocnika;
Cz pozycjaAlloc =
Utwórz obiekt ();
positionAlloc->Add(Vector(0.0, 0.0, 0.0));
positionAlloc->Add(Vector(0.0, odległość, 0.0));
mobilność.SetPositionAllocator(positionAlloc);
mobilność.Install(węzły);

· Instalacja generatora ruchu i pochłaniacza ruchu. Towar Zastosowania może być
używane, ale przykład zawiera obiekty niestandardowe w src/test/test02-apps.(cc|h), Te
mają proste zachowanie, generując określoną liczbę pakietów rozmieszczonych w określonych odstępach czasu.
Ponieważ jest tylko jeden egzemplarz, są one instalowane ręcznie; w przypadku większego zestawu
ns3::ApplicationHelper można użyć klasy. Skomentowany Konfiguracja::Ustaw zmiany linii
miejsce docelowe pakietów, w tym przykładzie domyślnie ustawione na rozgłaszanie. Zauważ to
ogólnie rzecz biorąc, Wi-Fi może mieć różną wydajność w przypadku ramek rozgłoszeniowych i pojedynczych z powodu
różne zasady kontroli szybkości transmisji i retransmisji adresów MAC.

Cz appSource = Lista węzłów::PobierzNode(0);
Cz nadawca = UtwórzObiekt ();
appSource->AddApplication(nadawca);
nadawca->Start(Sekund (1);

Cz appSink = Lista węzłów::PobierzNode(1);
Cz odbiorca = UtwórzObiekt ();
appSink->AddApplication(odbiornik);
odbiornik->Start(Sekund (0);

// Config::Set("/NodeList/*/ApplicationList/*/$Sender/Destination",
//Ipv4AddressValue("192.168.0.2"));

· Konfigurowanie danych i statystyk, które mają być gromadzone. Podstawowy paradygmat jest taki, że
ns3::Kolektor danych obiekt jest tworzony w celu przechowywania informacji o tym konkretnym przebiegu, do
do których przyłączeni są obserwatorzy i kalkulatory, aby faktycznie generować dane. Co ważne,
Informacje o przebiegu obejmują etykiety „eksperymentu”, „strategii”, „wejść” i
''uruchomić''. Służą one do późniejszej identyfikacji i łatwego grupowania danych z wielu badań.

· Eksperyment jest badaniem, którego częścią jest to badanie. Tutaj jest na WiFi
wydajność i dystans.

· Strategia to kod lub parametry sprawdzane w tej próbie. W tym przykładzie
zostało to naprawione, ale oczywistym rozszerzeniem byłoby sprawdzenie innego bitu Wi-Fi
stóp procentowych, z których każdy byłby inną strategią.

· Dane wejściowe stanowią konkretny problem rozpatrywany w tej próbie. Tutaj jest to po prostu
odległość między dwoma węzłami.

· RunID to unikalny identyfikator tej próby, za pomocą którego oznaczone są zawarte w niej informacje
do identyfikacji w późniejszej analizie. Jeśli nie podano identyfikatora uruchomienia, tworzy go przykładowy program
(słaby) identyfikator biegu wykorzystujący aktualny czas.

Wymagane są te cztery metadane, ale może być potrzebnych więcej. Mogą zostać dodane
do rekordu za pomocą ns3::DataCollector::AddMetadata() Metoda.

dane DataCollector;
dane.OpiszRun(eksperyment, strategia, dane wejściowe, identyfikator uruchomienia);
dane.AddMetadata("autor", "tjkopena");

Rzeczywistych obserwacji i obliczeń dokonuje ns3::Kalkulator danych obiekty, w tym
istnieje kilka różnych typów. Są one tworzone przez program symulacyjny dołączony do
kod raportowania lub próbkowania, a następnie zarejestrowany w ns3::Kolektor danych więc to zrobią
zostać później zapytany o ich wyniki. Jednym z łatwych mechanizmów obserwacji jest wykorzystanie istniejących
źródła śledzenia, na przykład w celu oprzyrządowania obiektów w rdzeniu ns-3 bez zmiany ich
kod. Tutaj licznik jest podłączony bezpośrednio do sygnału śledzenia w warstwie MAC WiFi
węzeł docelowy.

Cz totalRx = Utwórz obiekt ();
totalRx->SetKey("ramki-wifi-rx");
Config::Connect("/NodeList/1/DeviceList/*/$ns3::WifiNetDevice/Rx",
MakeCallback(&PacketCounterCalculator::FrameUpdate, totalRx));
dane.AddDataCalculator(totalRx);

Kalkulatorami można także manipulować bezpośrednio. W tym przykładzie tworzony jest licznik i
przekazywany do aplikacji pochłaniającej ruch w celu aktualizacji po odebraniu pakietów.

Cz > appRx = UtwórzObiekt >();
appRx->SetKey("pakiety-odbiornika-odbiornika");
odbiornik->SetCounter(appRx);
dane.AddDataCalculator(appRx);

Aby zwiększyć liczbę, kod przetwarzania pakietu ujścia wywołuje jedną z metod
metody aktualizacji kalkulatora.

m_calc->Aktualizuj();

Program zawiera także kilka innych przykładów, wykorzystujących oba elementy proste
kalkulatory np ns3::Kalkulator licznika oraz te przystosowane do obserwacji pakietów i
czasy. W src/test/test02-apps.(cc|h) tworzy również prosty niestandardowy tag, którego używa
do śledzenia opóźnień od końca do końca dla wygenerowanych pakietów i raportowania wyników do a
ns3::TimeMinMaxAvgTotalKalkulator kalkulator danych.

· Uruchamianie symulacji, która po skonstruowaniu jest bardzo prosta.

Symulator::Uruchom();

· Generowanie albo OMNet++ or SQLite dane wyjściowe, w zależności od argumentów wiersza poleceń. Do
zrób to ns3::DataOutputInterfejs obiekt jest tworzony i konfigurowany. Konkretny typ
z tego określi format wyjściowy. Obiekt ten otrzymuje następnie
ns3::Kolektor danych obiekt, który odpytuje w celu wygenerowania wyniku.

Cz wyjście;
if (format == "OMNet++") {
NS_LOG_INFO("Tworzenie danych wyjściowych w formacie OMNet++.");
wyjście = UtwórzObiekt ();
} Else {
# ifdef STAT_USE_DB
NS_LOG_INFO("Tworzenie danych wyjściowych w formacie SQLite.");
wyjście = UtwórzObiekt ();
# koniec
}

wyjście->Wyjście(dane);

· Zwolnienie pamięci używanej przez symulację. To powinno pojawić się na końcu głównego
funkcja dla przykładu.

Symulator::Zniszcz();

Logowanie
Aby zobaczyć szczegółowo, co robią przykładowy program, aplikacje i struktura statystyk, ustaw
dotychczasowy NS_LOG odpowiednio zmienna. Poniżej znajdziesz obszerne wyniki wszystkich
trzy.

$ eksport NS_LOG=WiFiDistanceExperiment:WiFiDistanceApps

Należy pamiętać, że to niezwykle spowalnia symulację.

Próba Wydajność
Dołączona zostanie kompilacja i proste uruchomienie programu testowego OMNet++ sformatowane dane wyjściowe, takie jak
następujące do dane.sca.

uruchom run-1212239121

eksperyment attr „test odległości Wi-Fi”
strategia attr „domyślne Wi-Fi”
wejście atrybutu „50”
opis atrybutu „”

attr "autor" "tjkopena"

skalarna liczba ramek Wi-Fi-tx 30
skalarna liczba ramek Wi-Fi-Rx 30
skalarna liczba pakietów nadawcy-tx 30
skalarna liczba pakietów odbiornika-rx 30
skalarna liczba rozmiarów tx-pkt 30
skalarny rozmiar tx-pkt łącznie 1920
skalarny średni rozmiar tx-pkt 64
skalarny rozmiar tx-pkt maks. 64
skalarny rozmiar tx-pkt min 64
Skalarna liczba opóźnień 30
Łączne opóźnienie skalarne 5884980ns
Średnie opóźnienie skalarne 196166 ns
Opóźnienie skalarne maks. 196166 ns
Opóźnienie skalarne min 196166ns

Control: Scenariusz
Aby zautomatyzować zbieranie danych na różnych wejściach (odległościach), prosty Bash
skrypt służy do wykonania serii symulacji. Można go znaleźć pod adresem
przykłady/stats/wifi-example-db.sh. Skrypt ma być uruchamiany z poziomu pliku przykłady/statystyki/
katalogiem.

Skrypt przebiega przez zestaw odległości, zbierając wyniki w pliku SQLite
Baza danych. Na każdym dystansie przeprowadza się pięć prób, aby uzyskać lepszy obraz oczekiwanych wyników
wydajność. Cały eksperyment trwa tylko kilkadziesiąt sekund i przebiega na niskim poziomie
maszynę, ponieważ podczas symulacji nie ma danych wyjściowych i generowany jest niewielki ruch.

#!/ Bin / sh

ODLEGŁOŚCI="25 50 75 100 125 145 147 150 152 155 157 160 162 165 167 170 172 175 177 180"
PRÓBY="1 2 3 4 5"

echo Przykład eksperymentu WiFi

jeśli [ -e dane.db ]
następnie
echo Zabić dane.db?
czytaj ANS
if [ "$ANS" = "tak" -o "$ANS" = "y" ]
następnie
echo Usuwanie bazy danych
rm dane.db
fi
fi

na próbę w $TRIALS
do
dla odległości w $DISTANCES
do
echo Próba $próba, odległość $dystans
./bin/test02 --format=db --distance=$odległość --run=run-$odległość-$próba
zrobić
zrobić

Analiza oraz Podsumowanie
Po przeprowadzeniu wszystkich prób skrypt wykonuje proste zapytanie SQL
baza danych za pomocą SQLite program wiersza poleceń. Zapytanie oblicza średnią utratę pakietów w
każdy zestaw prób powiązany z każdym dystansem. Nie bierze się pod uwagę odmienności
strategii, ale informacje są obecne w bazie danych w celu wprowadzenia kilku prostych rozszerzeń
i zrób to. Zebrane dane są następnie przesyłane do GNUPlot w celu wykreślenia.

CMD="wybierz exp.input,avg(100-((rx.value*100)/tx.value)) \
z Singletons rx, Singletons tx, Eksperymenty exp \
gdzie rx.run = tx.run ORAZ \
rx.uruchom = wyr.uruchom AND \
rx.name='odbiornik-pakietów-rx' ORAZ \
tx.name='pakiety-tx-nadawcy' \
grupuj według wejścia eksp. \
sortuj według abs(exp.input) ASC;"

sqlite3 -noheader data.db "$CMD" > wifi-default.data
sed -i "s/|/ /" wifi-default.data
gnuplot wifi-example.gnuplot

Skrypt GNUPlot znaleziony pod adresem przykłady/stats/wifi-example.gnuplot po prostu definiuje wynik
format i podstawowe formatowanie wykresu.

ustaw terminal postscriptowy ulepszony portret lw 2 „Helvetica” 14

ustaw rozmiar 1.0, 0.66

#---------------------------------------------------------------- ------
ustaw „wifi-default.eps”
#set title „Utrata pakietów na odległość”
set xlabel "Odległość (m) --- średnia z 5 prób na punkt"
ustaw zakres x [0:200]
ustaw etykietę „% utraty pakietów”
ustaw zakres [0:110]

wydrukuj „wifi-default.data” z tytułem wiersza „Domyślne ustawienia Wi-Fi”

Koniec Wynik
Wynikowy wykres nie dostarcza dowodów na to, że wydajność domyślnego modelu Wi-Fi jest taka
z konieczności nierozsądne i dodaje pewności przynajmniej symbolicznej wierności
rzeczywistość. Co ważniejsze, to proste dochodzenie zostało przeprowadzone przez cały czas
przy użyciu ram statystycznych. Powodzenie! [obraz]

Czas rzeczywisty
ns-3 został zaprojektowany do integracji ze środowiskami testowymi i maszynami wirtualnymi. Do
integruj się z rzeczywistymi stosami sieciowymi i emituj/zużywaj pakiety, jest to harmonogram działający w czasie rzeczywistym
potrzebne, aby spróbować zablokować zegar symulacyjny za pomocą zegara sprzętowego. Opisujemy tutaj a
jego składnik: harmonogram RealTime.

Celem harmonogramu czasu rzeczywistego jest spowodowanie postępu zegara symulacyjnego
występować synchronicznie względem jakiejś zewnętrznej podstawy czasu. Bez obecności
zewnętrzną podstawę czasu (zegar ścienny), czas symulacji przeskakuje natychmiastowo z czasu symulowanego
czas do następnego.

Zachowanie
Podczas korzystania z harmonogramu innego niż czas rzeczywisty (domyślny in ns-3), symulator rozwija
czas symulacji do następnego zaplanowanego wydarzenia. Podczas realizacji zdarzenia czas symulacji wynosi
mrożony. W przypadku harmonogramu czasu rzeczywistego zachowanie jest podobne z perspektywy
modele symulacyjne (tj. czas symulacji jest zamrażany podczas wykonywania zdarzenia), ale pomiędzy
zdarzeń, symulator będzie próbował ustawić zegar symulacyjny zgodnie z maszyną
zegar.

Gdy wykonywanie zdarzenia zostanie zakończone, a program planujący przejdzie do następnego zdarzenia, plik
harmonogram porównuje czas wykonania następnego zdarzenia z zegarem maszyny. Jeśli następny
wydarzenie jest zaplanowane na przyszłą godzinę, symulator pozostaje w stanie uśpienia do momentu osiągnięcia tego czasu rzeczywistego
a następnie wykonuje następne zdarzenie.

Może się zdarzyć, że w związku z przetwarzaniem związanym z realizacją zdarzeń symulacyjnych,
że symulator nie nadąża za czasem rzeczywistym. W takim przypadku decyzja należy do użytkownika
konfiguracja co robić. Istnieją dwa ns-3 atrybuty rządzące zachowaniem. The
pierwszy to ns3::RealTimeSimulatorImpl::Tryb synchronizacji. Możliwe dwa wpisy
ten atrybut to Najlepszy wysiłek (domyślnie) lub HardLimit. W trybie „BestEffort” plik
symulator będzie po prostu próbował dogonić czas rzeczywisty, wykonując zdarzenia, aż osiągnie wartość a
punkt, w którym w przyszłości (w czasie rzeczywistym) nastąpi następne wydarzenie, w przeciwnym razie symulacja się zakończy. W
W trybie BestEffort symulacja może zatem zająć więcej czasu niż symulacja
czas zegara ściennego. Druga opcja „HardLimit” spowoduje przerwanie symulacji, jeśli
przekroczony został próg tolerancji. Ten atrybut jest ns3::RealTimeSimulatorImpl::HardLimit
a wartość domyślna to 0.1 sekundy.

Innym trybem pracy jest ten, w którym symulowany jest czas nie zamrożone podczas wydarzenia
wykonanie. Ten tryb symulacji w czasie rzeczywistym został zaimplementowany, ale usunięty z wersji ns-3 drzewo
z powodu pytań, czy byłoby to przydatne. Jeśli użytkownicy są zainteresowani czasem rzeczywistym
symulator, dla którego czas symulacji nie ulega zamrożeniu podczas realizacji zdarzenia (tj. co
Dzwonić do Symulator::Teraz() zwraca bieżący czas zegara ściennego, a nie godzinę, o której
rozpoczęło się wykonywanie zdarzenia), skontaktuj się z listą mailingową ns-developers.

Stosowanie
Korzystanie z symulatora czasu rzeczywistego jest proste z punktu widzenia skryptu.
Użytkownicy muszą jedynie ustawić atrybut Typ implementacji symulatora do czasu rzeczywistego
symulator, taki jak:

GlobalValue::Bind („Typ implementacji symulatora”,
StringValue („ns3::RealtimeSimulatorImpl”));

Jest tam skrypt przykłady/realtime/realtime-udp-echo.cc tam jest przykład jak to zrobić
skonfiguruj zachowanie w czasie rzeczywistym. Próbować:

$ ./waf --uruchom-udp-echo w czasie rzeczywistym

To, czy symulator będzie działał z należytym zaangażowaniem, czy też zgodnie z zasadami twardych limitów, jest regulowane
poprzez atrybuty wyjaśnione w poprzedniej sekcji.

Wdrożenie
Implementacja zawarta jest w następujących plikach:

· src/core/model/symulator-czasu rzeczywistego-impl.{cc,h}

· src/core/model/synchronizator zegara ściennego.{cc,h}

Aby utworzyć harmonogram w czasie rzeczywistym, po prostu chcesz spowodować pierwsze przybliżenie
czas symulacji skacze, pochłaniając czas rzeczywisty. Proponujemy zrobić to za pomocą kombinacji
śpi i zajęty czeka. Oczekiwanie na uśpienie powoduje, że proces wywołujący (wątek) zwraca wartość
procesor na jakiś czas. Chociaż ten określony czas może minąć
do rozdzielczości nanosekundowej, jest ona w rzeczywistości konwertowana do szczegółowości specyficznej dla systemu operacyjnego. W
Linux, szczegółowość nazywa się Jiffy. Zwykle ta rozdzielczość jest niewystarczająca
nasze potrzeby (rzędu dziesięciu milisekund), więc zaokrąglamy w dół i chwilę śpimy
mniejsza liczba Jiffies. Proces jest następnie budzony po określonej liczbie
Jiffies minął. W tej chwili mamy jeszcze trochę czasu na czekanie. Tym razem jest
generalnie krótszy od minimalnego czasu snu, więc jesteśmy zajęci-czekamy na resztę
czas. Oznacza to, że wątek po prostu znajduje się w pętli for, zużywając cykle, aż do momentu
nadejdzie żądany czas. Po połączeniu oczekiwania na stan uśpienia i zajętości, upływ czasu rzeczywistego
Zegar (ścienny) powinien zgadzać się z czasem symulacji następnego zdarzenia i symulacją
dochód.

Pomocnicy
Powyższe rozdziały zapoznały Cię z różnymi ns-3 koncepcje programistyczne, takie jak smart
wskaźniki do zarządzania pamięcią zliczaną przez referencje, atrybuty, przestrzenie nazw, wywołania zwrotne itp.
Użytkownicy pracujący w tym interfejsie API niskiego poziomu mogą się łączyć ns-3 obiekty o drobnej ziarnistości.
Jednak program symulacyjny napisany w całości przy użyciu niskopoziomowego API byłby dość długi
i żmudne w kodowaniu. Z tego powodu nałożony został odrębny, tzw. „pomocniczy API”.
na rdzeniu ns-3 API. Jeśli przeczytałeś ns-3 samouczek, będziesz już zaznajomiony
z pomocniczym API, ponieważ to z nim nowi użytkownicy zazwyczaj zapoznają się jako pierwsi.
W tym rozdziale przedstawiamy filozofię projektowania pomocniczego API i porównujemy ją z nią
API niskiego poziomu. Jeśli staniesz się intensywnym użytkownikiem ns-3, prawdopodobnie będziesz się poruszać tam i z powrotem
między tymi interfejsami API nawet w tym samym programie.

Pomocniczy interfejs API ma kilka celów:

1. reszta src / nie ma zależności od pomocniczego API; wszystko, z czym da się zrobić
pomocniczy interfejs API może być kodowany również w interfejsie API niskiego poziomu

2. Pojemniki: Często symulacje będą wymagały wykonania szeregu identycznych działań w grupach
obiektów. Pomocniczy interfejs API w dużym stopniu wykorzystuje kontenery podobnych obiektów, do których
można wykonać podobne lub identyczne operacje.

3. API pomocnicze nie jest ogólne; nie ma na celu maksymalizacji ponownego wykorzystania kodu. Więc,
konstrukcje programistyczne, takie jak polimorfizm i szablony, które pozwalają na ponowne wykorzystanie kodu
nie tak powszechne. Na przykład istnieją osobne pomocniki CsmaNetDevice i
Pomocników PointToPointNetDevice, ale nie pochodzą one ze wspólnej bazy NetDevice
class.

4. Pomocniczy interfejs API zazwyczaj działa z obiektami przydzielonymi do stosu (a nie do obiektów przydzielonych do sterty). Dla
niektóre programy, ns-3 użytkownicy mogą nie martwić się o tworzenie obiektów na niskim poziomie lub
obsługa Ptr; mogą zadowolić się kontenerami obiektów i pomocnikami przydzielonymi do stosu
które na nich działają.

W pomocniczym interfejsie API tak naprawdę chodzi o tworzenie ns-3 programy, które są łatwiejsze do pisania i czytania, bez nich
odbierając moc interfejsu niskiego poziomu. Pozostała część tego rozdziału zawiera niektóre z nich
przykłady konwencji programowania pomocniczego API.

Dokonywanie Działki za pomocą dotychczasowy gnuplot Klasa
Istnieją 2 popularne metody tworzenia wykresów ns-3 i gnuplot (-
http://www.gnuplot.info):

1. Utwórz plik kontrolny gnuplot za pomocą ns-3klasa Gnuplot.

2. Utwórz plik danych gnuplot, korzystając z wartości wygenerowanych przez ns-3.

Ta sekcja dotyczy metody 1, tj. tego, jak stworzyć wykres za pomocą ns-3Gnuplota
klasa. Jeśli interesuje Cię metoda 2, zobacz podsekcję „Prawdziwy przykład” w sekcji
Sekcja „Śledzenie” w pliku ns-3 Poradnik.

Tworzenie Działki Korzystanie z dotychczasowy gnuplot Klasa
Aby utworzyć wykres za pomocą ns-3klasa Gnuplot:

1. Zmodyfikuj swój kod tak, aby korzystał z klasy Gnuplot i jej funkcji.

2. Uruchom swój kod, aby utworzyć plik kontrolny gnuplot.

3. Wywołaj gnuplot z nazwą pliku sterującego gnuplot.

4. Obejrzyj utworzony plik graficzny w swojej ulubionej przeglądarce graficznej.

Zobacz kod z przykładowych wykresów omówionych poniżej, aby uzyskać szczegółowe informacje na temat kroku 1.

An Przykład Program że Używa dotychczasowy gnuplot Klasa
Przykładowy program, który wykorzystuje ns-3klasę Gnuplot można znaleźć tutaj:

src/stats/examples/gnuplot-example.cc

Aby uruchomić ten przykład, wykonaj następujące czynności:

$ ./powłoka waf
$ cd kompilacja/debugowanie/źródło/statystyki/przykłady
$ ./gnuplot-example

Powinno to spowodować utworzenie następujących plików kontrolnych gnuplot w katalogu, w którym znajduje się plik example
jest usytuowany:

działka-2d.plt
plot-2d-with-error-bars.plt
działka-3d.plt

Aby przetworzyć te pliki kontrolne gnuplot, wykonaj następujące czynności:

$ gnuplot plot-2d.plt
$ gnuplot plot-2d-with-error-bars.plt
$ gnuplot plot-3d.plt

Powinno to spowodować utworzenie następujących plików graficznych w katalogu, w którym znajduje się przykład
usytuowany:

plot-2d.png
działka-2d-z-paskami-błędów.png
plot-3d.png

Możesz przeglądać te pliki graficzne w swojej ulubionej przeglądarce grafiki. Jeśli masz gimpa
zainstalowany na twoim komputerze, możesz na przykład to zrobić:

$ gimp plot-2d.png
$ gimp plot-2d-with-error-bars.png
$ gimp plot-3d.png

An Przykład 2-wymiarowy Wątek
Poniższy wykres 2-wymiarowy
[Obrazek]

został utworzony przy użyciu następującego kodu z gnuplot-example.cc:

using namespace std;

string fileNameWithNoExtension = "działka-2d";
stringgraphicFileName = nazwaplikuWithNoExtension + ".png";
string nazwaFilePliku =NazwaPlikuWithNoExtension + ".plt";
string plotTitle = "Wykres 2-D";
string dataTitle = "Dane 2-D";

// Utwórz instancję fabuły i ustaw jej tytuł.
Wykres Gnuplot (graphicsFileName);
fabuła.SetTitle (plotTitle);

// Utwórz plik graficzny, który zostanie utworzony przez plik fabuły
// jest używany z Gnuplot, może być plikiem PNG.
fabuła.SetTerminal („png”);

// Ustaw etykiety dla każdej osi.
plot.SetLegend („Wartości X”, „Wartości Y”);

// Ustaw zakres osi x.
plot.AppendExtra („ustaw xzakres [-6:+6]”);

// Utwórz instancję zbioru danych, ustaw jego tytuł i ustaw punkty
// wykreślane wraz z liniami łączącymi.
Gnuplot2dZbiór danych zestawu danych;
zbiór danych.SetTitle (dataTitle);
zestaw danych.SetStyle (Gnuplot2dDataset::LINES_POINTS);

podwójne x;
podwójne y;

// Utwórz zestaw danych 2-D.
dla (x = -5.0; x <= +5.0; x += 1.0)
{
// Oblicz krzywą 2-D
//
/ / 2
// y = x .
//
y = x * x;

// Dodaj ten punkt.
zbiór danych.Dodaj (x, y);
}

// Dodaj zbiór danych do wykresu.
plot.AddDataset (zestaw danych);

// Otwórz plik fabuły.
ofstream plotFile (plotFileName.c_str());

// Zapisz plik fabuły.
plot.GenerateOutput (plotFile);

// Zamknij plik fabuły.
plik plot.close ();

An Przykład 2-wymiarowy Wątek w Błąd Bary
Poniższy wykres dwuwymiarowy ze słupkami błędów w kierunkach x i y
[Obrazek]

został utworzony przy użyciu następującego kodu z gnuplot-example.cc:

using namespace std;

string fileNameWithNoExtension = "wykres-2d-z-paskami-błędów";
stringgraphicFileName = nazwaplikuWithNoExtension + ".png";
string nazwaFilePliku =NazwaPlikuWithNoExtension + ".plt";
string plotTitle = "Wykres 2-D ze słupkami błędów";
string dataTitle = "Dane 2-D ze słupkami błędów";

// Utwórz instancję fabuły i ustaw jej tytuł.
Wykres Gnuplot (graphicsFileName);
fabuła.SetTitle (plotTitle);

// Utwórz plik graficzny, który zostanie utworzony przez plik fabuły
// jest używany z Gnuplot, może być plikiem PNG.
fabuła.SetTerminal („png”);

// Ustaw etykiety dla każdej osi.
plot.SetLegend („Wartości X”, „Wartości Y”);

// Ustaw zakres osi x.
plot.AppendExtra („ustaw xzakres [-6:+6]”);

// Utwórz instancję zbioru danych, ustaw jego tytuł i ustaw punkty
// wykreślone bez linii łączących.
Gnuplot2dZbiór danych zestawu danych;
zbiór danych.SetTitle (dataTitle);
zestaw danych.SetStyle (Gnuplot2dDataset::POINTS);

// Spraw, aby zbiór danych zawierał słupki błędów zarówno w kierunku x, jak i y.
zestaw danych.SetErrorBars (Gnuplot2dDataset::XY);

podwójne x;
podwójny xErrorDelta;
podwójne y;
double yErrorDelta;

// Utwórz zestaw danych 2-D.
dla (x = -5.0; x <= +5.0; x += 1.0)
{
// Oblicz krzywą 2-D
//
/ / 2
// y = x .
//
y = x * x;

// Spraw, aby niepewność w kierunku x była stała i wykonaj
// niepewność w kierunku y będzie stałym ułamkiem
// wartość y.
xErrorDelta = 0.25;
yErrorDelta = 0.1 * y;

// Dodaj ten punkt z niepewnościami zarówno w x, jak i y
// kierunek.
dataset.Add (x, y, xBłądDelta, yBłądDelta);
}

// Dodaj zbiór danych do wykresu.
plot.AddDataset (zestaw danych);

// Otwórz plik fabuły.
ofstream plotFile (plotFileName.c_str());

// Zapisz plik fabuły.
plot.GenerateOutput (plotFile);

// Zamknij plik fabuły.
plik plot.close ();

An Przykład 3-wymiarowy Wątek
Poniższy wykres 3-wymiarowy
[Obrazek]

został utworzony przy użyciu następującego kodu z gnuplot-example.cc:

using namespace std;

string fileNameWithNoExtension = "działka-3d";
stringgraphicFileName = nazwaplikuWithNoExtension + ".png";
string nazwaFilePliku =NazwaPlikuWithNoExtension + ".plt";
string plotTitle = "Wykres 3-D";
string dataTitle = "Dane 3-D";

// Utwórz instancję fabuły i ustaw jej tytuł.
Wykres Gnuplot (graphicsFileName);
fabuła.SetTitle (plotTitle);

// Utwórz plik graficzny, który zostanie utworzony przez plik fabuły
// jest używany z Gnuplot, może być plikiem PNG.
fabuła.SetTerminal („png”);

// Obróć wykres o 30 stopni wokół osi x, a następnie obróć
// wykreśl 120 stopni wokół nowej osi z.
plot.AppendExtra („ustaw widok 30, 120, 1.0, 1.0”);

// Ustaw zero osi z na płaszczyźnie osi x i y.
plot.AppendExtra („ustaw poziom tików na 0”);

// Ustaw etykiety dla każdej osi.
plot.AppendExtra („ustaw xlabel 'Wartości X'");
plot.AppendExtra („ustaw etykietę 'Wartości Y'");
plot.AppendExtra („ustaw etykietę 'Wartości Z'");

// Ustaw zakresy dla osi x i y.
plot.AppendExtra („ustaw xzakres [-5:+5]”);
plot.AppendExtra („ustaw zakres [-5:+5]”);

// Utwórz instancję zbioru danych, ustaw jego tytuł i ustaw punkty
// połączone liniami.
Gnuplot3dZbiór danych zestawu danych;
zbiór danych.SetTitle (dataTitle);
dataset.SetStyle („z liniami”);

podwójne x;
podwójne y;
podwójne z;

// Utwórz zestaw danych 3-D.
dla (x = -5.0; x <= +5.0; x += 1.0)
{
dla (y = -5.0; y <= +5.0; y += 1.0)
{
// Oblicz powierzchnię 3D
//
// 2 2
// z = x * y .
//
z = x * x * y * y;

// Dodaj ten punkt.
zbiór danych.Dodaj (x, y, z);
}

// Pusta linia jest konieczna na końcu danych każdej wartości x
// punkty umożliwiające działanie trójwymiarowej siatki powierzchniowej.
zbiór danych.AddEmptyLine ();
}

// Dodaj zbiór danych do wykresu.
plot.AddDataset (zestaw danych);

// Otwórz plik fabuły.
ofstream plotFile (plotFileName.c_str());

// Zapisz plik fabuły.
plot.GenerateOutput (plotFile);

// Zamknij plik fabuły.
plik plot.close ();

Korzystanie z Python do Uruchom ns-3
Powiązania Pythona pozwalają na kod C++ ns-3 do wywołania z Pythona.

W tym rozdziale pokazano, jak utworzyć skrypt w języku Python, który można uruchomić ns-3 oraz
proces tworzenia powiązań Pythona dla C++ ns-3 moduł.

Wprowadzenie
Celem powiązań Pythona dla ns-3 są dwojakie:

1. Pozwól programiście pisać kompletne skrypty symulacyjne w Pythonie (-
http://www.python.org);

2. Prototypowanie nowych modeli (np. protokołów routingu).

Na razie wiązania skupiają się przede wszystkim na pierwszym celu, ale już na drugim
cel również zostanie ostatecznie wspierany. Powiązania Pythona dla ns-3 są rozwijane
za pomocą nowego narzędzia o nazwie PyBindGen (http://code.google.com/p/pybindgen).

An Przykład Python Scenariusz że Działa ns-3
Oto przykładowy kod napisany w Pythonie i działający ns-3, który jest napisany
w C++. Ten przykład Pythona można znaleźć w przykłady/tutorial/first.py:

importuj aplikacje ns
importuj ns.core
importuj ns.internet
importuj sieć ns
importuj ns.point_to_point

ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)

węzły = ns.network.NodeContainer()
węzły.Utwórz(2)

pointToPoint = ns.point_to_point.PointToPointHelper()
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
pointToPoint.SetChannelAttribute("Opóźnienie", ns.core.StringValue("2ms"))

urządzenia = pointToPoint.Install(węzły)

stos = ns.internet.InternetStackHelper()
stos.Instalacja(węzły)

adres = ns.internet.Ipv4AddressHelper()
adres.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))

interfejsy = adres. Przypisz (urządzenia);

echoServer = ns.applications.UdpEchoServerHelper(9)

serverApps = echoServer.Install(węzły.Get(1))
serverApps.Start(ns.core.Seconds(1.0))
serverApps.Stop(ns.core.Seconds(10.0))

echoClient = ns.applications.UdpEchoClientHelper(interfejsy.GetAddress(1), 9)
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
echoClient.SetAttribute("Interwał", ns.core.TimeValue(ns.core.Seconds (1.0)))
echoClient.SetAttribute("Rozmiar pakietu", ns.core.UintegerValue(1024))

aplikacje klienckie = echoClient.Install(węzły.Get(0))
ClientApps.Start(ns.core.Seconds(2.0))
ClientApps.Stop(ns.core.Seconds(10.0))

ns.core.Simulator.Run()
ns.core.Simulator.Destroy()

Bieganie Python Skrypty
waf zawiera kilka opcji, które automatycznie aktualizują ścieżkę Pythona w celu znalezienia pliku ns3
moduł. Aby uruchomić przykładowe programy, istnieją dwa sposoby użycia waf, aby się tym zająć. Jeden
jest uruchomienie powłoki waf; np:

$ ./waf --powłoka
$ przykłady Pythona/wireless/mixed-wireless.py

a drugim jest użycie opcji --pyrun do waf:

$ ./waf --pyrun przykłady/wireless/mixed-wireless.py

Aby uruchomić skrypt Pythona w debugerze C:

$ ./waf --powłoka
$ gdb --args python przykłady/wireless/mixed-wireless.py

Aby uruchomić własny skrypt w języku Python, który wywołuje ns-3 i to ma tę ścieżkę,
/ścieżka/do/twojego/przykładu/my-script.py, Wykonaj następujące czynności:

$ ./waf --powłoka
$ python /path/to/your/example/my-script.py

Ostrzeżenia
Powiązania Pythona dla ns-3 są w toku i znane są pewne ograniczenia
deweloperzy. Niektóre z tych ograniczeń (nie wszystkie) są wymienione tutaj.

Niekompletny Pokrycie
Przede wszystkim należy pamiętać, że nie w 100% API jest obsługiwane w Pythonie. Niektóre
powody to:

1. niektóre interfejsy API zawierają wskaźniki, które wymagają wiedzy o rodzaju pamięci
przekazywanie semantyki (kto jest właścicielem jakiej pamięci). Taka wiedza nie jest częścią funkcji
podpisów i jest albo udokumentowane, albo czasami nawet nieudokumentowane. Adnotacje są
potrzebne do powiązania tych funkcji;

2. Czasami używany jest niezwykły podstawowy typ danych lub konstrukcja C++, której jeszcze nie ma
obsługiwane przez PyBindGen;

3. GCC-XML nie raportuje klas opartych na szablonach, chyba że są one tworzone.

Większość brakujących interfejsów API można uzupełnić, jeśli wystarczy czasu, cierpliwości i wiedzy specjalistycznej
prawdopodobnie zostanie zamknięty, jeśli zostaną przesłane raporty o błędach. Nie zgłaszaj jednak błędu
mówiąc „wiązania są niekompletne”, ponieważ nie mamy siły roboczej, aby ukończyć 100%.
wiązania.

Konwersja Konstruktorzy
Konwersja konstruktorzy nie są jeszcze w pełni obsługiwane przez PyBindGen i zawsze działają jako
jawne konstruktory podczas tłumaczenia interfejsu API na język Python. Na przykład w C++ możesz to zrobić
to:

Ipv4AddressHelper ipAddrs;
ipAddrs.SetBase („192.168.0.0”, „255.255.255.0”);
ipAddrs.Assign (urządzenia szkieletowe);

W Pythonie na razie musisz zrobić:

ipAddrs = ns3.Ipv4AddressHelper()
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
ipAddrs.Assign(urządzenia szkieletowe)

Wiersz poleceń
Wiersz poleceń::Dodajwartość() działa inaczej w Pythonie niż w ns-3. W Pythonie
pierwszy parametr to ciąg znaków reprezentujący nazwę opcji wiersza poleceń. Kiedy opcja
jest ustawiony, w pliku ustawiany jest atrybut o tej samej nazwie, co nazwa opcji Wiersz poleceń()
obiekt. Przykład:

NUM_NODES_SIDE_DEFAULT = 3

cmd = ns3.Wiersz Poleceń()

cmd.NumNodesSide = Brak
cmd.AddValue("NumNodesSide", "Liczba węzłów po stronie siatki (całkowita liczba węzłów będzie tą liczbą do kwadratu)")

cmd.Parse(argv)

[...]

jeśli cmd.NumNodesSide ma wartość Brak:
num_nodes_side = NUM_NODES_SIDE_DEFAULT
jeszcze:
num_nodes_side = int(cmd.NumNodesSide)

Rysunek kalkowy
Śledzenie oparte na wywołaniach zwrotnych nie jest jeszcze poprawnie obsługiwane w języku Python, co jest nowością ns-3 API musi
zapewnione wsparcie w tym zakresie.

Zapisywanie plików Pcap jest obsługiwane przez normalne API.

Od tego czasu obsługiwane jest śledzenie ASCII ns-3.4 poprzez normalne API C++ przetłumaczone na Python.
Jednakże śledzenie ASCII wymaga utworzenia obiektu ostream, który będzie przekazywany do ASCII
metody śledzenia. W Pythonie std::ofstream w C++ został minimalnie zawinięty, aby umożliwić
Ten. Na przykład:

ascii = ns3.ofstream("wifi-ap.tr") # utwórz plik
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
ns3.Simulator.Run()
ns3.Simulator.Destroy()
ascii.close() # zamknij plik

Jest jedno zastrzeżenie: nie możesz pozwolić, aby obiekt pliku był usuwany podczas ns-3
nadal go używa. Oznacza to, że powyższa zmienna „ascii” nie może zostać usunięta
poza zakresem, w przeciwnym razie program ulegnie awarii.

Cygwin ograniczenie
Powiązania Pythona nie działają na Cygwin. Jest to spowodowane błędem gccxml.

Możesz sobie z tym poradzić, ponownie skanując definicje API z poziomu pliku cygwin
środowisko (./waf --python-scan). Jednak najbardziej prawdopodobne rozwiązanie prawdopodobnie będzie musiało
być takie, że wyłączamy powiązania Pythona w CygWin.

Jeśli naprawdę zależy Ci na powiązaniach Pythona w systemie Windows, spróbuj zbudować z mingw i natywnym
zamiast tego Python. Lub też, aby zbudować bez powiązań Pythona, wyłącz powiązania Pythona w pliku
etap konfiguracji:

$ ./waf skonfigurować --disable-python

Pracujący w Python Wiązania
Obecnie istnieją dwa rodzaje powiązań Pythona ns-3:

1. Powiązania monolityczne zawierają definicje API dla wszystkich modułów i można je znaleźć w
pojedynczy katalog, powiązania/pyton.

2. Powiązania modułowe zawierają definicje API dla pojedynczego modułu i można je znaleźć w każdym z nich
modułu wiązania katalogiem.

Python Wiązania Workflow
Proces obsługi powiązań Pythona jest następujący:

1. Okresowo programista korzysta z GCC-XML (http://www.gccxml.org) oparte na skanowaniu API
skrypt, który zapisuje zeskanowaną definicję API jako powiązania/python/ns3_module_*.py pliki
lub jako pliki Pythona w każdym module wiązania informator. Pliki te są przechowywane w ramach
kontrola wersji w pliku main ns-3 magazyn;

2. Inni programiści klonują repozytorium i korzystają z już przeskanowanych definicji API;

3. Podczas konfiguracji ns-3, pybindgen zostanie automatycznie pobrany, jeśli jeszcze tego nie zrobił
zainstalowany. Wydany ns-3 tarballs wyśle ​​kopię pybindgen.

Jeśli coś pójdzie nie tak z kompilacją powiązań Pythona i chcesz je po prostu zignorować
i kontynuuj pracę z C++, możesz wyłączyć Pythona za pomocą:

$ ./waf --disable-python

Instrukcje dla Prowadzenie Nowości Akta or Zmieniono Pszczoła
Więc zmieniałeś istniejące ns-3 Interfejsy API i powiązania Pythona nie są już kompilowane? Do
nie rozpaczaj, możesz ponownie przeskanować powiązania, aby utworzyć nowe powiązania, które odzwierciedlają zmiany
do ns-3 API.

W zależności od tego, czy używasz wiązań monolitycznych czy modułowych, zapoznaj się z poniższymi dyskusjami
dowiedz się, jak ponownie przeskanować powiązania w języku Python.

Monolityczny Python Wiązania
Skanowanie dotychczasowy Monolityczny Python Wiązania
Aby przeskanować monolityczne powiązania Pythona, wykonaj następujące czynności:

$ ./waf --python-scan

Organizacja of dotychczasowy Monolityczny Python Wiązania
Monolityczne definicje API języka Python są zorganizowane w następujący sposób. Dla każdego ns-3 moduł
, plik powiązania/python/ns3_module_ .py opisuje swoje API. Każdy z nich
pliki mają 3 funkcje najwyższego poziomu:

1. def typy_rejestrów(moduł)(): ta funkcja zajmuje się rejestracją nowych typów (np
klasy C++, wyliczenia), które są zdefiniowane w tym module;

2. def metody_rejestracji(moduł)(): ta funkcja wywołuje dla każdej klasy , inny
funkcja Register_methods_Ns3 (moduł). Te ostatnie funkcje dodają metodę
definicje dla każdej klasy;

3. def funkcje_rejestru(moduł)(): ta funkcja rejestruje ns-3 funkcje, do których należą
ten moduł.

Modułowe Python Wiązania
Omówienie
Od wersji ns 3.11 dodawane są wiązania modułowe, równolegle do starego monolitycznego
wiązania.

Nowe powiązania Pythona są generowane w przestrzeni nazw „ns”, zamiast „ns3” w przypadku starej
wiązania. Przykład:

z węzła importu ns.network
n1 = węzeł()

Z modułowymi powiązaniami Pythona:

1. Dla każdego z nich istnieje osobny moduł rozszerzenia Pythona ns-3 moduł;

2. Skanowanie definicji API (apidefs) odbywa się na zasadzie per ns-moduł;

3. Pliki apidefs każdego modułu są przechowywane w podkatalogu „bindings” modułu
informator;

Skanowanie dotychczasowy Modułowe Python Wiązania
Aby na przykład przeskanować modułowe powiązania Pythona dla modułu podstawowego, wykonaj następujące czynności:

$ ./waf --apiscan=rdzeń

Aby przeskanować modułowe powiązania Pythona dla wszystkich modułów, wykonaj następujące czynności:

$./waf --apiscan=all

Tworzenie a Nowości Moduł
Jeśli dodasz nowy moduł, powiązania Pythona będą nadal się kompilować, ale tak się nie stanie
zakryć nowy moduł.

Aby objąć nowy moduł, musisz utworzyć plik powiązania/python/ns3_module_ .py file,
podobnie jak opisano w poprzednich sekcjach i zarejestruj go w zmiennej
LOKALNE MODUŁY() in powiązania/python/ns3modulegen.py

Dodawanie Modułowe Wiązania Do A Istniejący Moduł
Aby dodać obsługę powiązań modułowych do istniejącego pliku ns-3 moduł, po prostu dodaj następujące elementy
linię do funkcji wscript build():

bld.ns3_python_bindings()

Organizacja of dotychczasowy Modułowe Python Wiązania
źródło/ /wiązania katalog może zawierać następujące pliki, niektóre z nich
opcjonalny:

· callbacks_list.py: to jest zeskanowany plik, NIE DOTYKAJ. Zawiera listę
W zeskanowanych nagłówkach znaleziono instancje szablonu wywołania zwrotnego<...>;

· modułgen__gcc_LP64.py: to jest zeskanowany plik, NIE DOTYKAJ. Zeskanowane definicje API
dla architektury GCC, LP64 (64-bit)

· modułgen__gcc_ILP32.py: to jest zeskanowany plik, NIE DOTYKAJ. Zeskanowane definicje API
dla architektury GCC, ILP32 (32-bit)

· modułgen_customizations.py: możesz opcjonalnie dodać ten plik, aby dostosować
generowanie kodu pybindgen

· scan-header.h: możesz opcjonalnie dodać ten plik, aby dostosować skanowany plik nagłówkowy
dla modułu. Zasadniczo ten plik jest skanowany zamiast ns3/ -moduł.h.
Zazwyczaj pierwszą instrukcją jest #include "ns3/ -module.h” oraz kilka innych
rzeczy wymuszające tworzenie instancji szablonów;

· moduł_helpers.cc: możesz dodać dodatkowe pliki, takie jak ten, które będą połączone z Pythonem
moduł rozszerzeń, ale muszą być zarejestrowane w wscript. Patrzeć na
src/core/wscript, aby zobaczyć przykład, jak to zrobić;

· .py: jeśli ten plik istnieje, staje się modułem Pythona „frontend” dla ns3
moduł, a moduł rozszerzenia (plik .so) staje się _ .więc zamiast .Więc.
The plik .py musi zaimportować wszystkie symbole z modułu _ (to więcej
trudniejsze niż się wydaje, zobacz przykład src/core/bindings/core.py), a następnie możesz dodać
kilka dodatkowych definicji w czystym Pythonie.

Więcej Informacje dla Programiści
Jeśli jesteś programistą i potrzebujesz więcej informacji nt ns-3powiązania Pythona, zobacz
Python Wiązania wiki strona.

Testy
Omówienie
Dokument ten dotyczy testowania i walidacji ns-3 oprogramowanie.

Ten dokument zapewnia

· podstawy terminologii i testowania oprogramowania (rozdział 2);

· opis struktury testowania ns-3 (rozdział 3);

· przewodnik dla twórców modeli lub nowych autorów modeli, dotyczący pisania testów (rozdz
4);

Krótko mówiąc, pierwsze trzy rozdziały powinni przeczytać programiści i współpracownicy ns, którzy
muszą zrozumieć, jak wnieść kod testowy i sprawdzone programy, i resztę
dokumentu zapewnia miejsce na raportowanie, jakie aspekty wybranych modeli
zostały potwierdzone.

Tło
Niniejsze publikacji naukowej może be pominięte by czytelnicy znajomy w dotychczasowy Podstawy of oprogramowanie testowanie.

Napisanie oprogramowania wolnego od defektów jest trudnym zadaniem. Istnieje wiele wymiarów
problem i istnieje wiele zamieszania dotyczącego znaczenia poszczególnych terminów w języku angielskim
różne konteksty. Stwierdziliśmy, że warto poświęcić trochę czasu na przejrzenie
temat i zdefiniowanie niektórych terminów.

Testowanie oprogramowania można luźno zdefiniować jako proces wykonywania programu za pomocą
zamiar znalezienia błędów. Kiedy ktoś rozpoczyna dyskusję na temat testowania oprogramowania, jest to
szybko staje się jasne, że można to osiągnąć na podstawie wielu różnych sposobów myślenia
podejść do tematu.

Na przykład można podzielić proces na szerokie kategorie funkcjonalne, takie jak
„testy poprawności”, „testy wydajności”, „testy odporności” i „bezpieczeństwa”
testowanie.” Innym sposobem spojrzenia na problem jest cykl życia: „testowanie wymagań”,
„testowanie projektu”, „testowanie akceptacyjne” i „testowanie utrzymania”. Jeszcze inny pogląd
wynika z zakresu testowanego systemu. W tym przypadku można mówić o „testach jednostkowych”
„testowanie komponentów”, „testowanie integracji” i „testowanie systemu”. Terminy te to:
również nie są w żaden sposób ustandaryzowane, stąd „testy konserwacyjne” i „regresja”.
testowanie” można słyszeć zamiennie. Co więcej, terminy te są często nadużywane.

Istnieje również wiele różnych filozoficznych podejść do testowania oprogramowania. Dla
na przykład niektóre organizacje zalecają pisanie programów testowych przed ich faktycznym wdrożeniem
pożądanego oprogramowania, co doprowadzi do „rozwoju opartego na testach”. Niektóre organizacje opowiadają się za tym
jak najszybsze przetestowanie z perspektywy klienta, równolegle z
zwinny proces programowania: „testuj wcześnie i często”. Nazywa się to czasem
„testowanie zwinne”. Wydaje się, że dla każdego istnieje co najmniej jedno podejście do testowania
metodologia rozwoju.

ns-3 Celem projektu nie jest wspieranie któregokolwiek z tych procesów, ale
projekt jako całość ma wymagania, które pomagają w informowaniu procesu testowego.

Podobnie jak wszystkie główne produkty programowe, ns-3 ma wiele cech, które muszą być obecne
produkt, który odniesie sukces. Z punktu widzenia testów niektóre z tych cech muszą być
adresowane są takie ns-3 muszą być „poprawne”, „solidne”, „wydajne” oraz
„możliwy do utrzymania”. Idealnie byłoby, gdyby istniały metryki dla każdego z tych wymiarów
sprawdzane za pomocą testów w celu wykrycia, kiedy produkt nie spełnia jego oczekiwań /
wymagania.

Poprawność
Zasadniczym celem testowania jest ustalenie, czy fragment oprogramowania zachowuje się
„poprawnie”. ns-3 oznacza to, że jeśli coś symulujemy, symulacja powinna
wiernie przedstawiają jakąś jednostkę fizyczną lub proces z określoną dokładnością i
precyzja.

Okazuje się, że na poprawność można spojrzeć z dwóch perspektyw.
Sprawdzenie, czy dany model jest zaimplementowany zgodnie ze specyfikacją, jest
ogólnie nazywany weryfikacja. Proces podejmowania decyzji, czy model jest poprawny
jego przeznaczenie jest ogólnie nazywane uprawomocnienie.

Walidacja oraz Weryfikacja
Model komputerowy to matematyczna lub logiczna reprezentacja czegoś. To może
przedstawiają pojazd, słonia (patrz David Harel mówić Parę słów o modelowanie an słoń at
SIMUTools 2009lub kartę sieciową. Modele mogą również reprezentować procesy, takie jak globalne
ocieplenie, przepływ ruchu na autostradach lub specyfikacja protokołu sieciowego. Modele mogą być
całkowicie wierne reprezentacje logicznej specyfikacji procesu, ale
niekoniecznie może nigdy całkowicie symulować obiekt lub proces fizyczny. W większości przypadków A
Wprowadzono szereg uproszczeń do modelu, aby przeprowadzić symulację obliczeniowo
uległy.

Każdy model posiada cel system że próbuje symulować. Pierwszy krok
stworzenie modelu symulacyjnego polega na określeniu tego docelowego systemu i poziomu szczegółowości oraz
dokładność, którą symulacja ma odtworzyć. W przypadku procesu logicznego
system docelowy można zidentyfikować jako „TCP zgodnie z definicją w dokumencie RFC 793”. W tym przypadku jest to
prawdopodobnie pożądane będzie stworzenie modelu, który całkowicie i wiernie odtworzy RFC
793. W przypadku procesu fizycznego nie będzie to możliwe. Jeśli na przykład ty
chciałbyś symulować kartę sieci bezprzewodowej, możesz stwierdzić, że potrzebujesz „an
dokładna implementacja specyfikacji 802.11 na poziomie MAC i [...] niezbyt powolna
Model na poziomie PHY specyfikacji 802.11a.''

Po wykonaniu tej czynności można opracować abstrakcyjny model systemu docelowego. To jest
zazwyczaj jest to ćwiczenie polegające na zarządzaniu kompromisami pomiędzy złożonością a wymaganiami dotyczącymi zasobów
i dokładność. Proces opracowywania modelu abstrakcyjnego nazwano model
kwalifikacja w literaturze. W przypadku protokołu TCP proces ten skutkuje:
projekt zbioru obiektów, interakcji i zachowań, który będzie w pełni realizowany
RFC 793 w ns-3. W przypadku karty bezprzewodowej proces ten skutkuje szeregiem
kompromisy umożliwiające symulację warstwy fizycznej i zaprojektowanie urządzenia sieciowego
i kanał dla ns-3, wraz z pożądanymi obiektami, interakcjami i zachowaniami.

Ten abstrakcyjny model jest następnie rozwijany w ns-3 model implementujący abstrakcję
model w postaci programu komputerowego. Proces uzyskiwania zgodności wdrożenia z
nazywa się model abstrakcyjny model weryfikacja w literaturze.

Dotychczasowy proces ma charakter otwartej pętli. Pozostaje ustalić, że dany ns-3
model ma pewne powiązanie z jakąś rzeczywistością, której model jest dokładną reprezentacją
prawdziwy system, niezależnie od tego, czy jest to proces logiczny, czy byt fizyczny.

Jeśli ktoś zamierza użyć modelu symulacyjnego, aby spróbować przewidzieć, jak będzie działał jakiś rzeczywisty system
aby się zachować, musi istnieć jakiś powód, aby wierzyć w wyniki – tj. czy można temu ufać
wniosek wyciągnięty z modelu przekłada się na prawidłowe przewidywanie dla rzeczywistego systemu.
Proces dopasowywania zachowania modelu ns-3 do pożądanego systemu docelowego
zachowanie zdefiniowane w procesie kwalifikacji modelu model uprawomocnienie
literatura. W przypadku implementacji protokołu TCP możesz chcieć porównać zachowanie
swój model ns-3 TCP do jakiejś referencyjnej implementacji w celu sprawdzenia poprawności modelu. W
w przypadku symulacji bezprzewodowej warstwy fizycznej możesz porównać zachowanie
Twój model do prawdziwego sprzętu w kontrolowanych ustawieniach,

ns-3 środowisko testowe zapewnia narzędzia umożliwiające zarówno walidację modelu, jak i
testowania i zachęca do publikowania wyników walidacji.

Krzepkość
Wytrzymałość to zdolność do wytrzymania naprężeń lub zmian w środowisku,
dane wejściowe lub obliczenia itp. System lub projekt jest „solidny”, jeśli jest w stanie sobie z nimi poradzić
zmiany przy minimalnej utracie funkcjonalności.

Tego rodzaju testy są zwykle przeprowadzane ze szczególnym naciskiem. Na przykład system jako
całość można uruchomić na wielu różnych konfiguracjach systemu, aby wykazać, że jest to możliwe
działać poprawnie w dużej liczbie środowisk.

System można również obciążać, pracując w pobliżu lub powyżej wydajności poprzez wytwarzanie
lub symulowanie różnego rodzaju wyczerpania zasobów. Ten rodzaj testów nazywa się
''test naprężeń.''

System i jego komponenty mogą zostać poddane tak zwanym „czystym testom”, które wykażą
wynik pozytywny — to znaczy, że system działa prawidłowo w reakcji na duży
zmienność oczekiwanych konfiguracji.

System i jego komponenty mogą być również poddane „brudnym testom”, które dostarczają danych wejściowych
poza oczekiwanym zakresem. Na przykład, jeśli moduł oczekuje ciągu znaków zakończonego zerem
reprezentację liczby całkowitej, brudny test może dostarczyć niezakończony ciąg znaków losowych
znaków, aby sprawdzić, czy system nie ulegnie awarii w wyniku tego nieoczekiwanego wejścia.
Niestety, wykrycie takich „brudnych” danych wejściowych i podjęcie środków zapobiegawczych w celu zapewnienia
system nie ulegnie katastrofalnej awarii, może wymagać ogromnych nakładów na rozwój.
Aby skrócić czas opracowywania, na początku projektu podjęto decyzję o
zminimalizować ilość sprawdzania parametrów i obsługi błędów w pliku ns-3 baza kodu. Dla
z tego powodu nie spędzamy dużo czasu na brudnych testach - to po prostu ujawniłoby
wyniki decyzji projektowej, o której wiemy, że podjęliśmy.

Naprawdę chcemy to zademonstrować ns-3 oprogramowanie działa w pewnych warunkach. My
zapożycz kilka definicji, aby nieco to zawęzić. The domena of stosowalność is
zbiór określonych warunków, z którymi model został przetestowany i porównany
rzeczywistości w możliwym zakresie i uznane za nadające się do użycia. The zasięg of precyzja jest
zgodność pomiędzy skomputeryzowanym modelem a rzeczywistością w zakresie stosowalności.

ns-3 środowisko testowe udostępnia narzędzia umożliwiające skonfigurowanie i uruchomienie testu
środowiskach na wielu systemach (buildbot) i udostępnia klasy zachęcające do czyszczenia
testy sprawdzające działanie systemu w oczekiwanej „dziedzinie zastosowania”
i „zakres dokładności”.

wydajnych
OK, „wykonawca” nie jest prawdziwym angielskim słowem. Jest to jednak bardzo zwięzły neologizm
jest to dość często używane do opisania tego, czego chcemy ns-3 być: mocny i wystarczająco szybki, aby
zrobić robotę.

Tak naprawdę dotyczy to szerokiego tematu testowania wydajności oprogramowania. Jeden z kluczowych
Jedyne, co się robi, to porównanie dwóch systemów w celu znalezienia, który działa lepiej (por
benchmarki). Służy to do wykazania, że ​​np. ns-3 może wykonać podstawowy rodzaj
symulacji co najmniej tak szybko, jak konkurencyjne narzędzie, lub można je wykorzystać do identyfikacji części
system, który działa źle.

W ns-3 framework testowy, zapewniamy wsparcie w zakresie synchronizacji różnych rodzajów testów.

Łatwość utrzymania
Oprogramowanie musi być łatwe w utrzymaniu. To znowu bardzo szerokie stwierdzenie, ale:
framework testowy może pomóc w tym zadaniu. Po opracowaniu modelu, jego walidacji i
zweryfikowane, możemy wielokrotnie wykonać zestaw testów dla całego systemu, aby się upewnić
aby pozostała ona ważna i zweryfikowana przez cały okres jej obowiązywania.

Kiedy funkcja przestaje działać zgodnie z przeznaczeniem po jakiejś zmianie w systemie
zintegrowany, nazywa się to ogólnie a regresja. Pierwotnie termin regresja
odnosiło się do zmiany, która spowodowała ponowne pojawienie się wcześniej naprawionego błędu, ale termin ma
ewoluował, aby opisać każdy rodzaj zmiany, która psuje istniejącą funkcjonalność. Jest wiele
rodzaje regresji, które mogą wystąpić w praktyce.

A miejscowy regresja to taki, w którym zmiana wpływa bezpośrednio na zmieniony komponent. Dla
na przykład, jeśli komponent został zmodyfikowany w celu przydzielenia i zwolnienia pamięci, ale wskaźniki są przestarzałe
używany, sam komponent ulega awarii.

A zdalny regresja to taki, w którym zmiana jednego komponentu psuje funkcjonalność
kolejny komponent. Odzwierciedla to naruszenie dorozumianego, ale prawdopodobnie nierozpoznanego
kontrakt pomiędzy komponentami.

An zdemaskowany regresja to taki, który stwarza sytuację, w której pojawia się wcześniej istniejący błąd
które nie miały żadnego wpływu, zostają nagle ujawnione w systemie. Może to być tak proste, jak ćwiczenia
ścieżkę kodu po raz pierwszy.

A jest gwarancją najlepszej jakości, które mogą dostarczyć Ci Twoje monitory, regresja to taki, który powoduje, że wymagania wydajnościowe systemu spadają
zostać naruszone. Na przykład wykonanie pewnej pracy w funkcji niskiego poziomu, która może zostać powtórzona
duża liczba razy może nagle sprawić, że system stanie się bezużyteczny z pewnych perspektyw.

ns-3 Framework testowania zapewnia narzędzia do automatyzacji procesu używanego do walidacji i
zweryfikuj kod w nocnych zestawach testów, aby szybko zidentyfikować możliwe regresje.

Testy
ns-3 składa się z rdzenia silnika symulacyjnego, zestawu modeli, przykładowych programów i testów.
Z biegiem czasu nowi współpracownicy udostępniają modele, testy i przykłady. Program testowy w Pythonie
test.py pełni funkcję menedżera wykonania testów; test.py może uruchomić kod testowy i przykłady
szukać regresji, potrafi zapisać wyniki w wielu formularzach i potrafi zarządzać kodem
narzędzia do analizy zasięgu. Na to nakładamy warstwy Buildboty które są zautomatyzowane
roboty, które przeprowadzają testy odporności, uruchamiając platformę testową w różnych systemach
i z różnymi opcjami konfiguracji.

BudujBoty
Na najwyższym poziomie testów ns-3 znajdują się buildboty (build roboty). Jeśli jesteś
nie zaznajomiony z tym systemem http://djmitche.github.com/buildbot/docs/0.7.11/.
Jest to zautomatyzowany system typu open source, który umożliwia ns-3 każdy z nich można odbudować i przetestować
czas coś się zmieniło. Uruchamiając buildboty na wielu różnych systemach, możemy
może to zapewnić ns-3 kompiluje się i działa poprawnie na wszystkich obsługiwanych systemach.

Użytkownicy (i programiści) zazwyczaj nie będą wchodzić w interakcję z systemem buildbot w inny sposób niż
przeczytaj jego wiadomości dotyczące wyników testów. Jeśli zostanie wykryta awaria w jednym z
zautomatyzowanych zadań kompilacji i testowania, buildbot wyśle ​​wiadomość e-mail na adres ns-programiści
Lista mailingowa. Ten e-mail będzie wyglądał mniej więcej tak

W adresie URL pełnych szczegółów pokazanym w wiadomości e-mail można wyszukać słowo kluczowe powiodło oraz
wybierz std link do odpowiedniego kroku, aby zobaczyć przyczynę niepowodzenia.

Buildbot wykona swoją pracę po cichu, jeśli nie będzie żadnych błędów, a system przejdzie
codziennie buduj i testuj cykle, aby sprawdzić, czy wszystko jest w porządku.

test.py
Buildboty korzystają z programu w języku Python, test.py, który jest odpowiedzialny za uruchamianie wszystkich
testów i zbieranie powstałych raportów w formie czytelnej dla człowieka. Ten program jest
dostępne również do użytku przez użytkowników i programistów.

test.py jest bardzo elastyczny, umożliwiając użytkownikowi określenie liczby i rodzaju testów
uruchomić; a także ilość i rodzaj generowanego produktu.

Przed uruchomieniem test.py, upewnij się, że przykłady i testy ns3 zostały zbudowane w ten sposób
Poniższa

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

Domyślnie test.py przeprowadzi wszystkie dostępne testy i zgłosi stan w bardzo zwięzły sposób
formularz. Uruchomienie polecenia

$ ./test.py

spowoduje szereg PASS, FAIL, CRASH or POMINĄĆ oznaczenia, po których następuje rodzaj
uruchomionego testu i jego nazwę wyświetlaną.

Waf: Wejście do katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„kompilacja” zakończyła się pomyślnie (0.939 s)
NIEPOWODZENIE: TestSuite ns3-wifi-propagation-loss-models
PASS: usługa-nazwa-obiektu TestSuite
PASS: Obiekt-pliku pcap TestSuite
ZALICZ: TestSuite ns3-tcp-cwnd
...
PASS: TestSuite ns3-tcp-interoperacyjność
PASS: Przykładowa transmisja csma
PASS: Przykład csma-multicast

Ten tryb jest przeznaczony dla użytkowników zainteresowanych ustaleniem, czy ich
dystrybucja działa poprawnie i przez programistów, którzy są zainteresowani ustaleniem, czy
wprowadzone przez nich zmiany spowodowały regresję.

Dostępnych jest wiele opcji kontrolowania zachowania test.py. jeśli pobiegniesz
test.py --help powinieneś zobaczyć podsumowanie poleceń, takie jak:

Użycie: test.py [opcje]

Opcje:
-h, --help pokaż tę wiadomość pomocy i wyjdź
-b ŚCIEŻKA BUDOWANIA, --ścieżka kompilacji=ŚCIEŻKA BUDOWANIA
określ ścieżkę, w której zbudowano ns-3 (domyślnie jest to
katalog kompilacji dla bieżącego wariantu)
-c RODZAJ, --constrain=RODZ
ograniczaj osobę przeprowadzającą test według rodzaju testu
-e PRZYKŁAD, --przykład=PRZYKŁAD
określ pojedynczy przykład do uruchomienia (nie ma ścieżki względnej
potrzebne)
-g, --grind uruchamia zestawy testów i przykłady przy użyciu valgrind
-k, --kinds wyświetla dostępne rodzaje testów
-l, --list wyświetla listę znanych testów
-m, --multiple zgłasza wiele błędów z zestawów testów i pliku test
Etui
-n, --nowaf nie uruchamia waf przed rozpoczęciem testowania
-p PRZYKŁAD, --pyprzykład=PYPRZYKŁAD
określ pojedynczy przykład Pythona do uruchomienia (z względnym
ścieżka)
-r, --retain zachowuje wszystkie pliki tymczasowe (które zwykle są plikami
usunięte)
-s PAKIET TESTOWY, --suite=ZESTAW TESTOWY
określ pojedynczy zestaw testów do uruchomienia
-t PLIK TEKSTOWY, --text=PLIK TEKSTOWY
zapisz szczegółowe wyniki testu w pliku TEXT-FILE.txt
-v, --verbose wyświetla postęp i komunikaty informacyjne
-w PLIK HTML, --web=PLIK-HTML, --html=PLIK-HTML
zapisz szczegółowe wyniki testu w HTML-FILE.html
-x PLIK-XML, --xml=PLIK-XML
zapisz szczegółowe wyniki testu w pliku XML-FILE.xml

Jeśli określono opcjonalny styl wyjściowy, można wygenerować szczegółowe opisy
testy i stan. Dostępne style to XNUMX oraz HTML. Roboty budujące wybiorą kod HTML
opcja generowania raportów z testów HTML dla kompilacji nocnych przy użyciu

$ ./test.py --html=nightly.html

W tym przypadku zostanie utworzony plik HTML o nazwie „nightly.html” zawierający ładne podsumowanie
wykonanych testów. Dla użytkowników zainteresowanych dostępny jest format „czytelny dla człowieka”.
detale.

$ ./test.py --text=results.txt

W powyższym przykładzie zestaw testów sprawdzający plik ns-3 utrata propagacji urządzenia bezprzewodowego
modele zawiodły. Domyślnie nie są podawane żadne dalsze informacje.

Aby dokładniej zbadać awarię, test.py umożliwia określenie jednego zestawu testów.
Uruchomienie polecenia

$ ./test.py --suite=ns3-wifi-propagation-loss-models

lub równoważnie

$ ./test.py -s ns3-wifi-propagacja-strat-modele

powoduje uruchomienie tego pojedynczego zestawu testów.

NIEPOWODZENIE: TestSuite ns3-wifi-propagation-loss-models

Aby znaleźć szczegółowe informacje na temat awarii, należy określić rodzaj wyjścia
pożądany. Na przykład większość ludzi prawdopodobnie będzie zainteresowana plikiem tekstowym:

$ ./test.py --suite=ns3-wifi-propagation-loss-models --text=results.txt

Spowoduje to uruchomienie tego pojedynczego zestawu testów ze statusem testu zapisanym w pliku
plik „results.txt”.

W tym pliku powinieneś znaleźć coś podobnego do poniższego

BŁĄD: Zestaw testowy „ns3-wifi-propagation-loss-models” (rzeczywisty 0.02 użytkownik 0.01 system 0.00)
ZALICZONY: Przypadek testowy „Sprawdź… Friis… model…” (rzeczywisty 0.01 użytkownik 0.00 system 0.00)
BŁĄD: Przypadek testowy „Sprawdź… Odległość dziennika… model” (rzeczywisty 0.01 użytkownik 0.01 system 0.00)
Szczegóły
Komunikat: Otrzymano nieoczekiwaną wartość SNR
Warunek: [długi opis tego, co faktycznie zawiodło]
Rzeczywista: 176.395
Limit: 176.407 +- 0.0005
Plik: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
Line: 360

Zauważ, że zestaw testów składa się z dwóch przypadków testowych. Pierwszy przypadek testowy sprawdzał
Model utraty propagacji Friisa i zaliczony. W drugim przypadku testowym nie udało się sprawdzić dziennika
Model propagacji odległości. W tym przypadku stwierdzono SNR na poziomie 176.395 i test
oczekiwano wartości 176.407 z dokładnością do trzech miejsc po przecinku. Plik, który został zaimplementowany
wyszczególniony jest test, który zakończył się niepowodzeniem, oraz linia kodu, która spowodowała awarię.

Jeśli chcesz, możesz równie łatwo napisać plik HTML, używając metody --html opcja
jak opisano powyżej.

Zazwyczaj użytkownik uruchomi wszystkie testy co najmniej raz po pobraniu ns-3 aby to zapewnić
jego środowisko zostało zbudowane poprawnie i generuje prawidłowe wyniki
zgodnie z zestawami testowymi. Programiści zazwyczaj uruchamiają zestawy testów przed i
po dokonaniu zmiany, aby upewnić się, że nie wprowadzili regresji ze swoimi
zmiany. W takim przypadku programiści mogą nie chcieć uruchamiać wszystkich testów, a jedynie ich podzbiór. Dla
na przykład programista może chcieć tylko okresowo uruchamiać testy jednostkowe podczas tworzenia
zmiany w repozytorium. W tym przypadku, test.py można powiedzieć, aby ograniczały typy
testy przeprowadzane dla określonej klasy testów. Poniższe polecenie spowoduje tylko
przeprowadzane testy jednostkowe:

$ ./test.py --constrain=jednostka

Podobnie poniższe polecenie spowoduje uruchomienie tylko przykładowych testów dymu:

$ ./test.py --constrain=jednostka

Aby wyświetlić szybką listę prawnych rodzajów ograniczeń, możesz poprosić o ich umieszczenie.
Następujące polecenie

$ ./test.py --kinds

spowoduje wyświetlenie następującej listy:

Waf: Wejście do katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„kompilacja” zakończyła się pomyślnie (0.939 s) Waf: Wejście do katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
bvt: Testy weryfikacyjne kompilacji (aby sprawdzić, czy kompilacja zakończyła się pomyślnie)
core: Uruchom wszystkie testy oparte na TestSuite (z wyłączeniem przykładów)
przykład: Przykłady (aby sprawdzić, czy przykładowe programy działają pomyślnie)
wydajność: Testy wydajności (sprawdź, czy system działa tak szybko, jak oczekiwano)
system: Testy systemowe (obejmuje moduły w celu sprawdzenia integracji modułów)
jednostka: Testy jednostkowe (w obrębie modułów w celu sprawdzenia podstawowej funkcjonalności)

Każdy z tych rodzajów testów może być dostarczony jako ograniczenie przy użyciu metody --ograniczenie opcja.

Aby wyświetlić krótką listę wszystkich dostępnych zestawów testów, możesz poprosić o ich udostępnienie
katalogowany. Następujące polecenie,

$ ./test.py --list

spowoduje wyświetlenie listy zestawu testów, podobnej do

Waf: Wejście do katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„kompilacja” zakończyła się pomyślnie (0.939 s)
histogram
Zakłócenia-Wi-Fi ns3
ns3-tcp-cwnd
Interoperacyjność ns3-tcp
próba
urządzenia-siatka-płomień
urządzenia-siatka-dot11s
urządzenia-siatka
...
nazwa-obiektu-usługa
oddzwonić
atrybuty
config
wartość-globalna
wiersza polecenia
podstawowa liczba-losowa
przedmiot

Każdy z wymienionych pakietów może zostać wybrany do samodzielnego uruchomienia za pomocą --zestaw opcja jako
pokazane powyżej.

Podobnie jak w przypadku zestawów testów, można uruchomić pojedynczy przykładowy program w języku C++, korzystając z metody --przykład
opcja. Należy zauważyć, że ścieżka względna dla przykładu nie musi być uwzględniona i to
pliki wykonywalne zbudowane dla przykładów C++ nie mają rozszerzeń. Wstępowanie

$ ./test.py --example=udp-echo

powoduje uruchomienie tego pojedynczego przykładu.

PASS: Przykładowe przykłady/udp/udp-echo

Możesz określić katalog, w którym zbudowano ns-3, używając --ścieżka kompilacji opcja jako
Następuje.

$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc

Można uruchomić pojedynczy przykładowy program w języku Python, używając metody --pyprzykład opcja. Zauważ, że
musi zostać uwzględniona ścieżka względna dla przykładu i że przykłady Pythona wymagają ich
rozszerzenia. Wstępowanie

$ ./test.py --pyexample=przykłady/samouczek/first.py

powoduje uruchomienie tego pojedynczego przykładu.

PASS: Przykładowe przykłady/tutorial/first.py

Ponieważ przykłady Pythona nie są zbudowane, nie musisz określać katalogu, w którym znajduje się plik ns-3
został zbudowany do ich obsługi.

Zwykle podczas wykonywania programów przykładowych zapisują one dużą ilość danych w pliku śledzenia.
Zwykle jest on zapisywany w katalogu podstawowym dystrybucji (np.
/home/user/ns-3-dev). Gdy test.py podaje przykład, tak naprawdę jest to całkowicie obojętne
z plikami śledzenia. Chce tylko ustalić, czy przykład można zbudować i uruchomić
bez błędu. W takim przypadku pliki śledzenia są zapisywane w formacie
/tmp/niezaznaczone-traces informator. Jeśli uruchomisz powyższy przykład, powinieneś być w stanie znaleźć
Powiązane udp-echo.tr oraz udp-echo-n-1.pcap pliki tam.

Listę dostępnych przykładów definiuje zawartość katalogu ''przykłady'' w pliku
dystrybucja. Jeśli wybierzesz przykład do wykonania za pomocą --przykład opcja,
test.py nie podejmie żadnej próby sprawdzenia, czy przykład został skonfigurowany, czy nie
po prostu spróbuję go uruchomić i zgłoszę wynik próby.

Kiedy test.py działa, domyślnie najpierw upewni się, że system został całkowicie uruchomiony
wybudowany. Można temu zaradzić, wybierając opcję --nowaf opcja.

$ ./test.py --list --nowaf

spowoduje wyświetlenie listy aktualnie zbudowanych zestawów testów, podobnej do:

modele utraty propagacji ns3 Wi-Fi
ns3-tcp-cwnd
Interoperacyjność ns3-tcp
obiekt-pliku pcap
nazwa-obiektu-usługa
generatory liczb losowych

Zwróć uwagę na brak Waf budować wiadomości.

test.py obsługuje także uruchamianie zestawów testów i przykładów w valgrind. Valgrind jest
elastyczny program do debugowania i profilowania plików wykonywalnych systemu Linux. Domyślnie działa valgrind
narzędzie zwane memcheck, które wykonuje szereg funkcji sprawdzających pamięć, w tym
wykrywanie dostępu do niezainicjowanej pamięci, niewłaściwego wykorzystania przydzielonej pamięci (podwójne zwolnienie,
dostęp po zwolnieniu itp.) i wykrywanie wycieków pamięci. Można to wybrać za pomocą
--mielić opcja.

$ ./test.py --grind

W miarę upływu czasu test.py a programy, które uruchamia pośrednio, generują duże liczby
pliki tymczasowe. Zwykle zawartość tych plików nie jest interesująca, jednak w niektórych przypadkach
przypadkach przydatne może być (do celów debugowania) przejrzenie tych plików. test.py zapewnia
--zachować opcję, która spowoduje, że te pliki tymczasowe zostaną zachowane po uruchomieniu
zakończony. Pliki są zapisywane w katalogu o nazwie wyjście testowe w podkatalogu
nazwane zgodnie z bieżącym uniwersalnym czasem koordynowanym (znanym również jako średnia Greenwich
Czas).

$ ./test.py --zachowaj

Wreszcie, test.py zapewnia --gadatliwy opcja, która wydrukuje dużą ilość informacji
o jego postępie. Nie oczekuje się, że będzie to bardzo przydatne, chyba że tak jest
błąd. W takim przypadku można uzyskać dostęp do standardowego wyjścia i standardowego błędu
raportowane poprzez uruchomienie zestawów testów i przykładów. Wybierz pełny w następujący sposób:

$ ./test.py --verbose

Wszystkie te opcje można mieszać i dopasowywać. Na przykład, aby uruchomić cały rdzeń ns-3
zestawy testowe w valgrind, w trybie pełnym, podczas generowania pliku wyjściowego HTML, jeden
zrobiłby:

$ ./test.py --verbose --grind --constrain=core --html=results.html

Taksonomia testowa
Jak wspomniano powyżej, testy są pogrupowane w szereg szeroko zdefiniowanych klasyfikacji
umożliwiają użytkownikom selektywne uruchamianie testów w celu uwzględnienia różnych rodzajów testów, których potrzebują
do zrobienia.

· Budowanie testów weryfikacyjnych

· Testy jednostkowe

· Testy systemu

· Przykłady

· Testy wydajności

Testy weryfikacyjne kompilacji
Są to stosunkowo proste testy, które buduje się wraz z dystrybucją i wykorzystuje
aby upewnić się, że kompilacja działa. Nasze obecne testy jednostkowe są dostępne w
pliki źródłowe testowanego przez siebie kodu i wbudowane w moduły ns-3; i tak pasuje
opis BVT. BVT korzystają z tego samego kodu źródłowego, który jest wbudowany w kod ns-3.
Nasze obecne testy są przykładami tego rodzaju testów.

Jednostka Testy
Testy jednostkowe to bardziej złożone testy, które szczegółowo analizują, aby upewnić się, że fragment kodu
działa zgodnie z reklamą oddzielnie. Naprawdę nie ma powodu, aby przeprowadzać tego rodzaju test
wbudowany w moduł ns-3. Okazuje się np., że jednostka testuje obiekt
usługi nazw mają mniej więcej taki sam rozmiar jak sam kod usługi nazw obiektów. Testy jednostkowe
to testy sprawdzające pojedynczy fragment funkcjonalności, który nie jest wbudowany w kod ns-3,
ale mieszka w tym samym katalogu, co testowany kod. Możliwe, że te testy
sprawdź także integrację wielu plików implementacyjnych w module. Plik
src/core/test/names-test-suite.cc jest przykładem tego rodzaju testu. Plik
src/network/test/pcap-file-test-suite.cc to kolejny przykład wykorzystujący znany dobry plik pcap
plik jako plik wektora testowego. Plik ten jest przechowywany lokalnie w katalogu src/network.

Konfiguracja Testy
Testy systemowe to takie, które obejmują więcej niż jeden moduł w systemie. Mamy mnóstwo
tego rodzaju testy działają w naszych obecnych ramach regresji, ale zazwyczaj tak jest
przeładowane przykłady. Udostępniamy nowe miejsce dla tego rodzaju testu w katalogu
źródło/test. Przykładem tego rodzaju jest plik src/test/ns3tcp/ns3-interop-test-suite.cc
testu. Wykorzystuje protokół NSC TCP do testowania implementacji protokołu ns-3 TCP. Często będzie test
wektory wymagane do tego rodzaju testu i są one przechowywane w katalogu, w którym znajduje się plik
testować życie. Na przykład ns3tcp-interop-response-vectors.pcap to plik składający się z
liczba nagłówków TCP używanych jako oczekiwane odpowiedzi testowanego protokołu TCP ns-3
do bodźca generowanego przez NSC TCP, który jest stosowany jako implementacja „znanego dobra”.

Przykłady
Przykłady są testowane przez framework, aby upewnić się, że zostały zbudowane i będą działać. Nic nie jest
zaznaczone i obecnie pliki pcap są po prostu zapisywane / Tmp do wyrzucenia. Jeśli
przykłady działają (nie zawieszają się), przechodzą ten test dymu.

Wydajność Testy
Testy wydajnościowe to takie, które ćwiczą określoną część systemu i określają
jeżeli testy zostały zakończone w rozsądnym czasie.

Bieganie Testy
Testy są zazwyczaj przeprowadzane na wysokim poziomie test.py program. Aby uzyskać listę
dostępne opcje wiersza poleceń, uruchom test.py --help

Program testowy test.py uruchomi zarówno testy, jak i te przykłady, które zostały dodane
listę do sprawdzenia. Różnica między testami a przykładami jest następująca. Testy
ogólnie sprawdzaj, czy określone wyniki symulacji lub zdarzenia są zgodne z oczekiwanym zachowaniem.
W przeciwieństwie do tego, dane wyjściowe przykładów nie są sprawdzane, a program testowy jedynie sprawdza
status wyjścia przykładowego programu, aby upewnić się, że działa on bez błędów.

Krótko mówiąc, aby uruchomić wszystkie testy, należy najpierw skonfigurować testy na etapie konfiguracji, oraz
także (opcjonalnie) przykłady, jeśli mają być sprawdzone przykłady:

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

Następnie zbuduj ns-3, a po zbudowaniu po prostu uruchom test.py. test.py -h pokaże numer
opcji konfiguracyjnych modyfikujących zachowanie test.py.

Program test.py wywołuje, dla testów i przykładów C++, program C++ niższego poziomu o nazwie
biegacz testowy aby faktycznie przeprowadzić testy. Jak omówiono poniżej, to biegacz testowy może być
pomocny sposób na debugowanie testów.

Debugowanie Testy
Debugowanie programów testowych najlepiej przeprowadzić, uruchamiając tester niskiego poziomu
program. Osoba uruchamiająca test jest pomostem łączącym ogólny kod Pythona z ns-3 kod. To jest
napisany w C++ i wykorzystuje proces automatycznego wykrywania testów w środowisku ns-3 kod do znalezienia i
umożliwiają wykonanie wszystkich różnych testów.

Główny powód test.py nie nadaje się do debugowania, jest to, że nie jest to dozwolone
logowanie, które należy włączyć za pomocą NS_LOG zmienna środowiskowa po uruchomieniu test.py. Ten
ograniczenie nie dotyczy pliku wykonywalnego modułu testowego. Dlatego jeśli chcesz zobaczyć logowanie
wyniki testów, musisz je uruchomić bezpośrednio za pomocą modułu uruchamiającego testy.

Aby uruchomić program uruchamiający test, uruchamiasz go jak każdy inny plik wykonywalny ns-3 - using
WAF. Aby wyświetlić listę dostępnych opcji, możesz wpisać:

$ ./waf --run „test-runner --help”

Powinieneś zobaczyć coś takiego jak poniżej

Waf: Wejście do katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
Waf: Opuszczenie katalogu `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
„kompilacja” zakończyła się pomyślnie (0.353 s)
--assert: Powiedz testom, aby segfault (np. Assert) w przypadku wykrycia błędu
--basedir=katalog: Ustaw katalog bazowy (gdzie znaleźć src) na „katalog”
--tempdir=katalog: Ustaw katalog tymczasowy (gdzie znaleźć pliki danych) na „katalog”
--constrain=typ-testu: Ogranicz kontrole do zestawów testów typu „typ-testu”
--help: Wydrukuj tę wiadomość
--kinds: Lista wszystkich dostępnych rodzajów testów
--list: Lista wszystkich zestawów testów (opcjonalnie ograniczone przez typ testu)
--out=nazwa pliku: Ustaw plik wyjściowy stanu testu na „nazwa pliku”
--suite=nazwa-zestawu: Uruchom zestaw testów o nazwie „nazwa-zestawu”
--verbose: Włącz komunikaty w zestawach testów uruchomieniowych

Dostępnych jest wiele rzeczy, które będą Ci znane, jeśli je posiadasz
spojrzał na test.py. Należy się tego spodziewać, ponieważ moduł uruchamiający test jest tylko interfejsem
pomiędzy test.py oraz ns-3. Możesz zauważyć, że brakuje tutaj poleceń związanych z przykładami.
To dlatego, że przykłady tak naprawdę nie są ns-3 testów. test.py prowadzi je tak, jakby były
przedstawić ujednolicone środowisko testowe, ale tak naprawdę są one zupełnie różne i nie
można znaleźć tutaj.

Pierwszą nową opcją, która pojawia się tutaj, ale nie w test.py, jest --zapewniać opcja. To
opcja jest przydatna podczas debugowania przypadku testowego uruchamianego w debugerze, np gdb. Kiedy
ta opcja instruuje podstawowy przypadek testowy, aby spowodował naruszenie segmentacji if
wykryty został błąd. Ma to miły efekt uboczny polegający na zatrzymaniu wykonywania programu
(włamać się do debugera) w przypadku wykrycia błędu. Jeśli używasz gdb, możesz użyć
ta opcja coś w stylu,

$ ./powłoka waf
$ cd kompilacja/debugowanie/narzędzia
Narzędzie testowe $ gdb
$ run --suite=wartość-globalna --assert

Jeśli w zestawie testów o wartości globalnej zostanie znaleziony błąd, zostanie wygenerowany błąd segfault
a debuger (na poziomie źródłowym) zatrzyma się na NS_TEST_ASSERT_MSG który wykrył
Błąd.

Kolejną nową opcją, która się tutaj pojawia, jest --baseir opcja. Okazuje się, że niektórzy
testy mogą wymagać odniesienia się do katalogu źródłowego pliku ns-3 dystrybucja, aby znaleźć lokalną
data, więc do uruchomienia testu zawsze wymagany jest katalog podstawowy.

Jeśli uruchomisz test z pliku test.py, program w języku Python udostępni opcję Basedir dla
Ty. Aby uruchomić jeden z testów bezpośrednio z poziomu uruchamiającego test, należy użyć WAF, będziesz musiał
określ zestaw testów, który ma zostać uruchomiony wraz z katalogiem podstawowym. Więc możesz użyć powłoki
i robić:

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-obiekt"

Zwróć uwagę na cudzysłów „wsteczny” w Pwd dowództwo.

Jeśli uruchamiasz zestaw testów za pomocą debugera, zapamiętanie tego może być dość bolesne
i stale wpisz ścieżkę bezwzględną katalogu bazy dystrybucji. Z powodu
to oznacza, że ​​jeśli pominiesz adres bazowy, osoba przeprowadzająca test spróbuje go dla ciebie znaleźć. To
rozpoczyna się w bieżącym katalogu roboczym i przechodzi w górę drzewa katalogów w poszukiwaniu pliku
plik katalogu z nazwami plików WERSJA oraz LICENCJA. Jeśli znajdzie taki, zakłada, że ​​tak
musi być bazą i zapewnia ją dla Ciebie.

Testowanie wydajność
Wiele zestawów testów musi w trakcie zapisywać pliki tymczasowe (takie jak pliki pcap).
przeprowadzanie testów. Testy wymagają wówczas katalogu tymczasowego, w którym można zapisywać. Pyton
Narzędzie testowe (test.py) automatycznie udostępni plik tymczasowy, ale jeśli zostanie uruchomione samodzielnie
należy podać ten katalog tymczasowy. Podobnie jak w przypadku ich podstawy, może tak być
irytujące, że ciągle muszę zapewniać --katalog temp, więc osoba przeprowadzająca test wytypuje jeden
dla Ciebie, jeśli go nie podasz. Najpierw szuka zmiennych środowiskowych o nazwie TMP
oraz TEMP i z nich korzysta. Jeśli żadne TMP ani TEMP są zdefiniowane, wybiera / Tmp. Kod
następnie dodaje identyfikator wskazujący, co utworzyło katalog (ns-3), a następnie czas
(gg.mm.ss), po którym następuje duża liczba losowa. Osoba uruchamiająca test tworzy z tego katalog
nazwa, która będzie używana jako katalog tymczasowy. Pliki tymczasowe następnie trafiają do katalogu, w którym
będzie się nazywać jakoś

/tmp/ns-3.10.25.37.61537845

Czas podany jest jako wskazówka, dzięki czemu można stosunkowo łatwo zrekonstruować, co
katalog został użyty, jeśli chcesz wrócić i sprawdzić pliki, które zostały w nim umieszczone
katalogiem.

Inną klasą wyników są wyniki testów, takie jak ślady pcap, które są generowane w celu porównania
wyjście referencyjne. Program testowy zazwyczaj usuwa je po zakończeniu wszystkich zestawów testowych
uruchomić. Aby wyłączyć usuwanie wyników testu, uruchom test.py z opcją „zachowaj”:

$ ./test.py -r

i wyniki testu można znaleźć w pliku testowe-wyjście/ katalogiem.

Raportowanie of test Awarie
Kiedy uruchamiasz zestaw testów za pomocą narzędzia testowego, domyślnie przeprowadzi on test w trybie cichym.
Jedyną oznaką pozytywnego wyniku testu jest brak wiadomości
od WAF mówiąc, że program zwrócił coś innego niż zerowy kod zakończenia. Aby dostać
jakieś dane wyjściowe testu, musisz określić plik wyjściowy, do którego zostaną skierowane testy
zapisz swój status XML za pomocą --na zewnątrz opcja. Trzeba zachować ostrożność przy interpretacji
wyniki, ponieważ zestawy testów to zrobią dodać wyniki do tego pliku. Próbować,

$ ./waf --run "test-runner --basedir=`pwd` --suite=pcap-file-object --out=myfile.xml"

Jeśli spojrzysz na plik mójplik.xml powinieneś zobaczyć coś takiego,


obiekt-pliku pcap

Sprawdź, czy działa PcapFile::Open w trybie „w”.
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Sprawdź, czy działa PcapFile::Open w trybie „r”.
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Sprawdź, czy działa PcapFile::Open w trybie „a”.
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Sprawdź, czy PcapFileHeader jest poprawnie zarządzany
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Sprawdź, czy PcapRecordHeader jest poprawnie zarządzany
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Sprawdź, czy PcapFile może odczytać znany, dobry plik pcap
PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00

PRZECHODZIĆ
rzeczywisty 0.00 użytkownik 0.00 system 0.00


Jeśli znasz XML, powinno to być dość oczywiste. Nie jest to również tzw
kompletny plik XML, ponieważ zestawy testów są zaprojektowane tak, aby ich dane wyjściowe były dołączane do wzorca
Plik stanu XML zgodnie z opisem w test.py

Debugowanie test apartament Awarie
Aby debugować awarie testowe, takie jak

AWARIA: TestSuite ns3-zakłócenia Wi-Fi

Dostęp do podstawowego programu uruchamiającego testy można uzyskać za pośrednictwem gdb w następujący sposób, a następnie przekazać plik
Argument „--basedir=`pwd`” do uruchomienia (w razie potrzeby możesz także przekazać inne argumenty, ale
Basedir to potrzebne minimum):

$ ./waf --command-template="gdb %s" --run "test-runner"
Waf: Wejście do katalogu `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
Waf: Opuszczenie katalogu `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
„kompilacja” zakończyła się pomyślnie (0.380 s)
GNU gdb 6.8-debian
Prawa autorskie (C) 2008 Fundacja Wolnego Oprogramowania, Inc.
L cense GPLv3+: GNU GPL wersja 3 lub nowszahttp://gnu.org/licenses/gpl.html>
To jest wolne oprogramowanie: możesz je zmieniać i rozpowszechniać.
W zakresie dozwolonym przez prawo nie udziela się ŻADNEJ GWARANCJI. Wpisz „pokaż kopiowanie”
i "pokaż gwarancję", aby uzyskać szczegółowe informacje.
Ten GDB został skonfigurowany jako „x86_64-linux-gnu”…
(gdb) r --basedir=`pwd`
Uruchamianie programu: <..>/build/debug/utils/test-runner --basedir=`pwd`
[Debugowanie wątków przy użyciu włączonej biblioteki libthread_db]
twierdzenie nie powiodło się. plik=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
...

Oto kolejny przykład użycia Valgrind do debugowania problemu z pamięcią, takiego jak:

VALGR: Regresja urządzeń TestSuite-mesh-dot11s

$ ./waf --command-template="valgrind %s --basedir=`pwd` --suite=devices-mesh-dot11s-regression" --run test-runner

Klasa Biegacz testowy
Pliki wykonywalne, w których uruchamiane są dedykowane programy testowe, korzystają z klasy TestRunner. Ta klasa
zapewnia automatyczną rejestrację i wyświetlanie testów, a także sposób ich wykonania
testy indywidualne. Poszczególne zestawy testów korzystają z globalnych konstruktorów C++, do których można się dodawać
zbiór zestawów testów zarządzanych przez osobę uruchamiającą testy. Program uruchamiający test służy do tworzenia listy
wszystkich dostępnych testów i wybrać test do wykonania. To dość prosta klasa
który zapewnia trzy statyczne metody dostarczania lub dodawania i pobierania zestawów testów do pliku
zbiór testów. Zobacz doxygen dla zajęć ns3::TestRunner .

Testowanie Zestaw
Wszystkie produkty ns-3 testy dzielą się na zestawy testów i przypadki testowe. Zestaw testów to a
zbiór przypadków testowych, które całkowicie realizują dany rodzaj funkcjonalności. Jak
opisanych powyżej, zestawy testów można sklasyfikować jako:

· Budowanie testów weryfikacyjnych

· Testy jednostkowe

· Testy systemu

· Przykłady

· Testy wydajności

Ta klasyfikacja jest eksportowana z klasy TestSuite. Ta klasa jest dość prosta,
istniejąca jedynie jako miejsce do eksportu tego typu i gromadzenia przypadków testowych. Od użytkownika
perspektywy, aby stworzyć nowy TestSuite w systemie wystarczy zdefiniować nowy
klasa, która dziedziczy po klasie Pakiet testowy i pełnić te dwa obowiązki.

Poniższy kod zdefiniuje nową klasę, za pomocą której można uruchomić test.py jako „test jednostkowy”.
z wyświetlaną nazwą, nazwa mojego-zestawu-testowego.

klasa MySuite: publiczny TestSuite
{
publiczny:
Mój pakiet testowy ();
};

MójTestSuite::MójTestSuite ()
: TestSuite („nazwa mojego-zestawu-testów”, JEDNOSTKA)
{
AddTestCase (nowy MyTestCase);
}

MójTestSuite mójTestSuite;

Klasa bazowa zajmuje się całą rejestracją i raportowaniem wymaganym, aby towar był dobry
obywatel w ramach testowych.

Testowanie Walizka
Poszczególne testy tworzone są przy użyciu klasy TestCase. Typowe modele wykorzystania testu
obejmuje „jeden przypadek testowy na funkcję” i „jeden przypadek testowy na metodę”. Mieszanki
modele te mogą być stosowane.

Aby utworzyć nowy przypadek testowy w systemie wystarczy dziedziczyć z pliku
Przypadek testowy bazowej, zastąp konstruktora, nadając przypadkowi testowemu nazwę i zastąp
dotychczasowy DoRun metoda uruchomienia testu.

class MyTestCase : publiczny przypadek testowy
{
MójPrzypadek Testowy ();
wirtualna pustka DoRun (pustka);
};

MójTestCase::MójTestCase ()
: TestCase („Sprawdź trochę funkcjonalności”)
{
}

unieważnić
MyTestCase::DoRun (puste)
{
NS_TEST_ASSERT_MSG_EQ (true, true, „Jakiś komunikat o błędzie”);
}

Użytkowe
Istnieje wiele różnego rodzaju narzędzi, które również są częścią testów
struktura. Przykłady obejmują uogólniony plik pcap przydatny do przechowywania wektorów testowych; A
ogólny kontener przydatny do tymczasowego przechowywania wektorów testowych podczas wykonywania testu; I
narzędzia do generowania prezentacji na podstawie wyników testów walidacyjnych i weryfikacyjnych.

Te narzędzia nie są tutaj udokumentowane, ale na przykład zobacz, jak testuje protokół TCP
Znaleziono w src/test/ns3tcp/ użyj plików pcap i wyników referencyjnych.

W jaki sposób do napisać Testy
Podstawowym celem projektu ns-3 jest pomoc użytkownikom w poprawie ważności i
wiarygodność ich wyników. Uzyskanie prawidłowych modeli składa się z wielu elementów
symulacje, a testowanie jest głównym elementem. Jeśli udostępnisz modele lub przykłady
ns-3, możesz zostać poproszony o przesłanie kodu testowego. Wykorzystane zostaną modele, które prześlesz
od wielu lat przez inne osoby, które zapewne na pierwszy rzut oka nie mają pojęcia, czy
model jest poprawny. Kod testowy, który napiszesz dla swojego modelu, pomoże uniknąć przyszłości
regresje wyników i pomoże przyszłym użytkownikom zrozumieć proces weryfikacji i
granice zastosowania Twoich modeli.

Istnieje wiele sposobów sprawdzenia poprawności implementacji modelu. W tym
mamy nadzieję omówić kilka typowych przypadków, które można wykorzystać jako przewodnik podczas pisania nowego
testów.

Próba Pakiet testowy szkielet
Kiedy zaczynasz od zera (tzn. nie dodajesz TestCase do istniejącego pakietu TestSuite), te
sprawy muszą zostać ustalone z góry:

· Jak będzie się nazywał zestaw testów

· Jaki to będzie typ testu (test weryfikacyjny kompilacji, test jednostkowy, test systemowy lub
Test wydajności)

· Gdzie będzie umieszczony kod testowy (albo w istniejącym module ns-3, albo oddzielnie w
src/test/katalog). Będziesz musiał edytować plik wscript w tym katalogu
skompiluj nowy kod, jeśli jest to nowy plik.

Program o nazwie src/create-module.py jest dobrym punktem wyjścia. Ten program może być
wywoływane np utwórz-moduł.py Router dla hipotetycznego nowego modułu o nazwie Router. Pewnego razu
zrobisz to, zobaczysz Router katalog i a test/zestaw-testów-routerów.cc zestaw testowy.
Ten plik może być punktem wyjścia do wstępnego testu. To jest działający zestaw testów,
chociaż faktycznie przeprowadzone testy są trywialne. Skopiuj go do testu modułu
katalog i dokonaj globalnej podstawienia „Router” w tym pliku na coś powiązanego
do modelu, który chcesz przetestować. Możesz także edytować rzeczy takie jak bardziej opisowe
nazwa przypadku testowego.

Musisz także dodać blok do swojego skryptu wscript, aby ten test się skompilował:

moduł_test.źródło = [
„test/router-test-suite.cc”,
]

Zanim faktycznie zaczniesz robić z tego pożyteczne rzeczy, może pomóc spróbować uruchomić
szkielet. Upewnij się, że ns-3 został skonfigurowany z opcją „--enable-tests”.
Załóżmy, że Twój nowy zestaw testów nazywa się „router”, tak jak tutaj:

RouterTestSuite::RouterTestSuite ()
: TestSuite („router”, JEDNOSTKA)

Spróbuj tego polecenia:

$ ./test.py -s routera

Należy wygenerować dane wyjściowe takie jak poniżej:

PASS: router TestSuite
1 z 1 testów zaliczonych (1 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Zobacz src/lte/test/test-lte-antenna.cc, aby zapoznać się z działającym przykładem.

Testowanie makra
Dostępnych jest wiele makr umożliwiających sprawdzenie wyników programu testowego zgodnie z oczekiwaniami
wyjście. Te makra są zdefiniowane w src/core/model/test.h.

Główny zestaw używanych makr obejmuje:

NS_TEST_ASSERT_MSG_EQ(rzeczywista, limit, komunikat)
NS_TEST_ASSERT_MSG_NE(rzeczywista, limit, komunikat)
NS_TEST_ASSERT_MSG_LT(rzeczywista, limit, komunikat)
NS_TEST_ASSERT_MSG_GT(rzeczywista, limit, komunikat)
NS_TEST_ASSERT_MSG_EQ_TOL(rzeczywisty, limit, tol, komunikat)

Pierwszy argument rzeczywisty jest testowaną wartością, drugą wartością ograniczenie jest oczekiwane?
wartość (lub wartość do sprawdzenia) i ostatni argument msg to komunikat o błędzie
wydrukować, jeśli test się nie powiedzie.

Pierwsze cztery powyższe makra sprawdzają równość, nierówność, mniejszą lub większą niż,
odpowiednio. Piąte makro powyżej sprawdza równość, ale w ramach pewnej tolerancji.
Ten wariant jest przydatny podczas testowania liczb zmiennoprzecinkowych pod kątem równości względem limitu,
gdzie chcesz uniknąć niepowodzenia testu z powodu błędów zaokrągleń.

Wreszcie istnieją warianty powyższego, w którym słowo kluczowe ZAPEWNIAĆ zastępuje się OCZEKIWAĆ.
Warianty te zostały zaprojektowane specjalnie do stosowania w metodach zwracających (zwłaszcza wywołaniach zwrotnych).
próżnia. Zarezerwuj ich użycie dla wywołań zwrotnych, których używasz w swoich programach testowych; w przeciwnym razie użyj
dotychczasowy ZAPEWNIAĆ warianty.

W jaki sposób do Dodaj an przykład program do dotychczasowy test apartament
Można „test dymu”, który pokazuje, że przykłady kompilują się i działają pomyślnie do końca (bez
wycieki pamięci) przy użyciu pliku przykłady-do-run.py skrypt znajdujący się w katalogu testowym modułu.
W skrócie, włączając instancję tego pliku do katalogu testowego, możesz spowodować, że
uruchamiający test, aby wykonać wymienione przykłady. Zwykle najlepiej jest się upewnić
wybierz przykłady, które mają rozsądnie krótki czas wykonania, aby nie ugrzęznąć w testach. Widzieć
przykład w źródło/lte/test/ katalogiem.

Testy dla boolean wyniki
Testy wyniki jeśli chodzi o komunikację i motywację przypadkowość is zaangażowany
Testy wydajność dane przed a znany 分配
Zapewnienie nietrywialne wkład wektory of dane
Przechowywanie oraz odwoływanie się nietrywialne wydajność dane
Przedstawianie Twój wydajność test dane
Obsługa klienta
Tworzenie a nowych ns-3 model
W tym rozdziale opisano proces projektowania aplikacji ns-3 Model. W wielu przypadkach badawczych
użytkownicy nie będą zadowoleni jedynie z dostosowania istniejących modeli, ale mogą chcieć rozszerzyć
rdzeń symulatora w nowatorski sposób. Posłużymy się przykładem dodania ErrorModelu do pliku
prosty ns-3 link jako motywujący przykład tego, jak można podejść do tego problemu i
przejść przez proces projektowania i wdrażania.

UWAGA:
Dokumenty

Tutaj skupiamy się na procesie tworzenia nowych modeli i nowych modułów, a niektóre z nich
związane z tym wybory projektowe. Ze względu na przejrzystość odraczamy dyskusję nt mechanika
dokumentowania modeli i kodu źródłowego do Dokumenty rozdział.

Wnętrze Podejście
Zastanów się, jak chcesz, żeby to działało; co powinno zrobić. Pomyśl o tych rzeczach:

· funkcjonalność: Jaką funkcjonalność powinien posiadać? Jakie są atrybuty lub konfiguracja
wystawiony na działanie użytkownika?

· możliwość ponownego użycia: W jakim stopniu inni powinni mieć możliwość ponownego wykorzystania mojego projektu? Czy mogę ponownie wykorzystać kod z
ns-2 rozpocząć? W jaki sposób użytkownik integruje model z resztą innego
symulacja?

· zależności: Jak mogę ograniczyć wprowadzanie zewnętrznych zależności w moim nowym kodzie
w jak największym stopniu (aby uczynić go bardziej modułowym)? Na przykład, czy powinienem ich unikać
zależność od protokołu IPv4, jeśli chcę, aby był on również używany przez protokół IPv6? Czy powinienem unikać jakiejkolwiek zależności
w ogóle na IP?

Nie wahaj się skontaktować z użytkownicy ns-3 or ns-programiści listę, jeśli masz pytania.
W szczególności ważne jest, aby pomyśleć o publicznym interfejsie API nowego modelu i poprosić o niego
informacja zwrotna. Pomaga także poinformować innych o Twojej pracy, jeśli jesteś nią zainteresowany
współpracownicy.

Przykład: Model błędu
Model błędu istnieje w ns-2. Umożliwia przekazywanie pakietów do obiektu stanowego, który
określa na podstawie zmiennej losowej, czy pakiet jest uszkodzony. Osoba dzwoniąca może
następnie zdecyduj, co zrobić z pakietem (upuścić go itp.).

Głównym interfejsem API modelu błędów jest funkcja, do której należy przekazać pakiet i wartość zwracana
ta funkcja jest wartością logiczną, która informuje osobę dzwoniącą, czy wystąpiło jakiekolwiek uszkodzenie. Notatka
że w zależności od modelu błędu bufor danych pakietowych może być uszkodzony lub nie.
Nazwijmy tę funkcję „IsCorrupt()”.

Póki co w naszym projekcie mamy:

model błędu klasy
{
publiczny:
/ **
* \returns true, jeśli pakiet ma być uważany za błędny/uszkodzony
* \param pkt Pakiet, do którego ma zostać zastosowany model błędu
*/
bool jest uszkodzony (cz pkt);
};

Należy pamiętać, że nie przekazujemy wskaźnika const, co pozwala funkcji na modyfikację
pakiet, jeśli funkcja IsCorrupt() zwróci wartość true. Nie wszystkie modele błędów faktycznie modyfikują pakiet;
należy udokumentować, czy bufor danych pakietowych jest uszkodzony.

Możemy również chcieć specjalistycznych wersji tego, takich jak ns-2, więc chociaż to nie jest
jest to jedyny wybór projektowy ze względu na polimorfizm, zakładamy, że będziemy podklasować klasę bazową
ErrorModel dla klas specjalistycznych, takich jak RateErrorModel, ListErrorModel itp., takich jak
jest zrobione w ns-2.

Być może w tym momencie myślisz: „Dlaczego nie uczynić IsCorrupt() metodą wirtualną?”. To jest
jedno podejście; drugim jest uczynienie publicznej funkcji niewirtualnej pośrednią poprzez:
prywatna funkcja wirtualna (w C++ jest to idiom interfejsu niewirtualnego i jest
przyjęty w ns-3 klasy ErrorModel).

Następnie, czy to urządzenie powinno mieć jakieś zależności od protokołu IP lub innych protokołów? My nie chcemy
do tworzenia zależności od protokołów internetowych (model błędu powinien mieć zastosowanie do
protokoły inne niż internetowe), więc będziemy o tym pamiętać później.

Inną kwestią jest to, w jaki sposób obiekty będą uwzględniać ten model błędu. Przewidujemy umieszczenie
jawny setter w niektórych implementacjach NetDevice, na przykład:

/ **
* Dołącz otrzymany model błędu do urządzenia PointToPointNetDevice.
*
* Urządzenie PointToPointNetDevice może opcjonalnie zawierać moduł ErrorModel
* łańcuch odbioru pakietu.
*
* @zobacz Model błędu
* @param em Ptr do ErrorModelu.
*/
void PointToPointNetDevice::SetReceiveErrorModel(Ptr oni);

Ponownie, nie jest to jedyny wybór, jaki mamy (modele błędów można zagregować do wielu
inne obiekty), ale spełnia nasz podstawowy przypadek użycia, czyli umożliwienie użytkownikowi wymuszenia
błędy w innych udanych transmisjach pakietów na poziomie NetDevice.

Po pewnym przemyśleniu i przyjrzeniu się istniejącym ns-2 kod, oto przykładowy interfejs API bazy
klasa i pierwsza podklasa, które można przesłać do wstępnego przeglądu:

model błędu klasy
{
publiczny:
Model błędu ();
wirtualny ~Model błędu ();
bool jest uszkodzony (cz pkt);
nieważne Resetuj (unieważnione);
nieważne Włącz (nieważne);
void Wyłącz (void);
bool IsEnabled (void) const;
prywatny:
wirtualny bool DoCorrupt (cz pkt) = 0;
wirtualna pustka DoReset (void) = 0;
};

wyliczenie Jednostka błędu
{
EU_BIT,
EU_BYTE,
EU_PKT
};

// Określ, które pakiety są błędne, odpowiadające pakietowi bazowemu
// rozkład zmiennej losowej, poziom błędu i jednostka współczynnika.
klasa RateErrorModel: publiczny model błędu
{
publiczny:
Model błędu stawki ();
wirtualny ~RateErrorModel ();
wyliczenie ErrorUnit GetUnit (void) const;
void SetUnit (wyliczenie ErrorUnit error_unit);
podwójna stała GetRate (void);
void SetRate (podwójna stawka);
void SetRandomVariable (const RandomVariable &ranvar);
prywatny:
wirtualny bool DoCorrupt (cz pkt);
wirtualna nieważność DoReset (unieważnienie);
};

Rusztowanie
Powiedzmy, że jesteś gotowy, aby rozpocząć wdrażanie; masz dość jasny obraz
co chcesz zbudować i być może poprosiłeś o wstępną recenzję lub sugestie
Lista. Jednym ze sposobów podejścia do następnego kroku (wdrożenia) jest utworzenie rusztowania i
uzupełnij szczegóły w miarę dojrzewania projektu.

W tej sekcji omówiono wiele kroków, które należy wziąć pod uwagę przy definiowaniu rusztowania lub
niefunkcjonalny szkielet tego, co ostatecznie zaimplementuje Twój model. Zwykle jest dobrze
poćwicz, aby nie czekać, aż te szczegóły zostaną zintegrowane na końcu, ale zamiast tego zgłębić a
szkielet modelu do systemu wcześnie, a następnie dodaj funkcje później, gdy API i
integracja wydaje się właściwa.

Pamiętaj, że w poniższej prezentacji będziesz chciał zmodyfikować kilka rzeczy dla swojego modelu
ponieważ jeśli dosłownie zastosujesz się do modelu błędu, utworzony kod będzie kolidował z
istniejący model błędu. Poniżej znajduje się jedynie zarys tego, jak zbudowano ErrorModel
Możliwość dostosowania do innych modeli.

Review dotychczasowy ns-3 Kodowanie Styl dokument
W tym momencie możesz zrobić pauzę i przeczytać ns-3 zwłaszcza dokument dotyczący stylu kodowania
jeśli rozważasz wniesienie swojego kodu z powrotem do projektu. Styl kodowania
dokument jest połączony z główną stroną projektu: ns-3 kodowanie styl.

Zdecydować Gdzie in dotychczasowy Źródło Drzewo dotychczasowy Model Powinien Zamieszkać
Wszystkie ns-3 kod źródłowy modelu znajduje się w katalogu src /. Będziesz musiał wybrać który
podkatalog, w którym się znajduje. Jeśli jest to jakiś nowy kod modelu, warto go umieścić
do src / gdzieś w katalogu, szczególnie w celu ułatwienia integracji z kompilacją
pomimo napiętego harmonogramu

W przypadku modelu błędu jest on bardzo powiązany z klasą pakietu, więc ma to sens
wdrożyć to w źródło/sieć/ moduł gdzie ns-3 pakiety są realizowane.

WAF oraz skrypt
ns-3 używa Waf zbudować system. Będziesz chciał zintegrować swój nowy ns-3 używa Wafa
zbudować system. Będziesz chciał zintegrować swoje nowe pliki źródłowe z tym systemem. Ten
wymaga dodania plików do skrypt plik znaleziony w każdym katalogu.

Zacznijmy od pustych plików error-model.h i error-model.cc i dodajmy to do
src/sieć/wscript. Tak naprawdę to tylko kwestia dodania pliku .cc do reszty
pliki źródłowe, a plik .h na listę plików nagłówkowych.

Teraz przejdź do katalogu najwyższego poziomu i wpisz „./test.py”. Nie powinieneś był się łamać
cokolwiek za pomocą tej operacji.

Zawierać Gwardia
Następnie dodajmy trochę zawierać gwardia w naszym pliku nagłówkowym.:

#ifndef BŁĄD_MODEL_H
# zdefiniuj ERROR_MODEL_H
...
#endif

przestrzeń nazw Ns3
ns-3 używa ns-3 przestrzeń nazw aby odizolować swoje symbole od innych przestrzeni nazw. Zazwyczaj:
użytkownik następnie umieści plik ns-3 blok przestrzeni nazw w pliku cc i h.:

przestrzeń nazw ns3 {
...
}

W tym momencie mamy kilka plików szkieletowych, w których możemy rozpocząć definiowanie naszych nowych klas.
Plik nagłówkowy wygląda następująco:

#ifndef BŁĄD_MODEL_H
# zdefiniuj ERROR_MODEL_H

przestrzeń nazw ns3 {

} // przestrzeń nazw ns3
#endif

natomiast błąd-model.cc plik wygląda po prostu tak:

#include „błąd-model.h”

przestrzeń nazw ns3 {

} // przestrzeń nazw ns3

Pliki te powinny się skompilować, ponieważ tak naprawdę nie zawierają żadnej zawartości. Jesteśmy już na to gotowi
zacznij dodawać klasy.

Początkowy Wdrożenie
W tym momencie wciąż pracujemy nad pewnym rusztowaniem, ale możemy zacząć definiować nasze
klas, których funkcjonalność zostanie dodana później.

dziedziczyć od dotychczasowy przedmiot Klasa?
Jest to ważny krok projektowy; czy używać klasy przedmiot jako klasa bazowa dla twojego nowego
Klasy.

Jak opisano w rozdziale nt ns-3 Model obiektowy, klasy dziedziczące po klasie
przedmiot uzyskaj specjalne właściwości:

· ten ns-3 system typów i atrybutów (patrz Atrybuty)

· system agregacji obiektów

· system zliczania referencji smart-pointer (klasa Ptr)

Klasy wywodzące się z class Baza obiektów} uzyskaj dwie pierwsze właściwości powyżej, ale tego nie rób
uzyskać inteligentne wskazówki. Klasy wywodzące się z class Podstawa licznika ref zdobądź tylko inteligentny wskaźnik
system liczenia referencji.

W praktyce klasa przedmiot jest wariantem trzech powyższych, że ns-3 deweloper będzie
najczęściej spotykane.

W naszym przypadku chcemy skorzystać z systemu atrybutów i będziemy przekazywać instancje
tego obiektu w poprzek ns-3 publiczne API, więc class przedmiot jest dla nas odpowiedni.

Początkowy Zajęcia
Jednym ze sposobów postępowania jest rozpoczęcie od zdefiniowania absolutnego minimum funkcji i sprawdzenie, czy tak się stanie
skompilować. Przyjrzyjmy się, co jest potrzebne do wdrożenia, gdy wyprowadzamy z klasy Obiekt.:

#ifndef BŁĄD_MODEL_H
# zdefiniuj ERROR_MODEL_H

#include „ns3/obiekt.h”

przestrzeń nazw ns3 {

klasa ErrorModel: obiekt publiczny
{
publiczny:
statyczny TypeId GetTypeId (pusty);

Model błędu ();
wirtualny ~Model błędu ();
};

klasa RateErrorModel: publiczny model błędu
{
publiczny:
statyczny TypeId GetTypeId (pusty);

Model błędu stawki ();
wirtualny ~RateErrorModel ();
};
#endif

Warto tutaj zwrócić uwagę na kilka rzeczy. Musimy uwzględnić obiekt.h. Konwencja w ns-3 jest to, że jeśli
plik nagłówkowy znajduje się w tym samym katalogu, może zostać dołączony bez żadnej ścieżki
prefiks. Dlatego jeśli wdrażaliśmy ErrorModel w src/rdzeń/model katalog, my
mógł po prostu powiedzieć „#zawierać "obiekt.h"„. Ale jesteśmy w tym src/sieć/model, więc musimy
uwzględnij to jako „#zawierać „ns3/obiekt.h”". Należy również pamiętać, że wykracza to poza przestrzeń nazw
deklaracja.

Po drugie, każda klasa musi implementować statyczną funkcję publiczną o nazwie PobierzTypeId (próżnia).

Po trzecie, dobrym pomysłem jest raczej implementowanie konstruktorów i destruktorów, niż pozwalanie na to
kompilator je wygenerował i uczynił destruktor wirtualnym. W C++ zwróć także uwagę na tę kopię
operator przypisania i konstruktory kopiujące są generowane automatycznie, jeśli nie są zdefiniowane, tzw
jeśli ich nie chcesz, powinieneś zaimplementować je jako członków prywatnych. Ten aspekt
Język C++ jest omówiony w książce Efektywne C++ Scotta Meyersa. pozycja 45.

Przyjrzyjmy się teraz odpowiedniemu kodowi implementacji szkieletu w pliku .cc.:

#include „błąd-model.h”

przestrzeń nazw ns3 {

NS_OBJECT_ENSURE_REGISTERED (Model błędu);

TypeId ErrorModel::GetTypeId (puste)
{
statyczny TypeId tid = TypeId („ns3::ErrorModel”)
.Ustaw Rodzica ()
;
pora powrotu;
}

Model błędu::Model błędu ()
{
}

Model błędu::~Model błędu ()
{
}

NS_OBJECT_ENSURE_REGISTERED (Model błędu stawki);

TypeId RateErrorModel::GetTypeId (void)
{
statyczny TypeId tid = TypeId („ns3::RateErrorModel”)
.Ustaw Rodzica ()
.Dodaj konstruktora ()
;
pora powrotu;
}

RateErrorModel::rateErrorModel ()
{
}

RateErrorModel::~ RateErrorModel ()
{
}

Co to jest PobierzTypeId (próżnia) funkcjonować? Ta funkcja robi kilka rzeczy. Rejestruje A
unikalny ciąg znaków do systemu TypeId. Ustala hierarchię obiektów w pliku
system atrybutów (przez Ustaw Rodzica). Deklaruje również, że niektóre obiekty można tworzyć za pomocą
framework tworzenia obiektów (Dodaj Konstruktor).

Makro NS_OBJECT_ENSURE_REGISTERED (Nazwa klasy) jest potrzebny również raz dla każdej klasy that
definiuje nową metodę GetTypeId i dokonuje faktycznej rejestracji klasy w pliku
system. Rozdział Model obiektowy omawia to bardziej szczegółowo.

Włącznie z Zewnętrzny Akta
Logowanie Obsługa klienta
Tutaj, napisać a bit Parę słów o dodanie |ns3| zalogowaniu makra. Note że LOG_COMPONENT_DEFINE is
zrobić zewnętrzne dotychczasowy przestrzeń nazw Ns3

Konstruktor, pusty Funkcjonować prototypy
Klawisz Zmienne (Domyślny Wartości, Atrybuty)
Testowanie Program 1
przedmiot
Dodawanie a Próba Scenariusz
W tym momencie można spróbować zastosować podstawowe rusztowanie zdefiniowane powyżej i dodać je
do systemu. Wykonanie tego kroku pozwala teraz na użycie prostszego modelu podczas instalacji wodno-kanalizacyjnej
do systemu i może również ujawnić, czy konieczne są jakiekolwiek modyfikacje projektu lub interfejsu API
zrobiony. Kiedy już to zrobimy, powrócimy do budowania funkcjonalności
Same modele błędów.

Dodaj Basic Obsługa klienta in dotychczasowy Klasa
/* punkt-punkt-urzadzenie-sieciowe.h */
klasa ErrorModel;

/ **
* Model błędu dla zdarzeń odbierania pakietów
*/
Cz m_receiveErrorModel;

Dodaj Akcesor
unieważnić
PointToPointNetDevice::SetReceiveErrorModel (Ptr oni)
{
NS_LOG_FUNCTION (ten << em);
m_receiveErrorModel = em;
}

.AddAttribute("ReceiveErrorModel",
„Model błędów odbiornika wykorzystywany do symulacji utraty pakietów”,
Wartość wskaźnika (),
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
MakePointerChecker ())

pion W dotychczasowy Konfiguracja
void PointToPointNetDevice::Receive (Ptr paczka)
{
NS_LOG_FUNCTION (ten << pakiet);
protokół uint16_t = 0;

if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (pakiet) )
{
//
// Jeśli mamy model błędu i wskazuje on, że nadszedł czas, aby go stracić
// uszkodzony pakiet, nie przesyłaj dalej tego pakietu, puść go.
//
m_dropTrace (pakiet);
}
więcej
{
//
// Uderz w hak śledzenia odbioru, usuń nagłówek protokołu punkt-punkt
// i przekazuje ten pakiet w górę stosu protokołów.
//
m_rxTrace (pakiet);
ProcessHeader(pakiet, protokół);
m_rxCallback (to, pakiet, protokół, GetRemote ());
if (!m_promiscCallback.IsNull())
{ m_promiscCallback (to, pakiet, protokół, GetRemote (),
GetAddress (), NetDevice::PACKET_HOST);
}
}
}

Stwórz Null Funkcjonalny Scenariusz
/* prosty-błąd-model.cc */

// Błąd modelu
// Chcemy dodać model błędu do NetDevice węzła 3
// Możemy uzyskać uchwyt do urządzenia NetDevice za pośrednictwem kanału i węzła
// wskaźniki
Ptr nd3 = PointToPointTopology::GetNetDevice
(n3, kanał 2);
Ptr em = Stwórz ();
nd3->SetReceiveErrorModel (em);

bool
ErrorModel::DoCorrupt (pakiet i p)
{
NS_LOG_FUNCTION;
NS_LOG_UNCOND("Uszkodzony!");
return false;
}

W tym momencie możemy uruchomić program z naszym trywialnym ErrorModelem podłączonym do odbioru
ścieżka PointToPointNetDevice. Wypisuje napis „Corrupt!” za każdy pakiet
otrzymane w węźle n3. Następnie wracamy do modelu błędu, aby dodać podklasę, która działa
bardziej interesujące modelowanie błędów.

Dodaj a Podklasa
Trywialna klasa bazowa ErrorModel nie robi nic ciekawego, ale zapewnia
użyteczny interfejs klasy bazowej (Corrupt () i Reset ()), przekazany do funkcji wirtualnych, które
można podzielić na podklasy. Przyjrzyjmy się następnie temu, co nazywamy BasicErrorModel, na którym jest oparty
dotychczasowy ns-2 Klasa ErrorModel (w ns-2/kolejka/model błędu.{cc,h}).

Jakie właściwości chcemy, aby to miało, z perspektywy interfejsu użytkownika? Chcielibyśmy
aby użytkownik mógł w prosty sposób zamienić typ ErrorModel używany w
Urządzenie sieciowe. Chcielibyśmy również mieć możliwość ustawienia konfigurowalnych parametrów.

Oto kilka prostych wymagań, które rozważymy:

· Możliwość ustawienia zmiennej losowej, która zarządza stratami (domyślnie jest to UniformVariable)

· Możliwość ustawienia jednostki (bit, bajt, pakiet, czas) ziarnistości, nad którą są błędy
stosowany.

· Możliwość ustawienia współczynnika błędów (np. 10^-3) odpowiadającego powyższej jednostce
ziarnistość.

· Możliwość włączenia/wyłączenia (domyślnie włączona)

W jaki sposób do Podklasa
Deklarujemy, że BasicErrorModel jest podklasą ErrorModel w następujący sposób:

klasa BasicErrorModel: publiczny model błędu
{
publiczny:
statyczny TypeId GetTypeId (pusty);
...
prywatny:
// Zaimplementuj czysto wirtualne funkcje klasy bazowej
wirtualny bool DoCorrupt (Ptr P);
wirtualny bool DoReset (pusty);
...
}

i skonfiguruj funkcję GetTypeId podklasy, ustawiając unikalny ciąg TypeId i
ustawienie Rodzica na ErrorModel:

TypeId RateErrorModel::GetTypeId (void)
{
statyczny TypeId tid = TypeId („ns3::RateErrorModel”)
.Ustaw Rodzica ()
.Dodaj konstruktora ()
...

Buduj rdzeń Funkcje oraz Jednostka Testy
Twierdzić Makra
Pisanie Jednostka Testy
Dodawanie a Nowości Moduł do ns-3
Po utworzeniu grupy powiązanych klas, przykładów i testów mogą one być
połączone razem w ns-3 moduł, aby można było ich używać z istniejącymi ns-3 Moduły
oraz przez innych badaczy.

Ten rozdział przeprowadzi Cię przez kroki niezbędne do dodania nowego modułu ns-3.

Krok 0 - Moduł układ
Wszystkie moduły można znaleźć w src informator. Każdy moduł można znaleźć w katalogu
który ma taką samą nazwę jak moduł. Na przykład widmo moduł można znaleźć tutaj:
źródło/widmo. Będziemy cytować z widmo moduł dla ilustracji.

Prototypowy moduł ma następującą strukturę katalogów i wymagane pliki:

src /
Nazwa modułu/
wiązania/
doktor/
przykłady /
skrypt
pomocnik/
Model/
test/
przykłady-do-run.py
skrypt

Nie wszystkie katalogi będą obecne w każdym module.

Krok 1 - Stwórz a Moduł Szkielet
W katalogu źródłowym znajduje się program Pythona, który utworzy szkielet dla new
moduł. Na potrzeby tej dyskusji założymy, że twój nowy moduł nazywa się
nowy moduł. Od src katalog, wykonaj następujące czynności, aby utworzyć nowy moduł:

$ ./create-module.py nowy-moduł

Następnie cd najnowszych nowy moduł; znajdziesz ten układ katalogu:

$ cd nowy moduł
$ls
doc przykłady model pomocniczy test wscript

Bardziej szczegółowo, utwórz-moduł.py skrypt utworzy katalogi, a także plik initial
szkielet skrypt, .h, . DC oraz .pierwszy akta. Kompletny moduł ze szkieletem plików wygląda
lubię to:

src /
nowy moduł/
doktor/
nowy moduł.rst
przykłady /
nowy-moduł-przykład.cc
skrypt
pomocnik/
nowy-moduł-helper.cc
pomocnik nowego modułu.h
Model/
nowy-moduł.cc
nowy-moduł.h
test/
nowy-moduł-testowy-zestaw.cc
skrypt

(W razie potrzeby wiązania/ katalog wymieniony w Step-0 zostanie utworzony automatycznie podczas
budowa.)

Następnie omówimy, jak dostosować ten moduł. Informujący WAF o plikach które
uzupełnienie modułu odbywa się poprzez edycję dwóch skrypt akta. Przejdziemy przez
główne kroki w tym rozdziale.

Wszystkie produkty ns-3 moduły zależą od core module i zwykle na innych modułach. Ta zależność
jest określony w skrypt plik (na najwyższym poziomie modułu, a nie w oddzielnym pliku skrypt
Plik w przykłady informator!). W szkielecie skrypt wezwanie, które zadeklaruje twoje
nowy moduł do WAF będzie wyglądać tak (przed edycją):

def kompilacja(bld):
module = bld.create_ns3_module('nowy-moduł', ['rdzeń'])

Załóżmy, że nowy moduł zależy od Internet, mobilność, aodw moduły. Po
edytując to skrypt plik powinien wyglądać następująco:

def kompilacja(bld):
module = bld.create_ns3_module('nowy-moduł', ['internet', 'mobilność', 'aodv'])

Zauważ, że powinny być wymienione tylko zależności modułów pierwszego poziomu, dlatego usunęliśmy
core; Internet moduł z kolei zależy od core.

Twój moduł najprawdopodobniej będzie zawierał pliki źródłowe modelu. Początkowe szkielety (które będą
pomyślnie skompilować) są tworzone w model/nowy-moduł.cc oraz model/nowy-moduł.h.

Jeśli twój moduł będzie miał pomocnicze pliki źródłowe, przejdą one do pomocnik/
informator; ponownie w tym katalogu tworzone są początkowe szkielety.

Wreszcie dobrą praktyką jest pisanie testów i przykładów. Te prawie na pewno będą
wymagane, aby nowe moduły zostały przyjęte do oficjalnego ns-3 drzewo źródłowe. Szkielet
zestaw testów i przypadek testowy są tworzone w test/ informator. Zestaw testów szkieletowych będzie
zawierać poniższy konstruktor, który deklaruje nowy test jednostkowy o nazwie nowy moduł, Z
pojedynczy przypadek testowy składający się z klasy NowyModułTestCase1:

NowyModuleTestSuite::NowyModuleTestSuite ()
: TestSuite („nowy moduł”, JEDNOSTKA)
{
AddTestCase (nowy nowy modułTestCase1);
}

Krok 3 - Ogłosić Źródło Akta
Publiczne pliki nagłówkowe i kody źródłowe nowego modułu powinny być określone w pliku
skrypt plik, modyfikując go za pomocą edytora tekstu.

Na przykład po zadeklarowaniu widmo moduł, src/spektrum/wscript określa
pliki kodu źródłowego z następującą listą:

def kompilacja(bld):

module = bld.create_ns3_module('widmo', ['internet', 'propagacja', 'antena', 'aplikacje'])

źródło modułu = [
'model/model-widma.cc',
„model/wartość-widma.cc”,
.
.
.
„model/mikrofalówka-wartość-spektrum-pomocnik.cc”,
'pomocnik/pomocnik widma.cc',
'pomocnik/adhoc-aloha-noack-ideal-phy-helper.cc',
„pomocnik/generator przebiegów-pomocnik.cc”,
„pomoc/pomoc-analizatora widma.cc”,
]

Obiekty powstałe w wyniku kompilacji tych źródeł zostaną złożone w bibliotekę linków,
które zostaną połączone z dowolnymi programami opartymi na tym module.

Ale w jaki sposób takie programy uczą się publicznego API naszego nowego modułu? Czytaj!

Krok 4 - Ogłosić Publiczne Nagłówek Akta
Pliki nagłówkowe definiujące publiczny interfejs API twojego modelu i pomocników również powinny być
określone w skrypt plik.

Kontynuując z widmo ilustracji modelu, określono publiczne pliki nagłówkowe
z następną zwrotką. (Zauważ, że argument do bld mówi funkcja WAF do
zainstaluj nagłówki tego modułu z innymi ns-3 nagłówki):

nagłówki = bld(funkcje='ns3header')

headers.module = 'widmo'

źródło nagłówków = [
'model/model-spektrum.h',
„model/wartość-widma.h”,
.
.
.
„model/mikrofalówka-wartość-spektrum-pomocnik.h”,
'pomocnik/pomocnik widma.h',
'pomocnik/adhoc-aloha-noack-ideal-phy-helper.h',
'pomocnik/generator przebiegów-pomocnik.h',
'pomoc/pomoc-analizatora widma.h',
]

Nagłówki upublicznione w ten sposób będą dostępne dla użytkowników Twojego modelu z opcją include
stwierdzenia jak

#include "ns3/spectrum-model.h"

Nagłówki używane wyłącznie wewnętrznie w Twojej implementacji nie powinny być tutaj uwzględniane. Oni
są nadal dostępne dla twojej implementacji za pomocą instrukcji include, takich jak

#include "mój-moduł-implementacja.h"

Krok 5 - Ogłosić Testy
Jeśli twój nowy moduł ma testy, muszą być one określone w twoim skrypt plik przez
modyfikując go za pomocą edytora tekstu.

widmo testy modelowe są określone następującą zwrotką:

module_test = bld.create_ns3_module_test_library('spektrum')

moduł_test.źródło = [
'test/test-interferencji-widma.cc',
'test/test-wartosci-widma.cc',
]

See Testy aby uzyskać więcej informacji na temat pisania przypadków testowych.

Krok 6 - Ogłosić Przykłady
Jeśli twój nowy moduł ma przykłady, muszą być one określone w twoim przykłady/wscript
plik. (Szkielet na najwyższym poziomie skrypt będzie rekurencyjnie zawierać przykłady/wscript tylko, jeżeli
przykłady zostały włączone w czasie konfiguracji).

widmo model definiuje swój pierwszy przykład w src/spektrum/przyklady/wscript w

def kompilacja(bld):
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
[„widmo”, „mobilność”])
obj.source = 'adhoc-aloha-ideal-phy.cc'

Zauważ, że drugi argument funkcji utwórz_ns3_program() to lista modułów
od których zależy tworzony program; ponownie, nie zapomnij dołączyć nowy moduł in
Lista. Najlepszą praktyką jest wypisanie tylko bezpośrednich zależności modułów i niech WAF
wydedukować pełne drzewo zależności.

Czasami, dla jasności, możesz chcieć podzielić implementację dla swojego przykładu między
kilka plików źródłowych. W takim przypadku po prostu dołącz te pliki jako dodatkowe jawne
źródła przykładu:

obj = bld.create_ns3_program('przykład-nowego-modułu', [nowy-moduł])
obj.source = ['nowy-przyklad-modulu.cc', 'nowy-przyklad-modulu-czesc.cc']

Przykłady języka Python są określone przy użyciu następującego wywołania funkcji. Zauważ, że drugie
argument funkcji zarejestruj_ns3_skrypt() to lista modułów, które Python
przykład zależy od:

bld.register_ns3_script('nowy-moduł-przykład.py', ['nowy-moduł'])

Krok 7 - Przykłady Uruchom as Testy
Oprócz uruchamiania jawnego kodu testowego można również instrumentować platformę testową
uruchom pełne programy przykładowe, aby spróbować złapać regresje w przykładach. Jednak nie wszystkie
przykłady nadają się do testów regresji. Plik test/przykłady-do-uruchomienia.py kontroluje
wywołanie przykładów podczas uruchamiania środowiska testowego.

widmo modelowe przykłady prowadzone przez test.py są określone w
src/spectrum/test/examples-to-run.py przy użyciu następujących dwóch list C++ i Pythona
przykłady:

# Lista przykładów C++ do uruchomienia, aby upewnić się, że pozostaną
# możliwe do zbudowania i uruchomienia w czasie. Każda krotka na liście zawiera
#
# (nazwa_przykładu, do_run, do_valgrind_run).
#
# Zobacz test.py, aby uzyskać więcej informacji.
cpp_examples = [
(„adhoc-aloha-ideal-phy”, „Prawda”, „Prawda”),
(„adhoc-aloha-ideal-phy-with-microwave-piece”, „True”, „True”),
(„model adhoc-aloha-ideal-phy-matrix-propagation-loss-model”, „Prawda”, „Prawda”),
]

# Lista przykładów Pythona do uruchomienia, aby upewnić się, że pozostaną
# możliwe do uruchomienia w czasie. Każda krotka na liście zawiera
#
# (nazwa_przykładu, wykonaj_uruchom).
#
# Zobacz test.py, aby uzyskać więcej informacji.
przykłady_pythona = [
("przykład-symulator.py", "Prawda"),
]

Jak wskazano w komentarzu, każdy wpis na liście przykładów C++ do uruchomienia zawiera plik
krotka (nazwa_przykładu, wykonaj_uruchom, do_valgrind_run), Gdzie

· nazwa_przykładu jest plikiem wykonywalnym do uruchomienia,

· wykonaj_uruchom jest warunkiem uruchomienia przykładu i

· do_valgrind_run jest warunkiem uruchomienia przykładu pod valgrind. (Ten
jest potrzebny, ponieważ NSC powoduje awarie nielegalnych instrukcji z niektórymi testami, gdy one
są uruchamiane pod valgrind.)

Zauważ, że te dwa warunki to instrukcje Pythona, od których mogą zależeć WAF konfiguracja
zmienne. Na przykład,

("tcp-nsc-lfn", "NSC_ENABLED == prawda", "NSC_ENABLED == fałsz"),

Każdy wpis na liście przykładów Pythona do uruchomienia zawiera krotkę (nazwa_przykładu,
wykonaj_uruchom), gdzie, podobnie jak w przykładach C++,

· nazwa_przykładu to skrypt Pythona, który ma zostać uruchomiony, oraz

· wykonaj_uruchom jest warunkiem uruchomienia przykładu.

Ponownie, warunkiem jest instrukcja Pythona, od której może zależeć WAF zmienne konfiguracyjne.
Na przykład,

("realtime-udp-echo.py", "ENABLE_REAL_TIME == Fałsz"),

Krok 8 - Konfigurowanie oraz Buduj
Możesz teraz normalnie konfigurować, budować i testować swój moduł. Musisz ponownie skonfigurować
projekt jako pierwszy krok WAF buforuje nowe informacje w twoim skrypt pliki lub
w przeciwnym razie twój nowy moduł nie zostanie uwzględniony w kompilacji.

$ ./waf konfiguracji --enable-examples --enable-tests
Budowa $ ./waf
$ ./test.py

Poszukaj zestawu testów nowego modułu (i przykładowych programów, jeśli moduł je posiada
włączone) w wyjściu testowym.

Krok 9 - Python Wiązania
Dodanie powiązań Pythona do modułu jest opcjonalne, a krok jest komentowany przez
domyślnie w utwórz-moduł.py skrypt.

# bld.ns3_python_bindings()

Jeśli chcesz dołączyć powiązania Pythona (potrzebne tylko, jeśli chcesz napisać Python ns-3
programy zamiast programów C++ ns-3), należy odkomentować powyższe i zainstalować
systemu skanowania API Pythona (omówionego w innym miejscu tego podręcznika) i przeskanuj swój moduł do
wygenerować nowe powiązania.

Tworzenie Dokumenty
ns-3 dostarcza dwa rodzaje dokumentacji: ekspozycyjne rozdziały w stylu „przewodnika użytkownika” oraz
dokumentacja API kodu źródłowego.

Rozdziały „podręcznika użytkownika” są pisane ręcznie reStructuredText format (.pierwszy), który jest
przetwarzane przez system dokumentacji Pythona Sfinks do generowania stron internetowych i plików pdf.
Dokumentacja API jest generowana z samego kodu źródłowego przy użyciu doxygen, generować
usieciowane strony internetowe. Oba są ważne: rozdziały Sfinksa wyjaśniają dlaczego
i przegląd korzystania z modelu; dokumentacja API wyjaśnia w jaki sposób detale.

Ten rozdział zawiera krótki przegląd tych narzędzi, podkreślając preferowane użycie i
dostosowania dla ns-3.

Aby zbudować całą standardową dokumentację:

$ ./waf dokumenty

Aby uzyskać więcej specjalistycznych opcji, czytaj dalej.

Dokumentowanie w Sfinks
Używamy pliki Sfinks wygenerować rozdziały ekspozycyjne opisujące projekt i zastosowanie każdego z nich
moduł. W tej chwili czytasz tzw Dokumenty Rozdział. The Pokazać Źródło ogniwem
pasek boczny pokaże źródło reStructuredText dla tego rozdziału.

Dodawanie Nowości rozdziały
Dodanie nowego rozdziału składa się z trzech kroków (opisanych bardziej szczegółowo poniżej):

1. Wybierać Gdzie? pliki dokumentacji będą aktywne.

2. Połączyć z istniejącej strony do nowej dokumentacji.

3. Dodaj nowy plik do Makefile.

Gdzie?
Dokumentacja dla konkretnego modułu, bla, powinien normalnie wejść src/foo/doc/. Na przykład
src/foo/doc/foo.rst byłby dokumentem najwyższego poziomu dla modułu. The
src/create-module.py skrypt utworzy ten plik za Ciebie.

Niektóre modele wymagają kilku .pierwszy akta i figury; to wszystko powinno iść w
src/foo/doc/ informator. Dokumenty są faktycznie budowane przez Sphinx Makefile. Specjalnie dla
zaangażowanej dokumentacji, pomocne może być posiadanie lokalnego Makefile src/foo/doc/
katalog, aby uprościć tworzenie dokumentacji dla tego modułu (Antena jest przykładem).
Konfiguracja nie jest szczególnie trudna, ale wykracza poza zakres tego rozdziału.

W niektórych przypadkach dokumentacja obejmuje wiele modeli; the Sieć rozdział jest przykładem. W
te przypadki dodając .pierwszy pliki bezpośrednio do dokument/modele/źródło/ może być odpowiedni.

Połączyć
Sfinks musi wiedzieć gdzie powinien pojawić się twój nowy rozdział. W większości przypadków nowy model
rozdział powinien pojawić się w modele książka. Aby dodać tam swój rozdział, edytuj
doc/models/source/index.rst

...drzewo::
:maksymalna głębokość: 1

organizacja
animacja
antena
aodw
aplikacje
...

Dodaj nazwę swojego dokumentu (bez rozszerzenia .pierwszy rozszerzenie) do tej listy. Proszę zachować
Modeluj rozdziały w porządku alfabetycznym, aby ułatwić wizualne wyszukiwanie określonych rozdziałów.

Makefile
Musisz także dodać swój dokument do odpowiedniego Makefile, więc robić wie, żeby to sprawdzić
dla aktualizacji. Książka Modele Makefile jest doc/models/Makefile, instrukcja obsługi Makefile jest
doc/manual/Makefile.

# wyświetla listę wszystkich plików .rst biblioteki modeli, które należy skopiować do $SOURCETEMP
ŹRÓDŁA = \
źródło/konf.py \
źródło/_statyczne \
źródło/indeks.rst \
źródło/zamień.txt \
źródło/organizacja.rst \
...
$(SRC)/antena/doc/source/antena.rst \
...

Dodajesz swoje .pierwszy pliki do ŹRÓDŁA zmienny. Aby dodać liczby, przeczytaj komentarze w
Makefile aby zobaczyć, która zmienna powinna zawierać pliki obrazów. Jeszcze raz proszę, zachowaj je
w porządku alfabetycznym.

Budowanie Sfinks Docs
Tworzenie dokumentacji Sphinxa jest dość proste. Zbudować całego Sfinksa
dokumentacja:

$ ./waf sfinks

Aby zbudować tylko dokumentację modeli:

$ make -C dokument/modele

Aby zobaczyć wygenerowaną dokumentację, skieruj swoją przeglądarkę na doc/models/build/html.

Jak widać, Sphinx używa Make do kierowania procesem. Domyślny cel buduje wszystko
włączone formularze wyjściowe, które w ns-3 są wielostronicowe html, pojedyncza strona pojedynczyhtml,
PDF(lateks). Aby zbudować tylko wielostronicowy html, dodajesz plik html cel:

$ make -C dokument/modele html

Może to być pomocne w skróceniu czasu kompilacji (i rozmiaru gadania o kompilacji), tak jak ty
piszesz swój rozdział

Przed przekazaniem dokumentacji do repozytorium sprawdź, czy kompiluje się bez
błędy lub ostrzeżenia. Proces kompilacji generuje wiele danych wyjściowych (głównie normalnego gadania
z LaTeX), co może utrudniać sprawdzenie, czy są jakieś ostrzeżenia Sphinx lub
błędy. Aby znaleźć ważne ostrzeżenia i błędy, zbuduj tylko plik html wersję, a następnie wyszukaj
dziennik budowy dla ostrzeżenie or błąd.

ns-3 specyfika
Sfinks dokumentacja oraz Tutorial są całkiem dobre. Nie będziemy powielać podstaw
tutaj, zamiast skupiać się na preferowanym użyciu dla ns-3.

· Rozpocznij dokumenty od tych dwóch linii:

.. zawiera:: zastąp.txt
.. zaznacz:: cpp

Pierwsza linia umożliwia kilka prostych zamian. Na przykład pisanie |ns3| renderuje jako
ns-3. Drugi ustawia domyślny język podświetlania kodu źródłowego jawnie dla
plik, ponieważ zgadywanie parsera nie zawsze jest dokładne. (Możliwe jest również ustawienie tzw
język jawnie dla pojedynczego bloku kodu, patrz poniżej).

· Sekcje:

Sphinx jest dość liberalny w kwestii oznaczania nagłówków sekcji. Zgodnie z konwencją wolimy to
hierarchia:

.. hierarchia nagłówków:
------------- Rozdział
************* Sekcja (#.#)
============= Podsekcja (#.#.#)
############# Podsekcja

· Podświetlanie składni:

Aby użyć domyślnego wyróżnienia składni, po prostu uruchom blok kodu źródłowego:

┌─────────────────────────────────────── ───────┬── ───────────────────────────────┐
│Źródło Sphinx │ Wyrenderowany wynik │
├─────────────────────────────────────── ───────┼── ───────────────────────────────┤
│ │ Frobnitz jest dostępny przez: │
│ Dostęp do ``Frobnitza`` mają: │ │
│ │ Foo::Frobnitz żaba; │
│ Foo::Frobnitz frob; │ frob.Zestaw (...); │
│ frob.Zestaw (...); │ │
└─────────────────────────────────────── ───────┴── ───────────────────────────────┘

Aby użyć określonego wyróżnienia składni, na przykład bash polecenia powłoki:

┌─────────────────────────────────┬───── ────────── ───┐
│Źródło Sphinx │ Wyrenderowany wynik │
├─────────────────────────────────┼───── ────────── ───┤
│ │
│ .. kod źródłowy:: bash │ $ ls │
│ │
│ $ ls │ │
└─────────────────────────────────┴───── ────────── ───┘

· Zapisy skrócone:

Te skróty są zdefiniowane:

┌────────────────────────┬────────────── ───┐
│Źródło Sphinx │ Wyrenderowany wynik │
├────────────────────────┼────────────── ───┤
│ │. ns-3
│ |ns3| │ │
├────────────────────────┼────────────── ───┤
│ │. ns-2
│ |ns2| │ │
├────────────────────────┼────────────── ───┤
│ │
│ |sprawdzić| │ │
├────────────────────────┼────────────── ───┤
│ │. RFC 6282
│ :rfc:`6282` │ │
└────────────────────────┴────────────── ───┘

Dokumentowanie w doxygen
Używamy pliki doxygen generować do przeglądania Dokumentacja API. Doxygen zapewnia szereg
przydatne funkcje:

· Tabela podsumowująca wszystkich członków klasy.

· Wykresy dziedziczenia i współpracy dla wszystkich klas.

· Linki do kodu źródłowego realizującego każdą funkcję.

· Linki do każdego miejsca, w którym członek jest używany.

· Linki do każdego obiektu użytego w realizacji funkcji.

· Grupowanie powiązanych klas, takich jak wszystkie klasy związane z określonym protokołem.

Ponadto używamy tzw TypId system do dodania do dokumentacji dla każdej klasy

· Config ścieżki, którymi można dotrzeć do takich obiektów.

· Dokumentacja dla każdego Atrybuty, w tym Atrybuty zdefiniowane w klasach nadrzędnych.

· Dokumentacja dla każdego Wyśledzić źródła zdefiniowane przez klasę.

Doxygen działa na zasadzie skanowania kodu źródłowego w poszukiwaniu specjalnie oznaczonych komentarzy. To
tworzy również odsyłacz wskazujący gdzie każdy plik, klasa, metoda i zmienna jest
używany.

preferowane Styl
Preferowanym stylem komentarzy Doxygen jest styl JavaDoc:

/ **
* Krótki opis tej klasy lub metody.
* Sąsiednie wiersze stają się pojedynczym akapitem.
*
* Dłuższy opis, z dużą ilością szczegółów.
*
* Puste linie oddzielają akapity.
*
* Wyjaśnij, co robi klasa lub metoda, używając jakiego algorytmu.
* Wyjaśnij jednostki argumentów i zwracane wartości.
*
* \note Zwróć uwagę na wszelkie ograniczenia lub problemy.
*
* (Dla funkcji z argumentami lub wartościami zwracanymi :)
* \param foo Krótkie wyrażenie rzeczownikowe opisujące ten argument.
* \param bar Uwaga Wielkość zdania i kropka kończąca.
* \return Krótkie wyrażenie rzeczownikowe opisujące wartość.
*
* \wewnętrzny
*
* Możesz także omówić wewnętrzne szczegóły implementacji.
* Zrozumienie tego materiału nie powinno być konieczne do używania
* klasa lub metoda.
*/
klasa Przykład

W tym stylu blok komentarza Doxygen zaczyna się od dwóch znaków `*': / **i poprzedza
przedmiot jest dokumentowany.

W przypadku pozycji wymagających tylko krótkiego opisu odpowiednia jest jedna z tych krótkich form:

/** Implementacja destruktora. */
nieważne Wykonaj ();

int m_liczba; //!< Liczba ...

Zwróć uwagę na specjalną formę komentarza końca wiersza, //!, wskazując, że odnosi się do
poprzedzający pozycja.

Niektóre elementy do zapamiętania:

· Użyj przypadku zdania, w tym kapitału początkowego.

· Używaj znaków interpunkcyjnych, zwłaszcza `.'s na końcu zdań lub fraz.

· \krótki tag nie jest potrzebny; pierwsze zdanie zostanie użyte jako podsumowanie
opis.

Każda klasa, metoda, definicja typu, zmienna składowa, argument funkcji i wartość zwracana powinny
być udokumentowane we wszystkich plikach kodu źródłowego, które tworzą formalne API i implementację
ns-3, Takie jak źródło/ /Model/*, źródło/ /pomocnik/* oraz źródło/ /narzędzia/*.
Dokumentacja pozycji w źródło/ /test/* oraz źródło/ /przykłady/* jest preferowany,
ale nie wymagane.

przydatny Funkcje
· Odziedziczeni członkowie będą automatycznie dziedziczyć dokumenty od rodzica (ale można ich zastąpić
z lokalnej dokumentacji).

1. Udokumentuj klasę bazową.

2. W podklasie funkcje dziedziczone zaznaczamy zwykłym komentarzem:

// Metody dziedziczone
wirtualna pustka FooBar (pustka);
virtual int BarFoo (podwójna baza);

Pamiętaj, że podpisy muszą dokładnie pasować, więc dołącz argument formalny (próżnia)

To nie działa w przypadku funkcji statycznych; Widzieć PobierzTypeId, poniżej, dla przykładu.

Budowanie doxygen Docs
Tworzenie dokumentacji Doxygen jest dość proste:

$ ./waf tlen

Kompiluje to przy użyciu domyślnej konfiguracji, która generuje sekcje dokumentacji dla
cała kolekcja elementy, nawet jeśli nie mają wyraźnych bloków dokumentacji komentarzy. To ma
efekt pomijania ostrzeżeń o nieudokumentowanych przedmiotach, ale upewnia się, że wszystko się pojawia
w wygenerowanym wyjściu.

Podczas pisania dokumentacji często bardziej przydatne jest sprawdzenie, które elementy są generowane
ostrzeżenia, zwykle o brakującej dokumentacji. Aby wyświetlić pełną listę ostrzeżeń, użyj pliku
doc/doxygen.warnings.report.sh scenariusz:

$ doc/doxygen.warnings.report.sh
Waf: Wejście do katalogu `build'
...
Waf: opuszczanie katalogu `build'
„Budowanie” zakończyło się pomyślnie (3m24.094s)

Przebudowywanie dokumentów doxygen z pełnymi błędami... Gotowe.

Raport ostrzeżeń Doxygen
----------------------------------------

(Wszystkie liczby są dolnymi granicami).

Ostrzeżenia według modułu/katalogu:

Katalog zliczania
----- ----------------------------------
3844 źródło/lte/model
1718 źródło/wimax/model
1423 źródło/rdzeń/model
....
138 dodatkowych nieudokumentowanych parametrów.
----------------------------------------
Łącznie 15765 ostrzeżeń
126 katalogów z ostrzeżeniami

Ostrzeżenia według plików (alfabetycznie)

Plik zliczania
----- ----------------------------------
17 doc/introspected-doxygen.h
15 przykładów/routing/manet-routing-compare.cc
26 przyklady/statystyki/wifi-przykladowe-aplikacje.h
....
----------------------------------------
967 plików z ostrzeżeniami

Ostrzeżenia według plików (liczbowo)

Plik zliczania
----- ----------------------------------
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 plików z ostrzeżeniami

Podsumowanie ostrzeżeń Doxygen
----------------------------------------
126 katalogów
Pliki 967
15765 ostrzeżenia

Skrypt modyfikuje konfigurację, aby wyświetlić wszystkie ostrzeżenia i skrócić czas działania.
Jak widać, przy tym piśmie mamy a los nieudokumentowanych przedmiotów. Raport
podsumowuje ostrzeżenia według modułu źródło/*/*i według plików, w porządku alfabetycznym i numerycznym.

Skrypt ma kilka opcji, aby ograniczyć rzeczy i uczynić to łatwiejszym do zarządzania. O pomoc,
użyć -h opcja. Po uruchomieniu go raz, aby wykonać kompilację Doxygen i wygenerować pełną
dziennika ostrzeżeń, możesz ponownie przetworzyć plik dziennika za pomocą różnych „filtrów” bez konieczności robienia tego
pełną wersję Doxygen, ponownie używając -s opcja. Możesz wykluczyć ostrzeżenia z
*/przykłady/* pliki (-e opcja) i/lub */test/* pliki (-t).

Być może najbardziej przydatną opcją podczas pisania komentarzy do dokumentacji jest -m , który
ograniczy raport tylko do pasujących plików źródło/ /*i postępuj zgodnie z raportem za pomocą
rzeczywiste linie ostrzegawcze. Łączą się z -and i możesz skupić się na ostrzeżeniach, które są
najpilniejsze w jednym module:

$ doc/doxygen.warnings.report.sh -m siatka/pomocnik
...
Podsumowanie ostrzeżeń Doxygen
----------------------------------------
1 katalogów
Pliki 3
149 ostrzeżenia

Filtrowane ostrzeżenia
====================================
src/mesh/helper/dot11s/dot11s-installer.h:72: ostrzeżenie: Składnik m_root (zmienna) klasy ns3::Dot11sStack nie jest udokumentowany.
src/mesh/helper/dot11s/dot11s-installer.h:35: ostrzeżenie: zwracany typ członka ns3::Dot11sStack::GetTypeId nie jest udokumentowany
src/mesh/helper/dot11s/dot11s-installer.h:56: ostrzeżenie: zwracany typ członka ns3::Dot11sStack::InstallStack nie jest udokumentowany
src/mesh/helper/flame/lfame-installer.h:40: ostrzeżenie: Member GetTypeId() (funkcja) klasy ns3::FlameStack nie jest udokumentowana.
src/mesh/helper/flame/flame-installer.h:60: ostrzeżenie: zwracany typ członka ns3::FlameStack::InstallStack nie jest udokumentowany
src/mesh/helper/mesh-helper.h:213: ostrzeżenie: Element m_nInterfaces (zmienna) klasy ns3::MeshHelper nie jest udokumentowany.
src/mesh/helper/mesh-helper.h:214: ostrzeżenie: Członek m_spreadChannelPolicy (zmienna) klasy ns3::MeshHelper nie jest udokumentowany.
src/mesh/helper/mesh-helper.h:215: ostrzeżenie: Składnik m_stack (zmienna) klasy ns3::MeshHelper nie jest udokumentowany.
src/mesh/helper/mesh-helper.h:216: ostrzeżenie: Członek m_stackFactory (zmienna) klasy ns3::MeshHelper nie jest udokumentowany.
src/mesh/helper/mesh-helper.h:209: ostrzeżenie: parametry członka ns3::MeshHelper::CreateInterface nie są (wszystkie) udokumentowane
src/mesh/helper/mesh-helper.h:119: ostrzeżenie: parametry członka ns3::MeshHelper::SetStandard nie są (wszystkie) udokumentowane

Teraz to tylko kwestia zrozumienia kodu i napisania kilku dokumentów!

ns-3 specyfika
Jeśli chodzi o Sfinksa, Doxygena docs oraz odniesienie są całkiem dobre. Nie będziemy dublować
podstaw tutaj, zamiast skupiać się na preferowanym użyciu dla ns-3.

· Użyj Doxygena Moduły do grupowania powiązanych elementów.

W głównym nagłówku modułu utwórz grupę Doxgyen:

/ **
* \defgroup foo Protokół Foo.
*/

Zaznacz każdą powiązaną klasę jako należącą do grupy:

/ **
* \ingroup foo
*
* Typ pakietu Foo.
*/
klasa Foo

· Czy wiedziałeś definicje typów może mieć argumenty formalne? Umożliwia to dokumentację funkcji
sygnatury wskaźnika:

/ **
* Sygnatura funkcji wywołania zwrotnego paska.
*
* \param ale Wielkość kufla piwa w uncjach imperialnych.
*/
typedef void (* BarCallback)(const int ale);

· Skopiuj Atrybut ciągi pomocy z PobierzTypeId metoda do wykorzystania jako brief
opisy członków stowarzyszonych.

· \błąd{298} utworzy link do błędu 298 w naszej Bugzilli.

· \pnazwa{foo} w opisie zostanie sformatowany bla jak \param bla parametr, wyjaśniając to
że odwołujesz się do rzeczywistego argumentu.

· \RFC{301} utworzy łącze do RFC 301.

· \wewnętrzny powinien być używany tylko do rozpoczynania dyskusji o szczegółach implementacji, a nie do
znak prywatny funkcje (są już zaznaczone, np prywatny!)

· Nie twórz klas o trywialnych nazwach, takich jak klasa A, nawet w zestawach testowych. Te
powoduje, że wszystkie wystąpienia literału nazwy klasy `A' będą renderowane jako dowiązania.

Jak wspomniano powyżej, funkcje statyczne nie dziedziczą dokumentacji tych samych funkcji w
klasa rodzicielska. ns-3 wszędzie używa kilku funkcji statycznych; sugerowane
blok dokumentacji dla tych przypadków to:

· Domyślny konstruktor/destruktor:

Moja klasa (); //!< Konstruktor domyślny
~MojaKlasa(); //!< Destruktor

· Fikcyjny destruktor i DoDispose:

/** Fikcyjny destruktor, zobacz DoDispose. */
~MojaKlasa();

/** Implementacja destruktora */
wirtualna pustka DoDispose ();

· GetTypeId:

/ **
* Zarejestruj ten typ.
* \return Obiekt TypeId.
*/
statyczny TypeId GetTypeId (pusty);

Włączanie Podzbiory of ns-3 Moduły
Podobnie jak w przypadku większości projektów oprogramowania, ns-3 jest coraz większy pod względem liczby modułów,
linii kodu i śladu pamięci. Użytkownicy mogą jednak korzystać tylko z kilku z tych modułów
na czas. Z tego powodu użytkownicy mogą chcieć jawnie włączyć tylko podzbiór
możliwy ns-3 modułów, których faktycznie potrzebują do swoich badań.

W tym rozdziale omówiono sposób włączania tylko ns-3 interesujące Cię moduły
za pomocą.

W jaki sposób do umożliwiać a podzbiór of ns-3's Moduły
Jeśli budowane są biblioteki współdzielone, włączenie modułu spowoduje powstanie co najmniej jednego
biblioteka do zbudowania:

libns3-nazwamodułu.so

Jeśli moduł ma bibliotekę testową i biblioteki testowe są budowane, to

libns3-nazwa modułu-test.so

też powstanie. Inne moduły, od których zależy moduł i ich biblioteki testowe
też zostanie zbudowany.

Domyślnie wszystkie moduły są wbudowane ns-3. Istnieją dwa sposoby włączenia ich podzbioru
moduły:

1. Używając opcji --enable-modules wafa

2. Używając ns-3 Plik konfiguracyjny

umożliwiać Moduły za pomocą wafy --włącz moduły opcja
Aby włączyć tylko moduł podstawowy z przykładami i testami, wypróbuj następujące polecenia:

$ ./waf czyste
$ ./waf konfiguracji --enable-examples --enable-tests --enable-modules=core
Budowa $ ./waf
$ kompilacja/debugowanie płyty CD/
$ls

i powinny być obecne następujące biblioteki:

powiązania libns3-core.so narzędzia do zarysowania ns3
przykłady libns3-core-test.so próbki src

Zanotuj ./waff kleń krok jest wykonywany tutaj tylko po to, aby było bardziej oczywiste, które biblioteki modułów
zostały zbudowane. Nie musisz robić ./waff kleń w celu umożliwienia podzbiorów modułów.

Uruchomienie test.py spowoduje uruchomienie tylko tych testów, które zależą od rdzenia modułu:

24 z 24 testów zaliczonych (24 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Powtórz powyższe kroki dla modułu „sieciowego” zamiast modułu „podstawowego”.
zostaną zbudowane następujące, ponieważ sieć zależy od rdzenia:

powiązania libns3-core.so libns3-network.so narzędzia do zarysowania ns3
przykłady libns3-core-test.so libns3-network-test.so próbki src

Uruchomienie test.py spowoduje, że te testy, które zależą tylko od modułów podstawowych i sieciowych, zostaną wykonane
Być uruchomiony:

31 z 31 testów zaliczonych (31 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

umożliwiać Moduły za pomocą dotychczasowy ns-3 konfiguracja filet
Dodano plik konfiguracyjny .ns3rc ns-3 który pozwala użytkownikom określić, które
moduły mają być zawarte w pliku build.

Podczas włączania podzbioru ns-3 modułów, zasady pierwszeństwa są następujące:

1. ciąg konfiguracyjny --enable-modules zastępuje dowolny plik .ns3rc

2. plik .ns3rc na najwyższym poziomie ns-3 Następnie sprawdzany jest katalog, jeśli jest obecny

3. system wyszuka ~/.ns3rc jeśli powyższe dwa są nieokreślone

Jeśli żadne z powyższych nie ogranicza budowanych modułów, wszystkie moduły, o których wie waf, będą
Być zbudowany.

Obsługiwana wersja pliku .ns3rc w formacie ns-3 repozytorium kodu źródłowego znajduje się w
dotychczasowy utils informator. Powodem tego jest to, że znajdował się w katalogu najwyższego poziomu pliku
repozytorium, byłoby podatne na przypadkowe sprawdzanie przez opiekunów, którzy umożliwiają
modułów, z których chcą korzystać. Dlatego użytkownicy muszą ręcznie skopiować plik .ns3rc z pliku
utils katalog do preferowanego miejsca (katalog najwyższego poziomu lub katalog domowy) do
włączyć trwałą konfigurację modułową.

Zakładając, że jesteś na najwyższym poziomie ns-3 katalogu, możesz uzyskać kopię pliku .ns3rc
plik znajdujący się w utils katalog w następujący sposób:

$cp utils/.ns3rc .

Plik .ns3rc powinien teraz znajdować się na najwyższym poziomie ns-3 katalog, w którym znajduje się plik
Następujące:

#! /usr/bin/env pyton

# Lista modułów, które zostaną włączone po uruchomieniu ns-3.
# Moduły zależne od wymienionych modułów również zostaną włączone.
#
# Wszystkie moduły można włączyć, wybierając „all_modules”.
moduły włączone = ['wszystkie_moduły']

# Ustaw to na true, jeśli chcesz, aby przykłady były uruchamiane.
Examples_enabled = Fałsz

# Ustaw to na true, jeśli chcesz uruchomić testy.
testing_enabled = Fałsz

Użyj swojego ulubionego edytora, aby zmodyfikować plik .ns3rc, aby włączyć tylko moduł podstawowy
takie przykłady i testy:

#! /usr/bin/env pyton

# Lista modułów, które zostaną włączone po uruchomieniu ns-3.
# Moduły zależne od wymienionych modułów również zostaną włączone.
#
# Wszystkie moduły można włączyć, wybierając „all_modules”.
moduły włączone = ['rdzeń']

# Ustaw to na true, jeśli chcesz, aby przykłady były uruchamiane.
Examples_enabled = Prawda

# Ustaw to na true, jeśli chcesz uruchomić testy.
testing_enabled = Prawda

Tylko moduł podstawowy zostanie teraz włączony, jeśli wypróbujesz te polecenia:

$ ./waf czyste
Konfiguracja $ ./waf
Budowa $ ./waf
$ kompilacja/debugowanie płyty CD/
$ls

i powinny być obecne następujące biblioteki:

powiązania libns3-core.so narzędzia do zarysowania ns3
przykłady libns3-core-test.so próbki src

Zanotuj ./waff kleń krok jest wykonywany tutaj tylko po to, aby było bardziej oczywiste, które biblioteki modułów
zostały zbudowane. Nie musisz robić ./waff kleń w celu umożliwienia podzbiorów modułów.

Uruchomienie test.py spowoduje uruchomienie tylko tych testów, które zależą od rdzenia modułu:

24 z 24 testów zaliczonych (24 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Powtórz powyższe kroki dla modułu „sieciowego” zamiast modułu „podstawowego”.
zostaną zbudowane następujące, ponieważ sieć zależy od rdzenia:

powiązania libns3-core.so libns3-network.so narzędzia do zarysowania ns3
przykłady libns3-core-test.so libns3-network-test.so próbki src

Uruchomienie test.py spowoduje, że te testy, które zależą tylko od modułów podstawowych i sieciowych, zostaną wykonane
Być uruchomiony:

31 z 31 testów zaliczonych (31 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Włączanie/wyłączanie ns-3 Testy oraz Przykłady
ns-3 dystrybucja zawiera wiele przykładów i testów, które służą do sprawdzania poprawności ns-3
system. Jednak użytkownicy mogą nie zawsze chcieć, aby te przykłady i testy były uruchamiane dla nich
instalacja ns-3.

W tym rozdziale omówiono sposób budowania ns-3 z przykładami i testami lub bez nich.

W jaki sposób do włącz / wyłącz przykłady oraz Testy in ns-3
Istnieją 3 sposoby włączania/wyłączania przykładów i testów w ns-3:

1. Używanie build.py kiedy ns-3 budowany jest po raz pierwszy

2. Jednokrotne użycie wafa ns-3 został wybudowany

3. Używając ns-3 plik konfiguracyjny raz ns-3 został wybudowany

Włącz / wyłącz przykłady oraz Testy za pomocą kompilacja.py
Możesz użyć build.py, aby włączyć/wyłączyć przykłady i testy, kiedy ns-3 jest zbudowany dla pierwszego
czas.

Domyślnie przykłady i testy nie są wbudowane ns-3.

Z katalogu ns-3-allinone możesz zbudować ns-3 bez żadnych przykładów lub testów po prostu
wykonując:

$ ./build.py

Uruchamianie test.py na najwyższym poziomie ns-3 katalog teraz nie spowoduje żadnych przykładów ani testów
biegać:

0 z 0 testów zaliczonych (0 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Jeśli chcesz budować ns-3 z przykładami i testami, a następnie wykonaj następujące czynności z
katalog ns-3-allinone:

$ ./build.py --enable-examples --enable-tests

Uruchamianie test.py na najwyższym poziomie ns-3 katalog spowoduje wszystkie przykłady i testy
do uruchomienia:

170 z 170 testów zaliczonych (170 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Włącz / wyłącz przykłady oraz Testy za pomocą WAF
Możesz użyć waf, aby raz włączyć/wyłączyć przykłady i testy ns-3 został wybudowany.

Domyślnie przykłady i testy nie są wbudowane ns-3.

Z najwyższego poziomu ns-3 katalog, który możesz zbudować ns-3 bez żadnych przykładów lub testów po prostu
wykonując:

Konfiguracja $ ./waf
Budowa $ ./waf

Uruchomienie test.py teraz nie spowoduje uruchomienia żadnych przykładów ani testów:

0 z 0 testów zaliczonych (0 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Jeśli chcesz budować ns-3 z przykładami i testami, a następnie wykonaj następujące czynności od góry
poziom ns-3 katalog:

$ ./waf konfiguracji --enable-examples --enable-tests
Budowa $ ./waf

Uruchomienie test.py spowoduje uruchomienie wszystkich przykładów i testów:

170 z 170 testów zaliczonych (170 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Włącz / wyłącz przykłady oraz Testy za pomocą dotychczasowy ns-3 konfiguracja filet
Dodano plik konfiguracyjny .ns3rc ns-3 który pozwala użytkownikom określić, czy
przykłady i testy powinny być budowane lub nie. Możesz użyć tego pliku, aby włączyć/wyłączyć
przykłady i testy raz ns-3 został wybudowany.

Podczas włączania wyłączania przykładów i testów zasady pierwszeństwa są następujące:

1. ciągi konfiguracyjne --enable-examples/--disable-examples zastępują dowolny plik .ns3rc

2. ciągi konfiguracyjne --enable-tests/--disable-tests zastępują dowolny plik .ns3rc

3. plik .ns3rc na najwyższym poziomie ns-3 Następnie sprawdzany jest katalog, jeśli jest obecny

4. system wyszuka ~/.ns3rc jeśli plik .ns3rc nie został znaleziony w poprzednim kroku

Jeśli żadne z powyższych nie istnieje, to przykłady i testy nie zostaną zbudowane.

Obsługiwana wersja pliku .ns3rc w formacie ns-3 repozytorium kodu źródłowego znajduje się w
dotychczasowy utils informator. Powodem tego jest to, że znajdował się w katalogu najwyższego poziomu pliku
repozytorium, byłoby podatne na przypadkowe sprawdzanie przez opiekunów, którzy umożliwiają
modułów, z których chcą korzystać. Dlatego użytkownicy muszą ręcznie skopiować plik .ns3rc z pliku
utils katalog do preferowanego miejsca (katalog najwyższego poziomu lub katalog domowy) do
umożliwiają trwałe włączanie przykładów i testów.

Zakładając, że jesteś na najwyższym poziomie ns-3 katalogu, możesz uzyskać kopię pliku .ns3rc
plik znajdujący się w utils katalog w następujący sposób:

$cp utils/.ns3rc .

Plik .ns3rc powinien teraz znajdować się na najwyższym poziomie ns-3 katalog, w którym znajduje się plik
Następujące:

#! /usr/bin/env pyton

# Lista modułów, które zostaną włączone po uruchomieniu ns-3.
# Moduły zależne od wymienionych modułów również zostaną włączone.
#
# Wszystkie moduły można włączyć, wybierając „all_modules”.
moduły włączone = ['wszystkie_moduły']

# Ustaw to na true, jeśli chcesz, aby przykłady były uruchamiane.
Examples_enabled = Fałsz

# Ustaw to na true, jeśli chcesz uruchomić testy.
testing_enabled = Fałsz

Z najwyższego poziomu ns-3 katalog, który możesz zbudować ns-3 bez żadnych przykładów lub testów po prostu
wykonując:

Konfiguracja $ ./waf
Budowa $ ./waf

Uruchomienie test.py teraz nie spowoduje uruchomienia żadnych przykładów ani testów:

0 z 0 testów zaliczonych (0 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Jeśli chcesz budować ns-3 z przykładami i testami, użyj swojego ulubionego edytora do zmian
wartości w pliku .ns3rc dla pliku Examples_enabled i tests_enabled mają wartość True:

#! /usr/bin/env pyton

# Lista modułów, które zostaną włączone po uruchomieniu ns-3.
# Moduły zależne od wymienionych modułów również zostaną włączone.
#
# Wszystkie moduły można włączyć, wybierając „all_modules”.
moduły włączone = ['wszystkie_moduły']

# Ustaw to na true, jeśli chcesz, aby przykłady były uruchamiane.
Examples_enabled = Prawda

# Ustaw to na true, jeśli chcesz uruchomić testy.
testing_enabled = Prawda

Z najwyższego poziomu ns-3 katalog, który możesz zbudować ns-3 z przykładami i testami po prostu przez
czyn:

Konfiguracja $ ./waf
Budowa $ ./waf

Uruchomienie test.py spowoduje uruchomienie wszystkich przykładów i testów:

170 z 170 testów zaliczonych (170 zaliczony, 0 pominiętych, 0 nieudanych, 0 awarii, 0 błędów valgrind)

Rozwiązywanie problemów
Ten rozdział zawiera informacje o prawdopodobnie typowych błędach podczas budowania lub uruchamiania
ns-3 programy.

Należy pamiętać, że wiki (http://www.nsnam.org/wiki/Troubleshooting) mógł się przyczynić
przedmiotów.

Buduj błędy
Czas pracy błędy
Czasami po udanej kompilacji mogą wystąpić błędy w programie. Są to czas działania
błędów i może często wystąpić, gdy pamięć jest uszkodzona lub wartości wskaźników są nieoczekiwane
zero.

Oto przykład tego, co może się wydarzyć:

$ ./waf --uruchom TCP-punkt-punkt
Wejście do katalogu „/home/tomh/ns-3-nsc/build”
Kompilacja zakończyła się pomyślnie
Polecenie ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] zostało zakończone z kodem -11

Komunikat o błędzie mówi, że program zakończył się niepowodzeniem, ale nie jest to jasne
z tych informacji co może być nie tak. Aby dokładniej zbadać, spróbuj uruchomić go pod
dotychczasowy gdb debugger:

$ ./waf --run tcp-point-to-point --command-template="gdb %s"
Wejście do katalogu „/home/tomh/ns-3-nsc/build”
Kompilacja zakończyła się pomyślnie
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
Prawa autorskie 2004 Fundacja Wolnego Oprogramowania, Inc.
GDB jest wolnym oprogramowaniem, objętym Powszechną Licencją Publiczną GNU, a Ty jesteś
zapraszamy do zmiany i/lub rozpowszechniania jego kopii pod pewnymi warunkami.
Wpisz „pokaż kopiowanie”, aby zobaczyć warunki.
Nie ma absolutnie żadnej gwarancji na GDB. Aby uzyskać szczegółowe informacje, wpisz „pokaż gwarancję”.
Ten GDB został skonfigurowany jako „i386-redhat-linux-gnu”… Używając hosta libthread_db
biblioteka "/lib/libthread_db.so.1".

(gdb) uruchom
Program startowy: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
Odczytywanie symboli z udostępnionego obiektu odczytanego z pamięci docelowej... gotowe.
Załadowany system dostarczył DSO pod adresem 0xf5c000

Odebrany sygnał programu SIGSEGV, błąd segmentacji.
0x0804aa12 w głównym (argc=1, argv=0xbfdfefa4)
w ../examples/tcp-point-to-point.cc:136
136 Ptr localSocket = socketFactory->CreateSocket ();
(gdb) p localSocket
1 $ = {m_ptr = 0x3c5d65}
(gdb) p fabryka gniazd
2 $ = {m_ptr = 0x0}
(gdb) wyjdź
Program jest uruchomiony. Wyjść mimo to? (y lub n) y

Zwróć najpierw uwagę na sposób, w jaki program został wywołany — przekaż polecenie uruchomienia jako argument do metody
szablon polecenia „gdb %s”.

To mówi nam, że podjęto próbę wyłuskania zerowego wskaźnika socketFactory.

Rozejrzyjmy się wokół linii 136 tcp-point-to-point, jak sugeruje gdb:

Ptr socketFactory = n2->GetObject (Tcp::iid);
Ptr localSocket = socketFactory->CreateSocket ();
localSocket->Bind ();

Winowajcą jest to, że zwracana wartość GetObject nie jest sprawdzana i może być
zero.

Czasami może być konieczne użycie tzw Valgrind pamięć sprawdzania dla bardziej subtelnych błędów. Ponownie,
podobnie wywołujesz użycie valgrind:

$ ./waf --run tcp-point-to-point --command-template="valgrind %s"

ŹRÓDŁO


Ten dokument jest napisany w reStructuredText dla Sfinks i jest utrzymywany w
dokument/instrukcja katalog kodu źródłowego ns-3.

Użyj ns-3-manual online, korzystając z usług onworks.net


Darmowe serwery i stacje robocze

Pobierz aplikacje Windows i Linux

Komendy systemu Linux

Ad




×
reklama
❤️Zrób zakupy, zarezerwuj lub kup tutaj — bezpłatnie, co pomaga utrzymać bezpłatne usługi.