Compare commits
15 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e9ae18f809 | |||
| 396c76892c | |||
| c329d7dd4e | |||
| ca3871e4f6 | |||
| d1c6c9a348 | |||
| d5260811dc | |||
| ce155b4d35 | |||
| 65aca43f3f | |||
| 96418f577d | |||
| 2ee9064175 | |||
| 4042681e83 | |||
| cdf13df87d | |||
| 50037ae611 | |||
| e38244e7c0 | |||
| 4f0eae4a6d |
@@ -1,56 +0,0 @@
|
||||
*.pyc
|
||||
.waf*
|
||||
src/NDNabstraction
|
||||
build/
|
||||
doc/html/
|
||||
doc/latex/
|
||||
\.rej$
|
||||
\.orig$
|
||||
\.o$
|
||||
~$
|
||||
^build-dir
|
||||
^build
|
||||
^testpy-output
|
||||
^doc/html
|
||||
^doc/latex
|
||||
.lock-wscript
|
||||
doc/ns3-object.txt
|
||||
doc/introspected-doxygen.h
|
||||
.lock-waf_darwin_build
|
||||
.lock-waf_linux2_build
|
||||
.lock-waf_cygwin_build
|
||||
.lock-waf_freebsd7_build
|
||||
.lock-waf_freebsd8_build
|
||||
.lock-waf_freebsd9_build
|
||||
.waf
|
||||
^doc/introspected-doxygen\.h$
|
||||
.*\.py[co]$
|
||||
\.pcap$
|
||||
\.mob$
|
||||
\.routes$
|
||||
^doc/manual/build
|
||||
^doc/tutorial/build
|
||||
^doc/testing/build
|
||||
^doc/models/build
|
||||
^doc/models/source-temp
|
||||
^doc/manual/figures/.*eps
|
||||
^doc/manual/figures/.*pdf
|
||||
^doc/manual/figures/.*png
|
||||
^bindings/python/pybindgen/
|
||||
ms_print.*
|
||||
massif.*
|
||||
\.patch$
|
||||
\.diff$
|
||||
\.tr$
|
||||
\#[^\#/]+\#$
|
||||
.lock-wscript
|
||||
.lock-wafbuild
|
||||
src/ndnSIM
|
||||
configure
|
||||
toggle
|
||||
.ns3rc
|
||||
.cproject
|
||||
.project
|
||||
.settings/
|
||||
|
||||
|
||||
@@ -1,48 +1,9 @@
|
||||
\.rej$
|
||||
\.orig$
|
||||
\.o$
|
||||
~$
|
||||
^build-dir
|
||||
^build
|
||||
^testpy-output
|
||||
^doc/html
|
||||
^doc/latex
|
||||
^doc/ns3-object.txt
|
||||
^\.lock-waf_darwin_build
|
||||
^\.lock-waf_linux2_build
|
||||
^\.lock-waf_cygwin_build
|
||||
^\.lock-waf_freebsd7_build
|
||||
^\.lock-waf_freebsd8_build
|
||||
^\.lock-waf_freebsd9_build
|
||||
^\.waf
|
||||
^doc/introspected-doxygen\.h$
|
||||
.*\.py[co]$
|
||||
\.pcap$
|
||||
\.mob$
|
||||
\.routes$
|
||||
^seventh-packet-byte-count.png
|
||||
\.cwnd
|
||||
^doc/doxygen.log$
|
||||
^doc/doxygen.warnings.log$
|
||||
^doc/(manual|models|tutorial|tutorial-pt-br)/build
|
||||
^doc/(manual|models|tutorial|tutorial-pt-br)/figures/.*\.(eps|pdf|png)$
|
||||
^doc/manual/source-temp
|
||||
^doc/models/source-temp
|
||||
^doc/ns3_html_theme/static/ns3_version.js$
|
||||
^src/.*/doc/build
|
||||
^src/.*/doc/source/figures/.*\.(eps|pdf)$
|
||||
^src/.*/doc/.*\.png
|
||||
^bindings/python/pybindgen/
|
||||
ms_print.*
|
||||
massif.*
|
||||
\.patch$
|
||||
\.diff$
|
||||
\.tr$
|
||||
\.dat$
|
||||
\.plt$
|
||||
\#[^\#/]+\#$
|
||||
^coverity
|
||||
^(D|U)l[A-Z][a-z]*Stats.txt$
|
||||
syntax: glob
|
||||
TAGS
|
||||
waf
|
||||
.*.orig$
|
||||
.*\.o$
|
||||
.*~$
|
||||
build-dir
|
||||
build
|
||||
.*\.sconsign
|
||||
doc/html.*
|
||||
doc/latex.*
|
||||
.lock-wscript
|
||||
|
||||
@@ -1,73 +1,2 @@
|
||||
56928998e05c9c11f5f3aefe79be8d2843e0db88 ns-3.0.1
|
||||
7ac5a4b0969b255c4824c926c2b37ef450136ce9 ns-3.0.2
|
||||
38099dd26e9467b8f49f8632f22789858149a6e7 ns-3.0.3
|
||||
5701e60bf01a8ac1308945e69001e0cc07948faf ns-3.0.4
|
||||
08046b6aef37932507696a2f2f427b42d693781e ns-3.0.5
|
||||
606df29888e7573b825fc891a002f0757166b616 ns-3.0.6
|
||||
36472385a1cc7c44d34fb7a5951b930010f4e8d2 ns-3.0.7
|
||||
560a5091e0e6ded47d269e2f2dee780f13950a63 ns-3.0.8
|
||||
4db981a0d9eb135e3e1c07765cff8d66f7a55cca ns-3.0.9
|
||||
b5bf2588cde2f1273b1095cc5c83a0c272e55370 ns-3.0.10
|
||||
ee5e1da76ecc52337f097cd90ebb50a3d49ec541 ns-3.0.11
|
||||
b17f2928291eec5bf5b1c59a4a5fd583f704ac40 ns-3.0.12
|
||||
79dba133b5f8a2d6f6f678a22e8519bc155e6a4e ns-3.0.13
|
||||
8869a79a391f49d0c787f0558cc9329b4e7ee40e ns-3.1-RC1
|
||||
951296e9a277e14113a0d3ba86a9688b222569ae ns-3.1-RC2
|
||||
ea16c44eb90db579c83d3434fc8a960be506a7f5 ns-3.1-RC3
|
||||
42504fb1f7be7817b8be513cdcd3d9bc8f3660e8 ns-3.1
|
||||
5768685f9fdba9fbf2e9561a840f085142c73575 ns-3.1
|
||||
dfd634417b8d1896d981b6f44d8f71030611596a ns-3.2-RC1
|
||||
319eb29611b18998abbad01548825643a8017bcb ns-3.2-RC2
|
||||
d783a951f8f5e64b33bc518f0415f76cae1ca6f3 ns-3.2-RC2-bis
|
||||
fa1c7b813873cfa251be7d1b7cea38373fe82fa1 ns-3.2-RC3
|
||||
68218c266a844f9fbda34a0ffddb1ae2adebd4b0 ns-3.2-RC4
|
||||
2ecac911b3ec40d73ab8301471bea6d9ba5b9885 ns-3.2
|
||||
f33cbf6b051c51d92db41b6fa825437958fbf801 ns-3.3-RC1
|
||||
0000000000000000000000000000000000000000 ns-3.3-RC1
|
||||
a84f2ab246e691a88b527a84ce21896b95080070 ns-3.3-RC1
|
||||
654eed5f3ad03c04c2bcfb6852b5b4ae94260bc6 ns-3.3-RC2
|
||||
a66553c56a8f14644af6c229c83bb0a653cb3f32 ns-3.3-RC3
|
||||
dfd0bc16dc991313896f351530a3dc5a25f62e15 ns-3.3-RC4
|
||||
58ae52c5845ff03ba08dc921e13e6cf3604c810a ns-3.3-RC5
|
||||
4267fd454004f5a60e517496f43a01000ccebc72 ns-3.3-RC6
|
||||
2efae18e73794c0acd8061a9bd5c74b69f1b0d93 ns-3.3
|
||||
5396ecd09060bcb1e7b1f750bb170cbfde7a77c2 ns-3.4
|
||||
13a5b15fbe3429a6c3d00c1bccffa2c156a0603b ns-3.5-rc1
|
||||
8562a42accf6f715d312c037326ec7da48095e13 ns-3.5-rc2
|
||||
a600c11ff8d40a40e88c2d692acad6512dde70c8 ns-3.5-rc3
|
||||
c975274c9707b1f07d94cc51f205c351122131a5 ns-3.5
|
||||
549243b47311211975b388cd64fcb9111caa2fc2 ns-3.6-RC1
|
||||
8996042990466b1eda718a848e1c02923c0add74 ns-3.6-RC2
|
||||
79ff6ad1adbb7b4677759ddf52028b68b0515168 ns-3.6-RC3
|
||||
39a82d7a0d661febe42e75f26ada79424258e330 ns-3.6-RC4
|
||||
d55c479666ac6be0575fac695ddf355c0530e0dd ns-3.6
|
||||
892efc87a1518fb69b04628c779195aee139d33e ns-3.7-RC3
|
||||
17bf6ee332baaa6b6f9b8a26d29c98f4e715648f ns-3.7-RC4
|
||||
be3fb855c65a46d22a7693cd156f154f63602e8e ns-3.7
|
||||
e61ce382fcdd5363a9d0052601c5eeae6f5e8d81 ns-3.8-RC1
|
||||
3b7ec0d36079b22fbc8c0d86b8edb876bf0ae9d8 ns-3.8-RC2
|
||||
d588e7fe6cb064919e2738e71e6169dc9be94e0b ns-3.8-RC3
|
||||
533be42b3c7ff738d76517a9a6e48f1795f54844 ns-3.8-RC4
|
||||
0ef062c0897e189f016951c307eaceedccad81e0 ns-3.8
|
||||
fb5ad9c7755aa6ea871e76d322fd4c43a5b1047d ns-3.9-RC1
|
||||
14019c625ab84486f0c263074048b0519db15f73 ns-3.9-RC2
|
||||
def4153e27cd744f117acf8c509979617850da83 ns-3.9-RC3
|
||||
8ddf25211f8995cde73baa8b8a419711ed451b85 ns-3.9
|
||||
63a8a4ed4054f1e1cd1756045abba657f6fd884c ns-3.10
|
||||
440bbee145f096193abcdd67fe6c16de62935d89 ns-3.11-RC1
|
||||
0a7a16b599e86ca7884e5b5772bf7c5cfe146603 ns-3.11-RC2
|
||||
e48ed3aabca6ad71c8c49e4604c0f83345eda6a8 ns-3.11-RC3
|
||||
9843c12351cb5ceb9613c9db390d94073b713284 ns-3.11
|
||||
19c9e2b33b4a53f200be80cd49810bdfecf76770 ns-3.12-RC1
|
||||
167fc2274f53d4d89fdd769e4a7683ad7b6b7157 ns-3.12
|
||||
9007193fbb8d18b69a1feb6aec16501dcf138b6f ns-3.13
|
||||
ae540de68a2534213342c5e0b12afb47d7518a90 ns-3.14
|
||||
26a511d6dbad7284e66de0f3b2ad5bebe6b76405 ns-3.15
|
||||
49d343e55caec257bb01e400eef6c14522f37e1b ns-3.16
|
||||
b4a70b99171ade6e9628a87780994238950a1df1 ns-3.17
|
||||
cfbc9491d7e7c9d346cc042fd35b8afb8836e81f ns-3.18
|
||||
322102df792e2be6c74df74f776b3470fb1db795 ns-3.19
|
||||
da0eb48df23f96335f32a37f047a6bc27e197c8d ns-3.20
|
||||
fa4be182ef17746cea50e89af557c79a28b76533 ns-3.21
|
||||
103f62fc7d586cf6d4d6fd9f5e488d2f7f8fe436 ns-3.22
|
||||
8ea92bc090cdc01e683a2116d7668ce745ae92e2 ns-3.23
|
||||
56928998e05c9c11f5f3aefe79be8d2843e0db88 release ns-3.0.1
|
||||
7ac5a4b0969b255c4824c926c2b37ef450136ce9 release ns-3.0.2
|
||||
|
||||
@@ -1,182 +1,6 @@
|
||||
Alexander Afanasyev (alexander.afanasyev@ucla.edu)
|
||||
Rohit Agarwal (mindprince@gmail.com)
|
||||
Kirill Andreev (andreev@iitp.ru)
|
||||
Dean Armstrong (deanarm@gmail.com)
|
||||
Stefano Avallone (stefano.avallone@unina.it)
|
||||
Ghada Badawy (gbadawy@gmail.com)
|
||||
Nicola Baldo (nbaldo@cttc.es)
|
||||
Mirko Banchi (mk.banchi@gmail.com)
|
||||
Peter D. Barnes, Jr. (barnes26@llnl.gov)
|
||||
Ramon Bauza (monbauza@gmail.com)
|
||||
Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
|
||||
Raj Bhattacharjea (raj.b@gatech.edu)
|
||||
Timo Bingmann (timo.bingmann@student.kit.edu)
|
||||
Julien Boite (juboite@gmail.com)
|
||||
Biljana Bojovic (bbojovic@cttc.es)
|
||||
Elena Borovkova (borokovaes@iitp.ru)
|
||||
Pavel Boyko (boyko@iitp.ru)
|
||||
Dan Broyles (muxman@sbcglobal.net)
|
||||
Jonathan Brugge (j.d.brugge@student.utwente.nl)
|
||||
Junling Bu (linlinjavaer@gmail.com)
|
||||
Elena Buchatskaia (borovkovaes@iitp.ru)
|
||||
Nuno Cardoso (nunopcardoso@gmail.com)
|
||||
Raj Bhattarcharjea (raj.b@gatech.edu)
|
||||
Gustavo Carneiro (gjc@inescporto.pt, gjcarneiro@gmail.com)
|
||||
Scott Carpenter (scarpen@ncsu.edu)
|
||||
Tiago Cerqueira (tiago.miguel43@gmail.com)
|
||||
Egemen K. Cetinkaya (ekc@iitc.ku.edu)
|
||||
Angelos Chatzipapas (chatzipa@ceid.upatras.gr)
|
||||
Eugene Chemeritskiy (echemeritskiy@arccn.ru)
|
||||
Yufei Cheng (yfcheng@ittc.ku.edu)
|
||||
Andrey Churin (aachurin@gmail.com)
|
||||
Benjamin Cizdziel (ben.cizdziel@gmail.com)
|
||||
Salva Climent (jocliba@gmail.com)
|
||||
Luis Cortes (cortes@gatech.edu)
|
||||
Luca Costantino (luca.costantino@gmail.com)
|
||||
Matthieu Coudron (matthieu.coudron@lip6.fr)
|
||||
Alexander D'souza (moijes12@gmail.com)
|
||||
Sébastien Deronne (sebastien.deronne@gmail.com)
|
||||
Craig Dowell (craigdo@ee.washington.edu)
|
||||
Gilaras Drakeson (gilaras@gmail.com)
|
||||
Christian Facchini (c.facchini@gmail.com)
|
||||
Denis Fakhriev (fakhriev@iitp.ru)
|
||||
Jahanzeb Farooq (Jahanzeb.Farooq@inria.fr, Jahanzeb.Farooq@gmail.com)
|
||||
Luca Favatella (slackydeb@gmail.com)
|
||||
Margherita Filippetti (morag87@gmail.com)
|
||||
Pedro Fortuna (pedro.fortuna@inescporto.pt)
|
||||
Juliana Freitag Borin (juliana.freitag@gmail.com)
|
||||
Piotr Gawlowicz (gawlowicz.p@gmail.com)
|
||||
Eric Gamess (egamess@gmail.com)
|
||||
Yida Gao (yidapb@gmail.com)
|
||||
Thomas Geithner (thomas.geithner@dai-labor.de)
|
||||
Ashim Ghosh (ashim.atiit@gmail.com)
|
||||
Martin Giachino (martin.giachino@gmail.com,giachino@fing.edu.uy)
|
||||
Tom Goff (tgoff@tgoff.net)
|
||||
Mohit Goyal (mohit.bits@gmail.com)
|
||||
Juan C. Granda (jcgranda@uniovi.es)
|
||||
David Gross (gdavid.devel@gmail.com)
|
||||
Maja Grubišić (maja.grubisic@live.com)
|
||||
Charline Taibi Guguen (charline.guguen@gmail.com)
|
||||
Daniel Halperin (daniel@halper.in)
|
||||
Bruno Haick (bghaick@hotmail.com)
|
||||
Frank Helbert (frank@ime.usp.br)
|
||||
Tom Henderson (tomhend@u.washington.edu)
|
||||
Christopher Hepner (hepner@hs-ulm.de)
|
||||
Budiarto Herman (budiarto.herman@magister.fi)
|
||||
Tom Hewer (tomhewer@mac.com)
|
||||
Kristian A. Hiorth (kristahi@ifi.uio.no)
|
||||
Kim Højgaard-Hansen (kimrhh@gmail.com)
|
||||
Chris Hood (chood8@gatech.edu)
|
||||
Blake Hurd (naimorai@gmail.com)
|
||||
ishan (ishan.chhabra@gmail.com)
|
||||
Mohamed Amine Ismail (amine.ismail@inria.fr, iamine@udcast.com)
|
||||
Jared Ivey (j.ivey@gatech.edu)
|
||||
Atishay Jain (atishayjain25@gmail.com)
|
||||
Sascha Alexander Jopen (jopen@informatik.uni-bonn.de)
|
||||
Sam Jansen (sam.jansen@gmail.com)
|
||||
Liu Jian (liujatp@gmail.com)
|
||||
Piotr Jurkiewicz (piotr.jerzy.jurkiewicz@gmail.com)
|
||||
Evgeny Kalishenko (ydginster@gmail.com)
|
||||
Konstantinos Katsaros (dinos.katsaros@gmail.com)
|
||||
Morteza Kheirkhah (m.kheirkhah@sussex.ac.uk)
|
||||
Flavio Kobuta (flaviokubota@gmail.com)
|
||||
Joe Kopena (tjkopena@cs.drexel.edu)
|
||||
Christopher Kosecki (christopher.l.kosecki.ctr@mail.mil)
|
||||
Aleksey Kovalenko (kovalenko@iitp.ru)
|
||||
Mathieu Lacage (mathieu.lacage@inria.fr)
|
||||
Emmanuelle Laprise (emmmanuelle.laprise@bluekazoo.ca)
|
||||
Kristijan Lenković (k.lenkovic@me.com)
|
||||
Daniel Lertpratchya (nikkipui@gmail.com)
|
||||
Brett Levasseur (brettl20@gmail.com)
|
||||
Björn Lichtblau (lichtbla@informatik.hu-berlin.de)
|
||||
Timo Lindhorst (tlnd@online.de)
|
||||
Erwan Livolant (erwan.livolant@inria.fr)
|
||||
Keith Ma (keith.nwsuaf@gmail.com)
|
||||
Federico Maguolo (maguolof@dei.unipd.it)
|
||||
Antti Makela (zarhan@cc.hut.fi)
|
||||
Francesco Malandrino (francesco.malandrino@gmail.com)
|
||||
Rubén Martínez (rmartinez@deic.uab.cat)
|
||||
Fabian Mauchle (f1mauchl@hsr.ch)
|
||||
Andrey Mazo (mazo@iitp.ru)
|
||||
Jonathan McCrohan (jmccroha@tcd.ie)
|
||||
Andrew McGregor (andrewmcgr@gmail.com)
|
||||
Vedran Miletić (rivanvx@gmail.com)
|
||||
Jens Mittag (jens.mittag@kit.edu)
|
||||
Marco Miozzo (mmiozzo@cttc.es)
|
||||
Faker Moatamri (faker.moatamri@inria.fr)
|
||||
Edvin Močibob (edvin.mocibob@gmail.com)
|
||||
Mike Moreton (mjvm_ns@hotmail.com)
|
||||
Michele Muccio (michelemuccio@virgilio.it)
|
||||
Esteban Municio (esteban.municio@urjc.es)
|
||||
Sidharth Nabar (snabar@uw.edu)
|
||||
Hemanth Narra (hemanth@ittc.ku.edu)
|
||||
Roman Naumann (naumann@informatik.hu-berlin.de)
|
||||
Ben Newton (bn@cs.unc.edu)
|
||||
Cecchi Niccolò (insa@igeek.it)
|
||||
Andreas Nilsson (andrnils@gmail.com)
|
||||
Jaume Nin (jnin@cttc.es)
|
||||
Michael Nowatkowski (nowatkom@gmail.com)
|
||||
Anh Nguyen (annguyen@ittc.ku.edu)
|
||||
Duy Nguyen (duy@soe.ucsc.edu)
|
||||
Luis Pacheco (luisbelem@gmail.com)
|
||||
Lluís Parcerisa (parcerisa@gmail.com)
|
||||
Natale Patriciello (natale.patriciello@gmail.com)
|
||||
Tommaso Pecorella (tommaso.pecorella@unifi.it)
|
||||
Guangyu Pei (guangyu.pei@boeing.com)
|
||||
Josh Pelkey (jpelkey@gatech.edu)
|
||||
Per (per_e_lists@rocketmail.com)
|
||||
Fernando Pereira (ferdonfeup@gmail.com)
|
||||
Colin Perkins (csp@csperkins.org)
|
||||
Giuseppe Piro (g.piro@poliba.it)
|
||||
Yana Podkosova (yanapdk@rambler.ru)
|
||||
Vikas Pushkar (vikaskupushkar@gmail.com)
|
||||
Andrea Ranieri (andreran@uno.it)
|
||||
Bruno Ranieri (Yrrsinn@googlemail.com)
|
||||
Ken Renard (kenneth.renard@arl.army.mil)
|
||||
Manuel Requena (mrequena@cttc.es)
|
||||
Matias Richart (mrichart@fing.edu.uy)
|
||||
Mathieu Lacage (mathieu.lacage@sophia.inria.fr)
|
||||
George F. Riley (riley@ece.gatech.edu)
|
||||
Juergen Rinas (jrinas@gmx.de)
|
||||
Sebastian Rohde (sebastian.rohde@tu-dortmund.de)
|
||||
Karsten Roscher (sfx@rocktale.de)
|
||||
Bill Roome (wdr@bell-labs.com)
|
||||
David (david.rua@gmail.com)
|
||||
Andrea Sacco (andrea.sacco85@gmail.com)
|
||||
Lynne Salameh (l.salameh@cs.ucl.ac.uk)
|
||||
Providence Salumu Munga (Providence.Salumu@gmail.com, Providence.Salumu_Munga@it-sudparis.eu)
|
||||
Francisco Javier Sánchez-Roselly (fnavarro@ujaen.es)
|
||||
Florian Schmidt (Florian.Schmidt@cs.rwth-aachen.de)
|
||||
Guillaume Seguin (guillaume.seguin@inria.fr)
|
||||
Ioannis Selinis (selinis.g@gmail.com)
|
||||
Tomasz Seweryn (tomasz.seweryn7@gmail.com)
|
||||
Dmitrii Shakshin (d.shakshin@gmail.com)
|
||||
Kulin Shah (m.kulin@gmail.com)
|
||||
Guowang Shi (shiguowang2007@gmail.com)
|
||||
Phillip Sitbon (phillip.sitbon@gmail.com)
|
||||
Pedro Silva (pmms@inesctec.pt)
|
||||
Anirudh Sivaraman (sk.anirudh@gmail.com)
|
||||
Steven Smith (smith84@llnl.gov)
|
||||
Andrew Stanton (acstanton515@gmail.com)
|
||||
Ewgenij Starostin (estar@cs.tu-berlin.de)
|
||||
YunQiang Su (wzssyqa@gmail.com)
|
||||
Brian Swenson (bswenson3@gatech.edu)
|
||||
Lalith Suresh (suresh.lalith@gmail.com)
|
||||
Dave Taht (dave.taht@bufferbloat.net)
|
||||
Marcos Talau (talau@users.sourceforge.net)
|
||||
Adrian S. W. Tam (adrian.sw.tam@gmail.com)
|
||||
Cristiano Tapparello (cristiano.tapparello@rochester.edu)
|
||||
Hajime Tazaki (tazaki@sfc.wide.ad.jp)
|
||||
Wilson Thong (wilsonwk@ee.cityu.edu.hk)
|
||||
Mauro Tortonesi (mauro.tortonesi@unife.it)
|
||||
Quincy Tse (quincy.tse@gmail.com)
|
||||
Frederic Urbani (frederic.urbani@inria.fr)
|
||||
Andras Varga (andras@omnetpp.org)
|
||||
Sebastien Vincent (vincent@clarinet.u-strasbg.fr)
|
||||
Guillaume Vu-Brugier (gvubrugier@gmail.com)
|
||||
Tom Wambold (tom5760@gmail.com)
|
||||
Danqi Wang (beyondwdq@gmail.com)
|
||||
Mitch Watrous (watrous@u.washington.edu)
|
||||
Florian Westphal (fw@strlen.de)
|
||||
He Wu (mdzz@u.washington.edu)
|
||||
Yoshihiko Yazawa (yoshiyaz@gmail.com)
|
||||
Dizhi Zhou (dizhi.zhou@gmail.com)
|
||||
Gaurav Sathe (gaurav.sathe@tcs.com)
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
# Makefile wrapper for waf
|
||||
|
||||
all:
|
||||
./waf
|
||||
|
||||
# free free to change this part to suit your requirements
|
||||
configure:
|
||||
#./waf configure --enable-examples --enable-tests
|
||||
./waf configure -d optimized --enable-examples --disable-python --disable-tests
|
||||
|
||||
build:
|
||||
./waf build
|
||||
|
||||
install:
|
||||
./waf install
|
||||
|
||||
clean:
|
||||
./waf clean
|
||||
|
||||
distclean:
|
||||
./waf distclean
|
||||
@@ -1,9 +1,3 @@
|
||||
Note:
|
||||
|
||||
This is a custom and unsupported fork of NS-3 simulator (http://www.nsnam.org/).
|
||||
|
||||
**The code in this repository is frequently rebased on top of the latest ns-3-dev branch**
|
||||
|
||||
|
||||
The Network Simulator, Version 3
|
||||
--------------------------------
|
||||
@@ -11,36 +5,63 @@ This is a custom and unsupported fork of NS-3 simulator (http://www.nsnam.org/).
|
||||
Table of Contents:
|
||||
------------------
|
||||
|
||||
1) An overview
|
||||
2) Building ns-3
|
||||
3) Running ns-3
|
||||
4) Getting access to the ns-3 documentation
|
||||
5) Working with the development version of ns-3
|
||||
1) An Open Source project
|
||||
2) An overview of the ns-3 project
|
||||
3) Building ns-3
|
||||
4) Running ns-3
|
||||
5) Getting access to the ns-3 documentation
|
||||
6) Working with the development version of ns-3
|
||||
|
||||
Note: Much more substantial information about ns-3 can be found at
|
||||
http://www.nsnam.org
|
||||
|
||||
1) An Open Source project
|
||||
-------------------------
|
||||
|
||||
ns-3 is a free open source project aiming to build a discrete-event
|
||||
network simulator targeted for simulation research and education.
|
||||
This is a collaborative project; we hope that
|
||||
ns-3 is an Open Source project. We intend to make this
|
||||
project a successful collaborative project: we hope that
|
||||
the missing pieces of the models we have not yet implemented
|
||||
will be contributed by the community in an open collaboration
|
||||
process.
|
||||
|
||||
The process of contributing to the ns-3 project varies with
|
||||
the people involved, the amount of time they can invest
|
||||
and the type of model they want to work on, but the current
|
||||
process that the project tries to follow is described here:
|
||||
http://www.nsnam.org/developers/contributing-code/
|
||||
Contributing to the ns-3 project is still a very informal
|
||||
process because that process depends heavily on the personality
|
||||
of the people involved, the amount of time they can invest
|
||||
and the type of model they want to work on.
|
||||
|
||||
This README excerpts some details from a more extensive
|
||||
tutorial that is maintained at:
|
||||
http://www.nsnam.org/documentation/latest/
|
||||
Despite this lack of a formal process, there are a number of
|
||||
steps which naturally stem from the open-source roots of the
|
||||
project. These steps are described in doc/contributing.txt
|
||||
|
||||
2) Building ns-3
|
||||
2) An overview of the ns-3 project
|
||||
----------------------------------
|
||||
|
||||
This package contains the latest version of ns-3 which is aims
|
||||
at being a replacement for ns-2. Currently, ns-3 provides a
|
||||
number of very simple network simulation models:
|
||||
- an ipv4 and udp stack
|
||||
- arp support at the bottom of the stack
|
||||
- point-to-point physical-layer links
|
||||
- OnOff traffic generator
|
||||
|
||||
Our focus to date has been on getting an overall software
|
||||
framework in place. The framework is there to make adding
|
||||
new models as simple as possible:
|
||||
- an extensive tracing system can be used to connect
|
||||
any number of arbitrary trace sources to any number
|
||||
of trace sinks. This tracing system is decoupled
|
||||
from the act of serializing the trace events to a
|
||||
file: users can and should provide their own
|
||||
trace handling routines.
|
||||
|
||||
- simple file trace serialization support is included
|
||||
to both text and pcap files.
|
||||
|
||||
- adding new MAC-level models simply requires subclassing
|
||||
the pair of classes NetDevice and Channel.
|
||||
|
||||
- adding new traffic generation algorithms is also very
|
||||
simple through the Application and the Socket classes.
|
||||
|
||||
3) Building ns-3
|
||||
----------------
|
||||
|
||||
The code for the framework and the default models provided
|
||||
@@ -50,65 +71,91 @@ use of these ns-3 libraries.
|
||||
|
||||
To build the set of default libraries and the example
|
||||
programs included in this package, you need to use the
|
||||
tool 'waf'. Detailed information on how use waf is
|
||||
included in the file doc/build.txt
|
||||
tool 'scons'. Detailed information on how to install
|
||||
and use scons is included in the file doc/build.txt
|
||||
|
||||
However, the real quick and dirty way to get started is to
|
||||
type the command
|
||||
./waf configure --enable-examples
|
||||
followed by
|
||||
./waf
|
||||
in the the directory which contains
|
||||
type the command "scons" the the directory which contains
|
||||
this README file. The files built will be copied in the
|
||||
build/ directory.
|
||||
build-dir/dbg-shared/bin and build-dir/dbg-shared/lib
|
||||
directories. build-dir/dbg-shared/bin will contain
|
||||
one binary for each of the sample code in the 'samples'
|
||||
directory and one binary for each of the detailed examples
|
||||
found in the 'examples' directory.
|
||||
|
||||
The current codebase is expected to build and run on the
|
||||
set of platforms listed in the RELEASE_NOTES file.
|
||||
following set of platforms:
|
||||
- linux x86 gcc 4.2, 4.1, and, 3.4.
|
||||
- linux x86_64 gcc 4.0
|
||||
- MacOS X ppc and x86
|
||||
|
||||
Other platforms may or may not work: we welcome patches to
|
||||
improve the portability of the code to these other platforms.
|
||||
The current codebase is expected to fail to build on
|
||||
the following platforms:
|
||||
- gcc 3.3 and earlier
|
||||
- optimized builds on linux x86 gcc 4.0
|
||||
|
||||
3) Running ns-3
|
||||
Other platforms may or may not work: we welcome
|
||||
patches to improve the portability of the code to these
|
||||
other platforms.
|
||||
|
||||
4) Running ns-3
|
||||
---------------
|
||||
|
||||
On recent Linux systems, once you have built ns-3 (with examples
|
||||
enabled), it should be easy to run the sample programs with the
|
||||
following command, such as:
|
||||
On recent Linux systems, once you have built ns-3, it
|
||||
should be easy to run the sample programs with the
|
||||
following command:
|
||||
|
||||
./waf --run simple-global-routing
|
||||
./build-dir/dbg-shared/bin/simple-p2p
|
||||
|
||||
That program should generate a simple-global-routing.tr text
|
||||
trace file and a set of simple-global-routing-xx-xx.pcap binary
|
||||
pcap trace files, which can be read by tcpdump -tt -r filename.pcap
|
||||
The program source can be found in the examples/routing directory.
|
||||
or:
|
||||
|
||||
4) Getting access to the ns-3 documentation
|
||||
cd build-dir/dbg-shared/bin
|
||||
./simple-p2p
|
||||
|
||||
That program should generate a simple-p2p.tr text
|
||||
trace file and a set of simple-p2p-xx-xx.pcap binary
|
||||
pcap trace files, which can be read by tcpdump.
|
||||
|
||||
5) Getting access to the ns-3 documentation
|
||||
-------------------------------------------
|
||||
|
||||
Once you have verified that your build of ns-3 works by running
|
||||
the simple-point-to-point example as outlined in 4) above, it is
|
||||
the simple-p2p example as outlined in 4) above, it is
|
||||
quite likely that you will want to get started on reading
|
||||
some ns-3 documentation.
|
||||
|
||||
All of that documentation should always be available from
|
||||
the ns-3 website: http:://www.nsnam.org/documentation/.
|
||||
the ns-3 website: http:://www.nsnam.org/ but we
|
||||
include some of it in this release for ease of use.
|
||||
|
||||
This documentation includes:
|
||||
|
||||
- a tutorial
|
||||
|
||||
- a reference manual
|
||||
|
||||
- models in the ns-3 model library
|
||||
- an architecture document which describes a very
|
||||
high-level view of ns-3: it tries to explain the
|
||||
use-cases the ns-3 developers really focused on when
|
||||
doing the initial design and then goes on to explain
|
||||
the structure of the resulting framework.
|
||||
See the file doc/architecture.pdf
|
||||
|
||||
- a wiki for user-contributed tips: http://www.nsnam.org/wiki/
|
||||
|
||||
- API documentation generated using doxygen: this is
|
||||
- an API documentation generated using doxygen: this is
|
||||
a reference manual, most likely not very well suited
|
||||
as introductory text:
|
||||
http://www.nsnam.org/doxygen/index.html
|
||||
|
||||
5) Working with the development version of ns-3
|
||||
If you want to re-generate this documentation, you can
|
||||
easily do so:
|
||||
|
||||
- doc/architecture.pdf is generated from the architecture.tex
|
||||
file in http://code.nsnam.org/docs
|
||||
|
||||
- the doxygen documentation is generated from the doc/doxygen.conf
|
||||
configuration file. The command "scons doc" will generate it
|
||||
as doc/html/index.html if you have installed the doxygen tools
|
||||
(see http://www.doxygen.org)
|
||||
|
||||
6) Working with the development version of ns-3
|
||||
-----------------------------------------------
|
||||
|
||||
If you want to download and use the development version
|
||||
|
||||
@@ -0,0 +1,475 @@
|
||||
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
import os.path
|
||||
import build
|
||||
|
||||
version_file = open ('VERSION', 'r')
|
||||
version = version_file.readline ()
|
||||
version_file.close ()
|
||||
version = version.strip ()
|
||||
|
||||
ns3 = build.Ns3()
|
||||
ns3.build_dir = 'build-dir'
|
||||
ns3.version = version
|
||||
ns3.name = 'ns3'
|
||||
ns3.distname = 'ns'
|
||||
ns3.doxygen_config = os.path.join('doc', 'doxygen.conf')
|
||||
ns3.add_extra_dist(os.path.join('doc', 'main.txt'))
|
||||
ns3.add_extra_dist ('doc/architecture.pdf')
|
||||
ns3.add_extra_dist ('doc/contributing.txt')
|
||||
ns3.add_extra_dist ('doc/build.txt')
|
||||
ns3.add_extra_dist ('doc/codingstd.txt')
|
||||
ns3.add_extra_dist ('doc/mercurial.txt')
|
||||
ns3.add_extra_dist ('README')
|
||||
ns3.add_extra_dist ('RELEASE_NOTES')
|
||||
ns3.add_extra_dist ('AUTHORS')
|
||||
ns3.add_extra_dist ('VERSION')
|
||||
|
||||
ns3.add_extra_dist('doc/build-waf.txt')
|
||||
ns3.add_extra_dist('ns3/_placeholder_')
|
||||
for wscript in [
|
||||
"src/core/wscript",
|
||||
"src/node/wscript",
|
||||
"src/devices/p2p/wscript",
|
||||
"src/common/wscript",
|
||||
"src/applications/wscript",
|
||||
"src/simulator/wscript",
|
||||
"src/internet-node/wscript",
|
||||
"src/wscript",
|
||||
"utils/wscript",
|
||||
"samples/wscript",
|
||||
"examples/wscript",
|
||||
"wscript",
|
||||
]:
|
||||
ns3.add_extra_dist(wscript)
|
||||
ns3.add_extra_dist('waf')
|
||||
ns3.add_extra_dist('waf.bat')
|
||||
|
||||
|
||||
|
||||
#
|
||||
# The Core module
|
||||
#
|
||||
core = build.Ns3Module('core', 'src/core')
|
||||
ns3.add(core)
|
||||
core.add_sources([
|
||||
'callback-test.cc',
|
||||
'debug.cc',
|
||||
'assert.cc',
|
||||
'ptr.cc',
|
||||
'object.cc',
|
||||
'test.cc',
|
||||
'random-variable.cc',
|
||||
'rng-stream.cc',
|
||||
'uid-manager.cc',
|
||||
'default-value.cc',
|
||||
'command-line.cc',
|
||||
'type-name.cc',
|
||||
'component-manager.cc',
|
||||
])
|
||||
env = Environment()
|
||||
if env['PLATFORM'] == 'posix' or env['PLATFORM'] == 'darwin' or env['PLATFORM'] == 'cygwin':
|
||||
core.add_external_dep('pthread')
|
||||
core.add_sources([
|
||||
'unix-system-wall-clock-ms.cc',
|
||||
])
|
||||
elif env['PLATFORM'] == 'win32':
|
||||
core.add_sources([
|
||||
'win32-system-wall-clock-ms.cc',
|
||||
])
|
||||
core.add_headers ([
|
||||
'uid-manager.h',
|
||||
'singleton.h',
|
||||
])
|
||||
core.add_inst_headers([
|
||||
'system-wall-clock-ms.h',
|
||||
'empty.h',
|
||||
'callback.h',
|
||||
'ptr.h',
|
||||
'object.h',
|
||||
'debug.h',
|
||||
'assert.h',
|
||||
'fatal-error.h',
|
||||
'test.h',
|
||||
'random-variable.h',
|
||||
'rng-stream.h',
|
||||
'default-value.h',
|
||||
'command-line.h',
|
||||
'type-name.h',
|
||||
'component-manager.h',
|
||||
])
|
||||
|
||||
def config_core (env, config):
|
||||
retval = []
|
||||
# XXX This check is primitive but it should be
|
||||
# good enough for now.
|
||||
if config.CheckCHeader ('stdlib.h') == 1:
|
||||
retval.append ('#define HAVE_STDLIB_H 1')
|
||||
retval.append ('#define HAVE_GETENV 1')
|
||||
else:
|
||||
retval.append ('#undef HAVE_STDLIB_H')
|
||||
retval.append ('#undef HAVE_GETENV')
|
||||
return retval
|
||||
core.add_config (config_core)
|
||||
|
||||
#
|
||||
# The Simu module
|
||||
#
|
||||
simu = build.Ns3Module('simulator', 'src/simulator')
|
||||
ns3.add(simu)
|
||||
simu.add_dep('core')
|
||||
simu.add_external_dep('m')
|
||||
simu.add_sources([
|
||||
'high-precision.cc',
|
||||
'time.cc',
|
||||
'event-id.cc',
|
||||
'scheduler.cc',
|
||||
'scheduler-factory.cc',
|
||||
'scheduler-list.cc',
|
||||
'scheduler-heap.cc',
|
||||
'scheduler-map.cc',
|
||||
'event-impl.cc',
|
||||
'simulator.cc',
|
||||
])
|
||||
simu.add_headers([
|
||||
'scheduler-heap.h',
|
||||
'scheduler-map.h',
|
||||
'scheduler-list.h'
|
||||
])
|
||||
simu.add_inst_headers([
|
||||
'high-precision.h',
|
||||
'nstime.h',
|
||||
'event-id.h',
|
||||
'event-impl.h',
|
||||
'simulator.h',
|
||||
'scheduler.h',
|
||||
'scheduler-factory.h',
|
||||
'simulation-singleton.h',
|
||||
])
|
||||
high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n')
|
||||
if high_precision_as_double == 'y':
|
||||
simu.add_inst_header ('high-precision-double.h')
|
||||
simu.add_source ('high-precision-double.cc')
|
||||
else:
|
||||
simu.add_inst_headers ([
|
||||
'high-precision-128.h',
|
||||
'cairo-wideint-private.h'
|
||||
])
|
||||
simu.add_sources ([
|
||||
'high-precision-128.cc',
|
||||
'cairo-wideint.c',
|
||||
])
|
||||
|
||||
def config_simulator (env, config):
|
||||
retval = []
|
||||
high_precision_as_double = ARGUMENTS.get('high-precision-as-double', 'n')
|
||||
if high_precision_as_double == 'y':
|
||||
retval.append ('#define USE_HIGH_PRECISION_DOUBLE 1')
|
||||
else:
|
||||
retval.append ('#undef USE_HIGH_PRECISION_DOUBLE')
|
||||
if config.CheckCHeader ('stdint.h') == 1:
|
||||
retval.append ('#define HAVE_STDINT_H 1')
|
||||
elif config.CheckCHeader ('inttypes.h') == 1:
|
||||
retval.append ('#define HAVE_INTTYPES_H 1')
|
||||
elif config.CheckCHeader ('sys/inttypes.h') == 1:
|
||||
retval.append ('#define HAVE_SYS_INT_TYPES_H 1')
|
||||
return retval
|
||||
simu.add_config (config_simulator)
|
||||
|
||||
|
||||
#
|
||||
# The Common module
|
||||
#
|
||||
common = build.Ns3Module('common', 'src/common')
|
||||
common.add_deps(['core', 'simulator'])
|
||||
ns3.add(common)
|
||||
common.add_sources([
|
||||
'buffer.cc',
|
||||
'chunk.cc',
|
||||
'header.cc',
|
||||
'trailer.cc',
|
||||
'packet.cc',
|
||||
'tags.cc',
|
||||
'pcap-writer.cc',
|
||||
'variable-tracer-test.cc',
|
||||
'trace-context.cc',
|
||||
'trace-resolver.cc',
|
||||
'callback-trace-source.cc',
|
||||
'empty-trace-resolver.cc',
|
||||
'composite-trace-resolver.cc',
|
||||
'trace-root.cc',
|
||||
'data-rate.cc',
|
||||
])
|
||||
common.add_headers ([
|
||||
])
|
||||
common.add_inst_headers([
|
||||
'buffer.h',
|
||||
'chunk.h',
|
||||
'header.h',
|
||||
'trailer.h',
|
||||
'tags.h',
|
||||
'packet.h',
|
||||
'uv-trace-source.h',
|
||||
'sv-trace-source.h',
|
||||
'fv-trace-source.h',
|
||||
'pcap-writer.h',
|
||||
'callback-trace-source.h',
|
||||
'trace-context.h',
|
||||
'trace-resolver.h',
|
||||
'empty-trace-resolver.h',
|
||||
'composite-trace-resolver.h',
|
||||
'array-trace-resolver.h',
|
||||
'trace-root.h',
|
||||
'terminal-trace-resolver.h',
|
||||
'data-rate.h',
|
||||
])
|
||||
|
||||
node = build.Ns3Module ('node', 'src/node')
|
||||
ns3.add (node)
|
||||
node.add_deps (['core', 'common', 'simulator'])
|
||||
node.add_sources ([
|
||||
'node.cc',
|
||||
'ipv4-address.cc',
|
||||
'net-device.cc',
|
||||
'mac-address.cc',
|
||||
'llc-snap-header.cc',
|
||||
'ipv4-route.cc',
|
||||
'queue.cc',
|
||||
'drop-tail-queue.cc',
|
||||
'channel.cc',
|
||||
'node-list.cc',
|
||||
'socket.cc',
|
||||
'socket-factory.cc',
|
||||
'udp.cc',
|
||||
'ipv4.cc',
|
||||
'application.cc',
|
||||
])
|
||||
node.add_inst_headers ([
|
||||
'node.h',
|
||||
'ipv4-address.h',
|
||||
'net-device.h',
|
||||
'mac-address.h',
|
||||
'ipv4-route.h',
|
||||
'queue.h',
|
||||
'drop-tail-queue.h',
|
||||
'llc-snap-header.h',
|
||||
'channel.h',
|
||||
'node-list.h',
|
||||
'socket.h',
|
||||
'socket-factory.h',
|
||||
'udp.h',
|
||||
'ipv4.h',
|
||||
'application.h',
|
||||
])
|
||||
|
||||
applications = build.Ns3Module ('applications', 'src/applications')
|
||||
ns3.add (applications)
|
||||
applications.add_deps (['node'])
|
||||
applications.add_sources ([
|
||||
'onoff-application.cc',
|
||||
])
|
||||
applications.add_inst_headers ([
|
||||
'onoff-application.h',
|
||||
])
|
||||
|
||||
inode = build.Ns3Module ('internet-node', 'src/internet-node')
|
||||
ns3.add (inode)
|
||||
inode.add_deps (['node'])
|
||||
inode.add_sources ([
|
||||
'internet-node.cc',
|
||||
'l3-demux.cc',
|
||||
'l3-protocol.cc',
|
||||
'ipv4-l4-demux.cc',
|
||||
'ipv4-l4-protocol.cc',
|
||||
'ipv4-header.cc',
|
||||
'udp-header.cc',
|
||||
'ipv4-checksum.cc',
|
||||
'ipv4-interface.cc',
|
||||
'ipv4-l3-protocol.cc',
|
||||
'ipv4-end-point.cc',
|
||||
'udp-l4-protocol.cc',
|
||||
'arp-header.cc',
|
||||
'arp-cache.cc',
|
||||
'arp-ipv4-interface.cc',
|
||||
'arp-l3-protocol.cc',
|
||||
'ipv4-loopback-interface.cc',
|
||||
'header-utils.cc',
|
||||
'udp-socket.cc',
|
||||
'ipv4-end-point-demux.cc',
|
||||
'arp-private.cc',
|
||||
'ipv4-impl.cc',
|
||||
'ipv4-private.cc',
|
||||
'ascii-trace.cc',
|
||||
'pcap-trace.cc',
|
||||
'udp-impl.cc',
|
||||
])
|
||||
inode.add_headers ([
|
||||
'ipv4-header.h',
|
||||
'udp-header.h',
|
||||
'ipv4-checksum.h',
|
||||
'arp-header.h',
|
||||
'arp-cache.h',
|
||||
'arp-l3-protocol.h',
|
||||
'ipv4-loopback-interface.h',
|
||||
'l3-demux.h',
|
||||
'header-utils.h',
|
||||
'arp-ipv4-interface.h',
|
||||
'udp-socket.h',
|
||||
'udp-l4-protocol.h',
|
||||
'arp-private.h',
|
||||
'ipv4-impl.h',
|
||||
'ipv4-private.h',
|
||||
'ipv4-l3-protocol.h',
|
||||
'l3-protocol.h',
|
||||
'ipv4-l4-protocol.h',
|
||||
'ipv4-l4-demux.h',
|
||||
'ipv4-end-point-demux.h',
|
||||
'ipv4-end-point.h',
|
||||
'ipv4-header.h',
|
||||
'udp-header.h',
|
||||
'ipv4-interface.h',
|
||||
'sgi-hashmap.h',
|
||||
'udp-impl.h',
|
||||
])
|
||||
inode.add_inst_headers ([
|
||||
'internet-node.h',
|
||||
'ascii-trace.h',
|
||||
'pcap-trace.h',
|
||||
])
|
||||
|
||||
|
||||
|
||||
p2p = build.Ns3Module ('p2p', 'src/devices/p2p')
|
||||
ns3.add (p2p)
|
||||
p2p.add_deps (['node'])
|
||||
p2p.add_sources ([
|
||||
'p2p-net-device.cc',
|
||||
'p2p-channel.cc',
|
||||
'p2p-topology.cc',
|
||||
])
|
||||
p2p.add_inst_headers ([
|
||||
'p2p-net-device.h',
|
||||
'p2p-channel.h',
|
||||
'p2p-topology.h',
|
||||
])
|
||||
|
||||
|
||||
# utils
|
||||
run_tests = build.Ns3Module('run-tests', 'utils')
|
||||
ns3.add(run_tests)
|
||||
run_tests.set_executable()
|
||||
run_tests.add_deps(['core', 'simulator', 'common'])
|
||||
run_tests.add_source('run-tests.cc')
|
||||
|
||||
bench_object = build.Ns3Module('bench-object', 'utils')
|
||||
ns3.add(bench_object)
|
||||
bench_object.set_executable()
|
||||
bench_object.add_deps(['core'])
|
||||
bench_object.add_source('bench-object.cc')
|
||||
|
||||
bench_packets = build.Ns3Module('bench-packets', 'utils')
|
||||
#ns3.add(bench_packets)
|
||||
bench_packets.set_executable()
|
||||
bench_packets.add_dep('core')
|
||||
bench_packets.add_source('bench-packets.cc')
|
||||
|
||||
bench_simu = build.Ns3Module('bench-simulator', 'utils')
|
||||
ns3.add(bench_simu)
|
||||
bench_simu.set_executable()
|
||||
bench_simu.add_dep('simulator')
|
||||
bench_simu.add_source('bench-simulator.cc')
|
||||
|
||||
replay_simu = build.Ns3Module('replay-simulation', 'utils')
|
||||
ns3.add(replay_simu)
|
||||
replay_simu.set_executable()
|
||||
replay_simu.add_dep('simulator')
|
||||
replay_simu.add_source('replay-simulation.cc')
|
||||
|
||||
|
||||
# samples
|
||||
sample_debug = build.Ns3Module('sample-debug', 'samples')
|
||||
sample_debug.set_executable()
|
||||
ns3.add(sample_debug)
|
||||
sample_debug.add_dep('core')
|
||||
sample_debug.add_source('main-debug.cc')
|
||||
sample_debug.add_source('main-debug-other.cc')
|
||||
|
||||
sample_callback = build.Ns3Module('sample-callback', 'samples')
|
||||
sample_callback.set_executable()
|
||||
ns3.add(sample_callback)
|
||||
sample_callback.add_dep('core')
|
||||
sample_callback.add_source('main-callback.cc')
|
||||
|
||||
sample_ptr = build.Ns3Module('sample-ptr', 'samples')
|
||||
sample_ptr.set_executable()
|
||||
ns3.add(sample_ptr)
|
||||
sample_ptr.add_dep('core')
|
||||
sample_ptr.add_source('main-ptr.cc')
|
||||
|
||||
sample_trace = build.Ns3Module('sample-trace', 'samples')
|
||||
#ns3.add(sample_trace)
|
||||
sample_trace.add_dep('common')
|
||||
sample_trace.set_executable()
|
||||
sample_trace.add_source('main-trace.cc')
|
||||
|
||||
sample_query_interface = build.Ns3Module('sample-query-interface', 'samples')
|
||||
ns3.add(sample_query_interface)
|
||||
sample_query_interface.add_dep('common')
|
||||
sample_query_interface.set_executable()
|
||||
sample_query_interface.add_source('main-query-interface.cc')
|
||||
|
||||
sample_simu = build.Ns3Module('sample-simulator', 'samples')
|
||||
ns3.add(sample_simu)
|
||||
sample_simu.set_executable()
|
||||
sample_simu.add_dep('simulator')
|
||||
sample_simu.add_source('main-simulator.cc')
|
||||
|
||||
sample_packet = build.Ns3Module('sample-packet', 'samples')
|
||||
ns3.add(sample_packet)
|
||||
sample_packet.set_executable()
|
||||
sample_packet.add_dep('common')
|
||||
sample_packet.add_source('main-packet.cc')
|
||||
|
||||
sample_test = build.Ns3Module('sample-test', 'samples')
|
||||
sample_test.set_executable()
|
||||
ns3.add(sample_test)
|
||||
sample_test.add_dep('core')
|
||||
sample_test.add_source('main-test.cc')
|
||||
|
||||
sample_simple = build.Ns3Module('sample-simple', 'samples')
|
||||
sample_simple.set_executable()
|
||||
ns3.add(sample_simple)
|
||||
sample_simple.add_deps(['core', 'simulator', 'node', 'internet-node'])
|
||||
sample_simple.add_source('main-simple.cc')
|
||||
|
||||
sample_sp2p = build.Ns3Module('sample-simple-p2p', 'samples')
|
||||
sample_sp2p.set_executable()
|
||||
#n3.add(sample_sp2p)
|
||||
sample_sp2p.add_deps(['core', 'simulator', 'node', 'internet-node', 'p2p'])
|
||||
sample_sp2p.add_source('main-simple-p2p.cc')
|
||||
|
||||
sample_default_value = build.Ns3Module('sample-default-value', 'samples')
|
||||
sample_default_value.set_executable()
|
||||
ns3.add(sample_default_value)
|
||||
sample_default_value.add_deps(['core', 'simulator', 'node', 'p2p'])
|
||||
sample_default_value.add_source('main-default-value.cc')
|
||||
|
||||
sample_object = build.Ns3Module('sample-object', 'samples')
|
||||
sample_object.set_executable()
|
||||
ns3.add(sample_object)
|
||||
sample_object.add_deps(['core'])
|
||||
sample_object.add_source('main-object.cc')
|
||||
|
||||
sample_component_manager = build.Ns3Module('sample-component-manager', 'samples')
|
||||
sample_component_manager.set_executable()
|
||||
ns3.add(sample_component_manager)
|
||||
sample_component_manager.add_deps(['core'])
|
||||
sample_component_manager.add_source('main-component-manager.cc')
|
||||
|
||||
# examples
|
||||
example_simple_p2p = build.Ns3Module('simple-p2p', 'examples')
|
||||
example_simple_p2p.set_executable()
|
||||
ns3.add(example_simple_p2p)
|
||||
example_simple_p2p.add_deps(['core', 'simulator', 'node', 'p2p', 'internet-node', 'applications'])
|
||||
example_simple_p2p.add_source('simple-p2p.cc')
|
||||
|
||||
ns3.generate_dependencies()
|
||||
@@ -1,13 +0,0 @@
|
||||
from pybindgen import Module, FileCodeSink, write_preamble, param, retval
|
||||
|
||||
def register_types(module):
|
||||
module.add_class('MyClass')
|
||||
|
||||
def register_methods(root_module):
|
||||
MyClass = root_module['MyClass']
|
||||
MyClass.add_constructor([], visibility='public')
|
||||
MyClass.add_constructor([param('double', 's'), param('double', 'l'), param('double', 'mean')], visibility='public')
|
||||
|
||||
def register_functions(module):
|
||||
module.add_function('SomeFunction', 'int', [param('int', 'xpto')])
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
|
||||
from _ns3 import *
|
||||
|
||||
import atexit
|
||||
atexit.register(Simulator.Destroy)
|
||||
del atexit
|
||||
|
||||
@@ -1,336 +0,0 @@
|
||||
#include "ns3module.h"
|
||||
#include "ns3/ref-count-base.h"
|
||||
|
||||
|
||||
namespace ns3{
|
||||
|
||||
void PythonCompleteConstruct (Ptr<Object> object, TypeId typeId, const AttributeList &attributes)
|
||||
{
|
||||
object->SetTypeId (typeId);
|
||||
object->Object::Construct (attributes);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PythonEventImpl : public ns3::EventImpl
|
||||
{
|
||||
private:
|
||||
PyObject *m_callback;
|
||||
PyObject *m_args;
|
||||
public:
|
||||
PythonEventImpl (PyObject *callback, PyObject *args)
|
||||
{
|
||||
m_callback = callback;
|
||||
Py_INCREF(m_callback);
|
||||
m_args = args;
|
||||
Py_INCREF(m_args);
|
||||
}
|
||||
virtual ~PythonEventImpl ()
|
||||
{
|
||||
PyGILState_STATE __py_gil_state;
|
||||
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
|
||||
|
||||
Py_DECREF(m_callback);
|
||||
Py_DECREF(m_args);
|
||||
|
||||
if (PyEval_ThreadsInitialized())
|
||||
PyGILState_Release(__py_gil_state);
|
||||
}
|
||||
virtual void Notify ()
|
||||
{
|
||||
PyGILState_STATE __py_gil_state;
|
||||
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
|
||||
|
||||
PyObject *retval = PyObject_CallObject(m_callback, m_args);
|
||||
if (retval) {
|
||||
if (retval != Py_None) {
|
||||
PyErr_SetString(PyExc_TypeError, "event callback should return None");
|
||||
PyErr_Print();
|
||||
}
|
||||
Py_DECREF(retval);
|
||||
} else {
|
||||
PyErr_Print();
|
||||
}
|
||||
|
||||
if (PyEval_ThreadsInitialized())
|
||||
PyGILState_Release(__py_gil_state);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
PyObject *
|
||||
_wrap_Simulator_Schedule(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyObject *py_time;
|
||||
PyObject *py_callback;
|
||||
PyObject *user_args;
|
||||
ns3::Ptr<PythonEventImpl> py_event_impl;
|
||||
PyNs3EventId *py_EventId;
|
||||
|
||||
if (kwargs && PyObject_Length(kwargs) > 0) {
|
||||
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (PyTuple_GET_SIZE(args) < 2) {
|
||||
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 2 arguments");
|
||||
goto error;
|
||||
}
|
||||
py_time = PyTuple_GET_ITEM(args, 0);
|
||||
py_callback = PyTuple_GET_ITEM(args, 1);
|
||||
|
||||
if (!PyObject_IsInstance(py_time, (PyObject*) &PyNs3Time_Type)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter 1 should be a ns3.Time instance");
|
||||
goto error;
|
||||
}
|
||||
if (!PyCallable_Check(py_callback)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
|
||||
goto error;
|
||||
}
|
||||
user_args = PyTuple_GetSlice(args, 2, PyTuple_GET_SIZE(args));
|
||||
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
|
||||
Py_DECREF(user_args);
|
||||
|
||||
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
|
||||
py_EventId->obj = new ns3::EventId(
|
||||
ns3::Simulator::Schedule(*((PyNs3Time *) py_time)->obj, py_event_impl));
|
||||
return (PyObject *) py_EventId;
|
||||
|
||||
error:
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_wrap_Simulator_ScheduleNow(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyObject *py_callback;
|
||||
PyObject *user_args;
|
||||
ns3::Ptr<PythonEventImpl> py_event_impl;
|
||||
PyNs3EventId *py_EventId;
|
||||
|
||||
if (kwargs && PyObject_Length(kwargs) > 0) {
|
||||
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (PyTuple_GET_SIZE(args) < 1) {
|
||||
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 1 argument");
|
||||
goto error;
|
||||
}
|
||||
py_callback = PyTuple_GET_ITEM(args, 0);
|
||||
|
||||
if (!PyCallable_Check(py_callback)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
|
||||
goto error;
|
||||
}
|
||||
user_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
|
||||
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
|
||||
Py_DECREF(user_args);
|
||||
|
||||
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
|
||||
py_EventId->obj = new ns3::EventId(ns3::Simulator::ScheduleNow(py_event_impl));
|
||||
return (PyObject *) py_EventId;
|
||||
|
||||
error:
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_wrap_Simulator_ScheduleDestroy(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
PyObject *exc_type, *traceback;
|
||||
PyObject *py_callback;
|
||||
PyObject *user_args;
|
||||
ns3::Ptr<PythonEventImpl> py_event_impl;
|
||||
PyNs3EventId *py_EventId;
|
||||
|
||||
if (kwargs && PyObject_Length(kwargs) > 0) {
|
||||
PyErr_SetString(PyExc_TypeError, "keyword arguments not supported");
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (PyTuple_GET_SIZE(args) < 1) {
|
||||
PyErr_SetString(PyExc_TypeError, "ns3.Simulator.Schedule needs at least 1 argument");
|
||||
goto error;
|
||||
}
|
||||
py_callback = PyTuple_GET_ITEM(args, 0);
|
||||
|
||||
if (!PyCallable_Check(py_callback)) {
|
||||
PyErr_SetString(PyExc_TypeError, "Parameter 2 should be callable");
|
||||
goto error;
|
||||
}
|
||||
user_args = PyTuple_GetSlice(args, 1, PyTuple_GET_SIZE(args));
|
||||
py_event_impl = ns3::Create<PythonEventImpl>(py_callback, user_args);
|
||||
Py_DECREF(user_args);
|
||||
|
||||
py_EventId = PyObject_New(PyNs3EventId, &PyNs3EventId_Type);
|
||||
py_EventId->obj = new ns3::EventId(ns3::Simulator::ScheduleDestroy(py_event_impl));
|
||||
return (PyObject *) py_EventId;
|
||||
|
||||
error:
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_wrap_TypeId_LookupByNameFailSafe(PyNs3TypeId *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
bool ok;
|
||||
const char *name;
|
||||
Py_ssize_t name_len;
|
||||
ns3::TypeId tid;
|
||||
PyNs3TypeId *py_tid;
|
||||
const char *keywords[] = {"name", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "s#", (char **) keywords, &name, &name_len)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
ok = ns3::TypeId::LookupByNameFailSafe(std::string(name, name_len), &tid);
|
||||
if (!ok)
|
||||
{
|
||||
PyErr_Format(PyExc_KeyError, "The ns3 type with name `%s' is not registered", name);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
py_tid = PyObject_New(PyNs3TypeId, &PyNs3TypeId_Type);
|
||||
py_tid->obj = new ns3::TypeId (tid);
|
||||
PyNs3TypeId_wrapper_registry[(void *) py_tid->obj] = (PyObject *) py_tid;
|
||||
|
||||
return (PyObject *) py_tid;
|
||||
}
|
||||
|
||||
|
||||
class CommandLinePythonValueSetter : public ns3::RefCountBase
|
||||
{
|
||||
PyObject *m_namespace;
|
||||
std::string m_variable;
|
||||
public:
|
||||
CommandLinePythonValueSetter (PyObject *ns, std::string const &variable) {
|
||||
Py_INCREF(ns);
|
||||
m_namespace = ns;
|
||||
m_variable = variable;
|
||||
}
|
||||
bool Parse (std::string value) {
|
||||
PyObject *pyvalue = PyString_FromStringAndSize (value.data(), value.size());
|
||||
PyObject_SetAttrString (m_namespace, (char *) m_variable.c_str(), pyvalue);
|
||||
if (PyErr_Occurred()) {
|
||||
PyErr_Print();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
virtual ~CommandLinePythonValueSetter () {
|
||||
Py_DECREF (m_namespace);
|
||||
m_namespace = NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
PyObject *
|
||||
_wrap_CommandLine_AddValue(PyNs3CommandLine *self, PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
const char *name, *help, *variable = NULL;
|
||||
PyObject *py_namespace = NULL;
|
||||
const char *keywords[] = {"name", "help", "variable", "namespace", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "ss|sO", (char **) keywords, &name, &help, &variable, &py_namespace)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (variable == NULL) {
|
||||
variable = name;
|
||||
}
|
||||
if (py_namespace == NULL) {
|
||||
py_namespace = (PyObject *) self;
|
||||
}
|
||||
|
||||
ns3::Ptr<CommandLinePythonValueSetter> setter = ns3::Create<CommandLinePythonValueSetter> (py_namespace, variable);
|
||||
self->obj->AddValue (name, help, ns3::MakeCallback (&CommandLinePythonValueSetter::Parse, setter));
|
||||
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
|
||||
PyObject *
|
||||
_wrap_Simulator_Run(PyNs3Simulator *PYBINDGEN_UNUSED(dummy), PyObject *args, PyObject *kwargs,
|
||||
PyObject **return_exception)
|
||||
{
|
||||
const char *keywords[] = {"signal_check_frequency", NULL};
|
||||
int signal_check_frequency;
|
||||
|
||||
ns3::Ptr<ns3::DefaultSimulatorImpl> defaultSim =
|
||||
ns3::DynamicCast<ns3::DefaultSimulatorImpl> (ns3::Simulator::GetImplementation ());
|
||||
if (defaultSim) {
|
||||
signal_check_frequency = 100;
|
||||
} else {
|
||||
signal_check_frequency = -1;
|
||||
}
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwargs, (char *) "|i", (char **) keywords, &signal_check_frequency)) {
|
||||
PyObject *exc_type, *traceback;
|
||||
PyErr_Fetch(&exc_type, return_exception, &traceback);
|
||||
Py_XDECREF(exc_type);
|
||||
Py_XDECREF(traceback);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
PyThreadState *py_thread_state = NULL;
|
||||
|
||||
if (signal_check_frequency == -1)
|
||||
{
|
||||
if (PyEval_ThreadsInitialized ())
|
||||
py_thread_state = PyEval_SaveThread();
|
||||
ns3::Simulator::Run();
|
||||
if (py_thread_state)
|
||||
PyEval_RestoreThread(py_thread_state);
|
||||
} else {
|
||||
while (!ns3::Simulator::IsFinished())
|
||||
{
|
||||
if (PyEval_ThreadsInitialized())
|
||||
py_thread_state = PyEval_SaveThread();
|
||||
|
||||
for (int n = signal_check_frequency; n > 0 && !ns3::Simulator::IsFinished(); --n)
|
||||
{
|
||||
ns3::Simulator::RunOneEvent();
|
||||
}
|
||||
|
||||
if (py_thread_state)
|
||||
PyEval_RestoreThread(py_thread_state);
|
||||
PyErr_CheckSignals();
|
||||
if (PyErr_Occurred())
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
Py_INCREF(Py_None);
|
||||
return Py_None;
|
||||
}
|
||||
|
||||
@@ -1,131 +0,0 @@
|
||||
from __future__ import print_function
|
||||
import warnings
|
||||
import sys
|
||||
import os
|
||||
import pybindgen.settings
|
||||
from pybindgen import Module, FileCodeSink, param, retval, cppclass, typehandlers
|
||||
from pybindgen.module import MultiSectionFactory
|
||||
import ns3modulegen_core_customizations
|
||||
|
||||
import logging
|
||||
|
||||
pybindgen.settings.wrapper_registry = pybindgen.settings.StdMapWrapperRegistry
|
||||
|
||||
import traceback
|
||||
|
||||
class ErrorHandler(pybindgen.settings.ErrorHandler):
|
||||
|
||||
def __init__(self, apidefs_file):
|
||||
self.apidefs_file = apidefs_file
|
||||
|
||||
def handle_error(self, wrapper, exception, traceback_):
|
||||
stack = getattr(wrapper, 'stack_where_defined', [])
|
||||
stack.reverse()
|
||||
for l in stack:
|
||||
if l[0] == self.apidefs_file:
|
||||
warnings.warn_explicit("exception %r in wrapper %s" % (exception, wrapper),
|
||||
Warning, l[0], l[1])
|
||||
break
|
||||
else:
|
||||
warnings.warn("exception %r in wrapper %s" % (exception, wrapper))
|
||||
return True
|
||||
|
||||
|
||||
#print >> sys.stderr, ">>>>>>>>>>>>>>>>>>>>>>>>>>>> ", bool(eval(os.environ["GCC_RTTI_ABI_COMPLETE"]))
|
||||
pybindgen.settings.gcc_rtti_abi_complete = bool(eval(os.environ["GCC_RTTI_ABI_COMPLETE"]))
|
||||
|
||||
class MyMultiSectionFactory(MultiSectionFactory):
|
||||
def __init__(self, main_file_name):
|
||||
super(MyMultiSectionFactory, self).__init__()
|
||||
self.main_file_name = main_file_name
|
||||
self.main_sink = FileCodeSink(open(main_file_name, "wt"))
|
||||
self.header_name = "ns3module.h"
|
||||
header_file_name = os.path.join(os.path.dirname(self.main_file_name), self.header_name)
|
||||
#print >> sys.stderr, ">>>>>>>>>>>>>>>>>", header_file_name, main_file_name
|
||||
self.header_sink = FileCodeSink(open(header_file_name, "wt"))
|
||||
def get_section_code_sink(self, section_name):
|
||||
return self.main_sink
|
||||
def get_main_code_sink(self):
|
||||
return self.main_sink
|
||||
def get_common_header_code_sink(self):
|
||||
return self.header_sink
|
||||
def get_common_header_include(self):
|
||||
return '"%s"' % self.header_name
|
||||
def close(self):
|
||||
self.header_sink.file.close()
|
||||
self.main_sink.file.close()
|
||||
|
||||
|
||||
|
||||
def main(argv):
|
||||
logging.basicConfig()
|
||||
logging.getLogger("pybindgen.typehandlers").setLevel(logging.DEBUG)
|
||||
|
||||
module_abs_src_path, target, extension_name, output_cc_file_name = argv[1:]
|
||||
module_name = os.path.basename(module_abs_src_path)
|
||||
out = MyMultiSectionFactory(output_cc_file_name)
|
||||
|
||||
sys.path.insert(0, os.path.join(module_abs_src_path, "bindings"))
|
||||
try:
|
||||
module_apidefs = __import__("modulegen__%s" % target)
|
||||
del sys.modules["modulegen__%s" % target]
|
||||
try:
|
||||
module_customization = __import__("modulegen_customizations")
|
||||
del sys.modules["modulegen_customizations"]
|
||||
except ImportError:
|
||||
module_customization = object()
|
||||
|
||||
try:
|
||||
from callbacks_list import callback_classes
|
||||
except ImportError as ex:
|
||||
print("***************", repr(ex), file=sys.stderr)
|
||||
callback_classes = []
|
||||
else:
|
||||
print(">>>>>>>>>>>>>>>>", repr(callback_classes), file=sys.stderr)
|
||||
|
||||
finally:
|
||||
sys.path.pop(0)
|
||||
|
||||
apidefs_file, dummy = os.path.splitext(module_apidefs.__file__)
|
||||
apidefs_file += '.py'
|
||||
pybindgen.settings.error_handler = ErrorHandler(apidefs_file)
|
||||
|
||||
root_module = module_apidefs.module_init()
|
||||
root_module.set_name(extension_name)
|
||||
root_module.add_include('"ns3/%s-module.h"' % module_name)
|
||||
|
||||
ns3modulegen_core_customizations.add_std_ios_openmode(root_module)
|
||||
|
||||
# -----------
|
||||
module_apidefs.register_types(root_module)
|
||||
|
||||
if hasattr(module_customization, 'post_register_types'):
|
||||
module_customization.post_register_types(root_module)
|
||||
|
||||
# register Callback<...> type handlers
|
||||
ns3modulegen_core_customizations.register_callback_classes(root_module.after_forward_declarations,
|
||||
callback_classes)
|
||||
|
||||
# -----------
|
||||
module_apidefs.register_methods(root_module)
|
||||
|
||||
if hasattr(module_customization, 'post_register_methods'):
|
||||
module_customization.post_register_methods(root_module)
|
||||
|
||||
ns3modulegen_core_customizations.Object_customizations(root_module)
|
||||
ns3modulegen_core_customizations.Attribute_customizations(root_module)
|
||||
ns3modulegen_core_customizations.generate_callback_classes(root_module,
|
||||
callback_classes)
|
||||
|
||||
# -----------
|
||||
module_apidefs.register_functions(root_module)
|
||||
|
||||
if hasattr(module_customization, 'post_register_functions'):
|
||||
module_customization.post_register_functions(root_module)
|
||||
|
||||
# -----------
|
||||
root_module.generate(out)
|
||||
|
||||
if __name__ == '__main__':
|
||||
import sys
|
||||
main(sys.argv)
|
||||
@@ -1,177 +0,0 @@
|
||||
|
||||
LOCAL_MODULES = [
|
||||
#'my_extra_api_definitions',
|
||||
]
|
||||
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
|
||||
sys.path.insert(0, sys.argv[2])
|
||||
|
||||
from pybindgen import FileCodeSink, write_preamble
|
||||
from pybindgen.module import MultiSectionFactory
|
||||
|
||||
import pybindgen.settings
|
||||
pybindgen.settings.deprecated_virtuals = False
|
||||
|
||||
from ns3modulegen_generated import module_init, register_types, register_methods, register_functions
|
||||
import ns3modulegen_core_customizations
|
||||
import callbacks_list
|
||||
import traceback
|
||||
|
||||
this_script_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
|
||||
|
||||
class ErrorHandler(pybindgen.settings.ErrorHandler):
|
||||
def handle_error(self, wrapper, exception, traceback_):
|
||||
print >> sys.stderr
|
||||
print >> sys.stderr, "---- location:"
|
||||
traceback.print_stack()
|
||||
print >> sys.stderr, "---- error:"
|
||||
traceback.print_tb(traceback_)
|
||||
try:
|
||||
stack = wrapper.stack_where_defined
|
||||
except AttributeError:
|
||||
print >> sys.stderr, "??:??: %s / %r" % (wrapper, exception)
|
||||
else:
|
||||
stack = list(stack)
|
||||
stack.reverse()
|
||||
for (filename, line_number, function_name, text) in stack:
|
||||
file_dir = os.path.dirname(os.path.abspath(filename))
|
||||
if file_dir.startswith(this_script_dir):
|
||||
print >> sys.stderr, "%s:%i: %r" % (os.path.join("..", "bindings", "python", os.path.basename(filename)),
|
||||
line_number, exception)
|
||||
break
|
||||
return True
|
||||
pybindgen.settings.error_handler = ErrorHandler()
|
||||
|
||||
pybindgen.settings.wrapper_registry = pybindgen.settings.StdMapWrapperRegistry
|
||||
|
||||
|
||||
class MyMultiSectionFactory(MultiSectionFactory):
|
||||
|
||||
def __init__(self, main_file_name, modules):
|
||||
super(MyMultiSectionFactory, self).__init__()
|
||||
self.main_file_name = main_file_name
|
||||
self.main_sink = FileCodeSink(open(main_file_name, "wt"))
|
||||
self.header_name = "ns3module.h"
|
||||
header_file_name = os.path.join(os.path.dirname(self.main_file_name), 'pch', self.header_name)
|
||||
self.header_sink = FileCodeSink(open(header_file_name, "wt"))
|
||||
self.section_sinks = {'__main__': self.main_sink}
|
||||
|
||||
for module in modules:
|
||||
section_name = 'ns3_module_%s' % module.replace('-', '_')
|
||||
file_name = os.path.join(os.path.dirname(self.main_file_name), "%s.cc" % section_name)
|
||||
sink = FileCodeSink(open(file_name, "wt"))
|
||||
self.section_sinks[section_name] = sink
|
||||
|
||||
def get_section_code_sink(self, section_name):
|
||||
return self.section_sinks[section_name]
|
||||
|
||||
def get_main_code_sink(self):
|
||||
return self.main_sink
|
||||
|
||||
def get_common_header_code_sink(self):
|
||||
return self.header_sink
|
||||
|
||||
def get_common_header_include(self):
|
||||
return '"%s"' % self.header_name
|
||||
|
||||
def close(self):
|
||||
self.header_sink.file.close()
|
||||
self.main_sink.file.close()
|
||||
for sink in self.section_sinks.itervalues():
|
||||
sink.file.close()
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
out = MyMultiSectionFactory(sys.argv[1], sys.argv[3:])
|
||||
root_module = module_init()
|
||||
root_module.add_include('"everything.h"')
|
||||
|
||||
register_types(root_module)
|
||||
|
||||
ns3modulegen_core_customizations.Simulator_customizations(root_module)
|
||||
ns3modulegen_core_customizations.CommandLine_customizations(root_module)
|
||||
ns3modulegen_core_customizations.TypeId_customizations(root_module)
|
||||
ns3modulegen_core_customizations.add_std_ofstream(root_module)
|
||||
ns3modulegen_core_customizations.add_ipv4_address_tp_hash(root_module)
|
||||
|
||||
|
||||
for local_module in LOCAL_MODULES:
|
||||
mod = __import__(local_module)
|
||||
mod.register_types(root_module)
|
||||
|
||||
ns3modulegen_core_customizations.generate_callback_classes(root_module.after_forward_declarations,
|
||||
callbacks_list.callback_classes)
|
||||
|
||||
|
||||
register_methods(root_module)
|
||||
|
||||
for local_module in LOCAL_MODULES:
|
||||
mod = __import__(local_module)
|
||||
mod.register_methods(root_module)
|
||||
|
||||
ns3modulegen_core_customizations.Object_customizations(root_module)
|
||||
ns3modulegen_core_customizations.Attribute_customizations(root_module)
|
||||
|
||||
register_functions(root_module)
|
||||
|
||||
for local_module in LOCAL_MODULES:
|
||||
mod = __import__(local_module)
|
||||
mod.register_functions(root_module)
|
||||
|
||||
enabled_features = os.environ['NS3_ENABLED_FEATURES'].split(',')
|
||||
|
||||
# if GtkConfigStore support is disabled, disable the class wrapper
|
||||
if 'GtkConfigStore' not in enabled_features:
|
||||
try:
|
||||
root_module.classes.remove(root_module['ns3::GtkConfigStore'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# if no sqlite, the class SqliteDataOutput is disabled
|
||||
if 'SqliteDataOutput' not in enabled_features:
|
||||
try:
|
||||
root_module.classes.remove(root_module['ns3::SqliteDataOutput'])
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if 'Threading' not in enabled_features:
|
||||
for clsname in ['SystemThread', 'SystemMutex', 'SystemCondition', 'CriticalSection',
|
||||
'SimpleRefCount< ns3::SystemThread, ns3::empty, ns3::DefaultDeleter<ns3::SystemThread> >']:
|
||||
root_module.classes.remove(root_module['ns3::%s' % clsname])
|
||||
|
||||
if 'EmuNetDevice' not in enabled_features:
|
||||
for clsname in ['EmuNetDevice', 'EmuHelper']:
|
||||
root_module.classes.remove(root_module['ns3::%s' % clsname])
|
||||
root_module.enums.remove(root_module['ns3::EmuNetDevice::EncapsulationMode'])
|
||||
|
||||
if 'RealTime' not in enabled_features:
|
||||
for clsname in ['WallClockSynchronizer', 'RealtimeSimulatorImpl']:
|
||||
root_module.classes.remove(root_module['ns3::%s' % clsname])
|
||||
root_module.enums.remove(root_module['ns3::RealtimeSimulatorImpl::SynchronizationMode'])
|
||||
|
||||
if 'TapBridge' not in enabled_features:
|
||||
for clsname in ['TapBridge', 'TapBridgeHelper', 'TapBridgeFdReader']:
|
||||
root_module.classes.remove(root_module['ns3::%s' % clsname])
|
||||
root_module.enums.remove(root_module['ns3::TapBridge::Mode'])
|
||||
|
||||
root_module.generate(out, '_ns3')
|
||||
|
||||
out.close()
|
||||
|
||||
if __name__ == '__main__':
|
||||
if 0:
|
||||
try:
|
||||
import cProfile as profile
|
||||
except ImportError:
|
||||
main()
|
||||
else:
|
||||
print >> sys.stderr, "** running under profiler"
|
||||
profile.run('main()', 'ns3modulegen.pstat')
|
||||
else:
|
||||
main()
|
||||
|
||||
@@ -1,436 +0,0 @@
|
||||
from __future__ import print_function
|
||||
import sys
|
||||
import re
|
||||
|
||||
from pybindgen.typehandlers import base as typehandlers
|
||||
from pybindgen import ReturnValue, Parameter
|
||||
from pybindgen.cppmethod import CustomCppMethodWrapper, CustomCppConstructorWrapper
|
||||
from pybindgen.typehandlers.codesink import MemoryCodeSink
|
||||
from pybindgen.typehandlers import ctypeparser
|
||||
from pybindgen.typehandlers.base import ForwardWrapperBase
|
||||
from pybindgen import cppclass
|
||||
import warnings
|
||||
|
||||
from pybindgen.typehandlers.base import CodeGenerationError
|
||||
|
||||
import sys
|
||||
|
||||
class SmartPointerTransformation(typehandlers.TypeTransformation):
|
||||
"""
|
||||
This class provides a "type transformation" that tends to support
|
||||
NS-3 smart pointers. Parameters such as "Ptr<Foo> foo" are
|
||||
transformed into something like Parameter.new("Foo*", "foo",
|
||||
transfer_ownership=False). Return values such as Ptr<Foo> are
|
||||
transformed into ReturnValue.new("Foo*",
|
||||
caller_owns_return=False). Since the underlying objects have
|
||||
reference counting, PyBindGen does the right thing.
|
||||
"""
|
||||
def __init__(self):
|
||||
super(SmartPointerTransformation, self).__init__()
|
||||
self.rx = re.compile(r'(ns3::|::ns3::|)Ptr<([^>]+)>\s*$')
|
||||
print("{0!r}".format(self), file=sys.stderr)
|
||||
|
||||
def _get_untransformed_type_traits(self, name):
|
||||
m = self.rx.match(name)
|
||||
is_const = False
|
||||
if m is None:
|
||||
print("{0!r} did not match".format(name), file=sys.stderr)
|
||||
return None, False
|
||||
else:
|
||||
name1 = m.group(2).strip()
|
||||
if name1.startswith('const '):
|
||||
name1 = name1[len('const '):]
|
||||
is_const = True
|
||||
if name1.endswith(' const'):
|
||||
name1 = name1[:-len(' const')]
|
||||
is_const = True
|
||||
new_name = name1+' *'
|
||||
|
||||
if new_name.startswith('::'):
|
||||
new_name = new_name[2:]
|
||||
return new_name, is_const
|
||||
|
||||
def get_untransformed_name(self, name):
|
||||
new_name, dummy_is_const = self._get_untransformed_type_traits(name)
|
||||
return new_name
|
||||
|
||||
def create_type_handler(self, type_handler, *args, **kwargs):
|
||||
if issubclass(type_handler, Parameter):
|
||||
kwargs['transfer_ownership'] = False
|
||||
elif issubclass(type_handler, ReturnValue):
|
||||
kwargs['caller_owns_return'] = False
|
||||
else:
|
||||
raise AssertionError
|
||||
|
||||
## fix the ctype, add ns3:: namespace
|
||||
orig_ctype, is_const = self._get_untransformed_type_traits(args[0])
|
||||
if is_const:
|
||||
correct_ctype = 'ns3::Ptr< {0} const >'.format(orig_ctype[:-2])
|
||||
else:
|
||||
correct_ctype = 'ns3::Ptr< {0} >'.format(orig_ctype[:-2])
|
||||
args = tuple([correct_ctype] + list(args[1:]))
|
||||
|
||||
handler = type_handler(*args, **kwargs)
|
||||
handler.set_tranformation(self, orig_ctype)
|
||||
return handler
|
||||
|
||||
def untransform(self, type_handler, declarations, code_block, expression):
|
||||
return 'const_cast<%s> (ns3::PeekPointer (%s))' % (type_handler.untransformed_ctype, expression)
|
||||
|
||||
def transform(self, type_handler, declarations, code_block, expression):
|
||||
assert type_handler.untransformed_ctype[-1] == '*'
|
||||
return 'ns3::Ptr< %s > (%s)' % (type_handler.untransformed_ctype[:-1], expression)
|
||||
|
||||
## register the type transformation
|
||||
transf = SmartPointerTransformation()
|
||||
typehandlers.return_type_matcher.register_transformation(transf)
|
||||
typehandlers.param_type_matcher.register_transformation(transf)
|
||||
del transf
|
||||
|
||||
|
||||
class CallbackImplProxyMethod(typehandlers.ReverseWrapperBase):
|
||||
"""
|
||||
Class that generates a proxy virtual method that calls a similarly named python method.
|
||||
"""
|
||||
|
||||
def __init__(self, return_value, parameters):
|
||||
super(CallbackImplProxyMethod, self).__init__(return_value, parameters)
|
||||
|
||||
def generate_python_call(self):
|
||||
"""code to call the python method"""
|
||||
build_params = self.build_params.get_parameters(force_tuple_creation=True)
|
||||
if build_params[0][0] == '"':
|
||||
build_params[0] = '(char *) ' + build_params[0]
|
||||
args = self.before_call.declare_variable('PyObject*', 'args')
|
||||
self.before_call.write_code('%s = Py_BuildValue(%s);'
|
||||
% (args, ', '.join(build_params)))
|
||||
self.before_call.add_cleanup_code('Py_DECREF(%s);' % args)
|
||||
self.before_call.write_code('py_retval = PyObject_CallObject(m_callback, %s);' % args)
|
||||
self.before_call.write_error_check('py_retval == NULL')
|
||||
self.before_call.add_cleanup_code('Py_DECREF(py_retval);')
|
||||
|
||||
|
||||
|
||||
|
||||
def register_callback_classes(out, callbacks):
|
||||
for callback_impl_num, template_parameters in enumerate(callbacks):
|
||||
cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
|
||||
#print >> sys.stderr, "***** trying to register callback: %r" % cls_name
|
||||
class_name = "PythonCallbackImpl%i" % callback_impl_num
|
||||
|
||||
class PythonCallbackParameter(Parameter):
|
||||
"Class handlers"
|
||||
CTYPES = [cls_name]
|
||||
print("***** registering callback handler: %r (%r)" % (ctypeparser.normalize_type_string(cls_name), cls_name), file=sys.stderr)
|
||||
DIRECTIONS = [Parameter.DIRECTION_IN]
|
||||
PYTHON_CALLBACK_IMPL_NAME = class_name
|
||||
TEMPLATE_ARGS = template_parameters
|
||||
DISABLED = False
|
||||
|
||||
def convert_python_to_c(self, wrapper):
|
||||
"parses python args to get C++ value"
|
||||
assert isinstance(wrapper, typehandlers.ForwardWrapperBase)
|
||||
|
||||
if self.DISABLED:
|
||||
raise CodeGenerationError("wrapper could not be generated")
|
||||
|
||||
if self.default_value is None:
|
||||
py_callback = wrapper.declarations.declare_variable('PyObject*', self.name)
|
||||
wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name)
|
||||
wrapper.before_call.write_error_check(
|
||||
'!PyCallable_Check(%s)' % py_callback,
|
||||
'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
|
||||
callback_impl = wrapper.declarations.declare_variable(
|
||||
'ns3::Ptr<%s>' % self.PYTHON_CALLBACK_IMPL_NAME,
|
||||
'%s_cb_impl' % self.name)
|
||||
wrapper.before_call.write_code("%s = ns3::Create<%s> (%s);"
|
||||
% (callback_impl, self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
|
||||
wrapper.call_params.append(
|
||||
'ns3::Callback<%s> (%s)' % (', '.join(self.TEMPLATE_ARGS), callback_impl))
|
||||
else:
|
||||
py_callback = wrapper.declarations.declare_variable('PyObject*', self.name, 'NULL')
|
||||
wrapper.parse_params.add_parameter('O', ['&'+py_callback], self.name, optional=True)
|
||||
value = wrapper.declarations.declare_variable(
|
||||
'ns3::Callback<%s>' % ', '.join(self.TEMPLATE_ARGS),
|
||||
self.name+'_value',
|
||||
self.default_value)
|
||||
|
||||
wrapper.before_call.write_code("if (%s) {" % (py_callback,))
|
||||
wrapper.before_call.indent()
|
||||
|
||||
wrapper.before_call.write_error_check(
|
||||
'!PyCallable_Check(%s)' % py_callback,
|
||||
'PyErr_SetString(PyExc_TypeError, "parameter \'%s\' must be callbale");' % self.name)
|
||||
|
||||
wrapper.before_call.write_code("%s = ns3::Callback<%s> (ns3::Create<%s> (%s));"
|
||||
% (value, ', '.join(self.TEMPLATE_ARGS),
|
||||
self.PYTHON_CALLBACK_IMPL_NAME, py_callback))
|
||||
|
||||
wrapper.before_call.unindent()
|
||||
wrapper.before_call.write_code("}") # closes: if (py_callback) {
|
||||
|
||||
wrapper.call_params.append(value)
|
||||
|
||||
|
||||
def convert_c_to_python(self, wrapper):
|
||||
raise typehandlers.NotSupportedError("Reverse wrappers for ns3::Callback<...> types "
|
||||
"(python using callbacks defined in C++) not implemented.")
|
||||
|
||||
|
||||
def generate_callback_classes(module, callbacks):
|
||||
out = module.after_forward_declarations
|
||||
for callback_impl_num, template_parameters in enumerate(callbacks):
|
||||
sink = MemoryCodeSink()
|
||||
cls_name = "ns3::Callback< %s >" % ', '.join(template_parameters)
|
||||
#print >> sys.stderr, "***** trying to register callback: %r" % cls_name
|
||||
class_name = "PythonCallbackImpl%i" % callback_impl_num
|
||||
sink.writeln('''
|
||||
class %s : public ns3::CallbackImpl<%s>
|
||||
{
|
||||
public:
|
||||
PyObject *m_callback;
|
||||
%s(PyObject *callback)
|
||||
{
|
||||
Py_INCREF(callback);
|
||||
m_callback = callback;
|
||||
}
|
||||
virtual ~%s()
|
||||
{
|
||||
PyGILState_STATE __py_gil_state;
|
||||
__py_gil_state = (PyEval_ThreadsInitialized() ? PyGILState_Ensure() : (PyGILState_STATE) 0);
|
||||
Py_DECREF(m_callback);
|
||||
m_callback = NULL;
|
||||
PyGILState_Release(__py_gil_state);
|
||||
}
|
||||
|
||||
virtual bool IsEqual(ns3::Ptr<const ns3::CallbackImplBase> other_base) const
|
||||
{
|
||||
const %s *other = dynamic_cast<const %s*> (ns3::PeekPointer (other_base));
|
||||
if (other != NULL)
|
||||
return (other->m_callback == m_callback);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
''' % (class_name, ', '.join(template_parameters), class_name, class_name, class_name, class_name))
|
||||
sink.indent()
|
||||
callback_return = template_parameters[0]
|
||||
return_ctype = ctypeparser.parse_type(callback_return)
|
||||
if ('const' in return_ctype.remove_modifiers()):
|
||||
kwargs = {'is_const': True}
|
||||
else:
|
||||
kwargs = {}
|
||||
try:
|
||||
return_type = ReturnValue.new(str(return_ctype), **kwargs)
|
||||
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
|
||||
warnings.warn("***** Unable to register callback; Return value '%s' error (used in %s): %r"
|
||||
% (callback_return, cls_name, ex),
|
||||
Warning)
|
||||
continue
|
||||
|
||||
arguments = []
|
||||
ok = True
|
||||
callback_parameters = [arg for arg in template_parameters[1:] if arg != 'ns3::empty']
|
||||
for arg_num, arg_type in enumerate(callback_parameters):
|
||||
arg_name = 'arg%i' % (arg_num+1)
|
||||
|
||||
param_ctype = ctypeparser.parse_type(arg_type)
|
||||
if ('const' in param_ctype.remove_modifiers()):
|
||||
kwargs = {'is_const': True}
|
||||
else:
|
||||
kwargs = {}
|
||||
try:
|
||||
arguments.append(Parameter.new(str(param_ctype), arg_name, **kwargs))
|
||||
except (typehandlers.TypeLookupError, typehandlers.TypeConfigurationError) as ex:
|
||||
warnings.warn("***** Unable to register callback; parameter '%s %s' error (used in %s): %r"
|
||||
% (arg_type, arg_name, cls_name, ex),
|
||||
Warning)
|
||||
ok = False
|
||||
if not ok:
|
||||
try:
|
||||
typehandlers.return_type_matcher.lookup(cls_name)[0].DISABLED = True
|
||||
except typehandlers.TypeLookupError:
|
||||
pass
|
||||
try:
|
||||
typehandlers.param_type_matcher.lookup(cls_name)[0].DISABLED = True
|
||||
except typehandlers.TypeLookupError:
|
||||
pass
|
||||
continue
|
||||
|
||||
wrapper = CallbackImplProxyMethod(return_type, arguments)
|
||||
wrapper.generate(sink, 'operator()', decl_modifiers=[])
|
||||
|
||||
sink.unindent()
|
||||
sink.writeln('};\n')
|
||||
print("Flushing to ", out, file=sys.stderr)
|
||||
sink.flush_to(out)
|
||||
|
||||
|
||||
# def write_preamble(out):
|
||||
# pybindgen.write_preamble(out)
|
||||
# out.writeln("#include \"ns3/everything.h\"")
|
||||
|
||||
|
||||
|
||||
def Simulator_customizations(module):
|
||||
Simulator = module['ns3::Simulator']
|
||||
|
||||
## Simulator::Schedule(delay, callback, ...user..args...)
|
||||
Simulator.add_custom_method_wrapper("Schedule", "_wrap_Simulator_Schedule",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
|
||||
|
||||
|
||||
## Simulator::ScheduleNow(callback, ...user..args...)
|
||||
Simulator.add_custom_method_wrapper("ScheduleNow", "_wrap_Simulator_ScheduleNow",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
|
||||
|
||||
|
||||
## Simulator::ScheduleDestroy(callback, ...user..args...)
|
||||
Simulator.add_custom_method_wrapper("ScheduleDestroy", "_wrap_Simulator_ScheduleDestroy",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
|
||||
|
||||
Simulator.add_custom_method_wrapper("Run", "_wrap_Simulator_Run",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
|
||||
|
||||
|
||||
def CommandLine_customizations(module):
|
||||
CommandLine = module['ns3::CommandLine']
|
||||
CommandLine.add_method('Parse', None, [ArgvParam(None, 'argv')],
|
||||
is_static=False)
|
||||
CommandLine.add_custom_method_wrapper("AddValue", "_wrap_CommandLine_AddValue",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS"])
|
||||
|
||||
|
||||
def Object_customizations(module):
|
||||
## ---------------------------------------------------------------------
|
||||
## Here we generate custom constructor code for all classes that
|
||||
## derive from ns3::Object. The custom constructors are needed in
|
||||
## order to support kwargs only and to translate kwargs into ns3
|
||||
## attributes, etc.
|
||||
## ---------------------------------------------------------------------
|
||||
try:
|
||||
Object = module['ns3::Object']
|
||||
except KeyError:
|
||||
return
|
||||
|
||||
## add a GetTypeId method to all generatd helper classes
|
||||
def helper_class_hook(helper_class):
|
||||
decl = """
|
||||
static ns3::TypeId GetTypeId (void)
|
||||
{
|
||||
static ns3::TypeId tid = ns3::TypeId ("%s")
|
||||
.SetParent< %s > ()
|
||||
;
|
||||
return tid;
|
||||
}""" % (helper_class.name, helper_class.class_.full_name)
|
||||
|
||||
helper_class.add_custom_method(decl)
|
||||
helper_class.add_post_generation_code(
|
||||
"NS_OBJECT_ENSURE_REGISTERED (%s);" % helper_class.name)
|
||||
Object.add_helper_class_hook(helper_class_hook)
|
||||
|
||||
def ns3_object_instance_creation_function(cpp_class, code_block, lvalue,
|
||||
parameters, construct_type_name):
|
||||
assert lvalue
|
||||
assert not lvalue.startswith('None')
|
||||
if cpp_class.cannot_be_constructed:
|
||||
raise CodeGenerationError("%s cannot be constructed (%s)"
|
||||
% cpp_class.full_name)
|
||||
if cpp_class.incomplete_type:
|
||||
raise CodeGenerationError("%s cannot be constructed (incomplete type)"
|
||||
% cpp_class.full_name)
|
||||
code_block.write_code("%s = new %s(%s);" % (lvalue, construct_type_name, parameters))
|
||||
code_block.write_code("%s->Ref ();" % (lvalue))
|
||||
|
||||
def ns3_object_post_instance_creation_function(cpp_class, code_block, lvalue,
|
||||
parameters, construct_type_name):
|
||||
code_block.write_code("ns3::CompleteConstruct(%s);" % (lvalue, ))
|
||||
|
||||
Object.set_instance_creation_function(ns3_object_instance_creation_function)
|
||||
Object.set_post_instance_creation_function(ns3_object_post_instance_creation_function)
|
||||
|
||||
|
||||
def Attribute_customizations(module):
|
||||
# Fix up for the "const AttributeValue &v = EmptyAttribute()"
|
||||
# case, as used extensively by helper classes.
|
||||
|
||||
# Here's why we need to do this: pybindgen.gccxmlscanner, when
|
||||
# scanning parameter default values, is only provided with the
|
||||
# value as a simple C expression string. (py)gccxml does not
|
||||
# report the type of the default value.
|
||||
|
||||
# As a workaround, here we iterate over all parameters of all
|
||||
# methods of all classes and tell pybindgen what is the type of
|
||||
# the default value for attributes.
|
||||
|
||||
for cls in module.classes:
|
||||
for meth in cls.get_all_methods():
|
||||
for param in meth.parameters:
|
||||
if isinstance(param, cppclass.CppClassRefParameter):
|
||||
if param.cpp_class.name == 'AttributeValue' \
|
||||
and param.default_value is not None \
|
||||
and param.default_value_type is None:
|
||||
param.default_value_type = 'ns3::EmptyAttributeValue'
|
||||
|
||||
|
||||
def TypeId_customizations(module):
|
||||
TypeId = module['ns3::TypeId']
|
||||
TypeId.add_custom_method_wrapper("LookupByNameFailSafe", "_wrap_TypeId_LookupByNameFailSafe",
|
||||
flags=["METH_VARARGS", "METH_KEYWORDS", "METH_STATIC"])
|
||||
|
||||
|
||||
def add_std_ofstream(module):
|
||||
module.add_include('<fstream>')
|
||||
ostream = module.add_class('ostream', foreign_cpp_namespace='::std')
|
||||
ostream.set_cannot_be_constructed("abstract base class")
|
||||
ofstream = module.add_class('ofstream', foreign_cpp_namespace='::std', parent=ostream)
|
||||
ofstream.add_enum('openmode', [
|
||||
('app', 'std::ios_base::app'),
|
||||
('ate', 'std::ios_base::ate'),
|
||||
('binary', 'std::ios_base::binary'),
|
||||
('in', 'std::ios_base::in'),
|
||||
('out', 'std::ios_base::out'),
|
||||
('trunc', 'std::ios_base::trunc'),
|
||||
])
|
||||
ofstream.add_constructor([Parameter.new("const char *", 'filename'),
|
||||
Parameter.new("::std::ofstream::openmode", 'mode', default_value="std::ios_base::out")])
|
||||
ofstream.add_method('close', None, [])
|
||||
|
||||
add_std_ios_openmode(module)
|
||||
|
||||
|
||||
class IosOpenmodeParam(Parameter):
|
||||
|
||||
DIRECTIONS = [Parameter.DIRECTION_IN]
|
||||
CTYPES = ['std::ios::openmode', 'std::_Ios_Openmode']
|
||||
|
||||
def convert_c_to_python(self, wrapper):
|
||||
assert isinstance(wrapper, ReverseWrapperBase)
|
||||
wrapper.build_params.add_parameter('i', [self.value])
|
||||
|
||||
def convert_python_to_c(self, wrapper):
|
||||
assert isinstance(wrapper, ForwardWrapperBase)
|
||||
name = wrapper.declarations.declare_variable("std::ios::openmode", self.name, self.default_value)
|
||||
wrapper.parse_params.add_parameter('i', ['&'+name], self.name, optional=bool(self.default_value))
|
||||
wrapper.call_params.append(name)
|
||||
|
||||
|
||||
|
||||
def add_std_ios_openmode(module):
|
||||
for flag in 'in', 'out', 'ate', 'app', 'trunc', 'binary':
|
||||
module.after_init.write_code('PyModule_AddIntConstant(m, (char *) "STD_IOS_%s", std::ios::%s);'
|
||||
% (flag.upper(), flag))
|
||||
|
||||
|
||||
|
||||
def add_ipv4_address_tp_hash(module):
|
||||
module.body.writeln('''
|
||||
long
|
||||
_ns3_Ipv4Address_tp_hash (PyObject *obj)
|
||||
{
|
||||
PyNs3Ipv4Address *addr = reinterpret_cast<PyNs3Ipv4Address *> (obj);
|
||||
return static_cast<long> (ns3::Ipv4AddressHash () (*addr->obj));
|
||||
}
|
||||
''')
|
||||
module.header.writeln('long _ns3_Ipv4Address_tp_hash (PyObject *obj);')
|
||||
module['Ipv4Address'].pytype.slots['tp_hash'] = "_ns3_Ipv4Address_tp_hash"
|
||||
@@ -1,281 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
import pybindgen.settings
|
||||
from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning, find_declaration_from_name
|
||||
from pybindgen.typehandlers.codesink import FileCodeSink
|
||||
from pygccxml.declarations import templates
|
||||
from pygccxml.declarations.enumeration import enumeration_t
|
||||
from pygccxml.declarations.class_declaration import class_t
|
||||
from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t, calldef_t
|
||||
|
||||
|
||||
## we need the smart pointer type transformation to be active even
|
||||
## during gccxml scanning.
|
||||
import ns3modulegen_core_customizations
|
||||
|
||||
|
||||
## silence gccxmlparser errors; we only want error handling in the
|
||||
## generated python script, not while scanning.
|
||||
class ErrorHandler(pybindgen.settings.ErrorHandler):
|
||||
def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_):
|
||||
return True
|
||||
pybindgen.settings.error_handler = ErrorHandler()
|
||||
import warnings
|
||||
warnings.filterwarnings(category=WrapperWarning, action='ignore')
|
||||
|
||||
|
||||
import ns3modulescan
|
||||
type_annotations = ns3modulescan.type_annotations
|
||||
|
||||
|
||||
def get_ns3_relative_path(path):
|
||||
l = []
|
||||
head = path
|
||||
while head:
|
||||
new_head, tail = os.path.split(head)
|
||||
if new_head == head:
|
||||
raise ValueError
|
||||
head = new_head
|
||||
if tail == 'ns3':
|
||||
return os.path.join(*l)
|
||||
l.insert(0, tail)
|
||||
raise AssertionError("is the path %r inside ns3?!" % path)
|
||||
|
||||
class PreScanHook:
|
||||
|
||||
def __init__(self, headers_map, module):
|
||||
self.headers_map = headers_map
|
||||
self.module = module
|
||||
|
||||
def __call__(self, module_parser,
|
||||
pygccxml_definition,
|
||||
global_annotations,
|
||||
parameter_annotations):
|
||||
try:
|
||||
ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
|
||||
except ValueError: # the header is not from ns3
|
||||
return # ignore the definition, it's not ns-3 def.
|
||||
|
||||
definition_module = self.headers_map[ns3_header]
|
||||
|
||||
## Note: we don't include line numbers in the comments because
|
||||
## those numbers are very likely to change frequently, which would
|
||||
## cause needless changes, since the generated python files are
|
||||
## kept under version control.
|
||||
|
||||
#global_annotations['pygen_comment'] = "%s:%i: %s" % \
|
||||
# (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
|
||||
global_annotations['pygen_comment'] = "%s (module %r): %s" % \
|
||||
(ns3_header, definition_module, pygccxml_definition)
|
||||
|
||||
|
||||
## handle ns3::Object::GetObject (left to its own devices,
|
||||
## pybindgen will generate a mangled name containing the template
|
||||
## argument type name).
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Object' \
|
||||
and pygccxml_definition.name == 'GetObject':
|
||||
template_args = templates.args(pygccxml_definition.demangled_name)
|
||||
if template_args == ['ns3::Object']:
|
||||
global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
|
||||
|
||||
## Don't wrap Simulator::Schedule* (manually wrapped)
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Simulator' \
|
||||
and pygccxml_definition.name.startswith('Schedule'):
|
||||
global_annotations['ignore'] = None
|
||||
|
||||
# manually wrapped
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Simulator' \
|
||||
and pygccxml_definition.name == 'Run':
|
||||
global_annotations['ignore'] = True
|
||||
|
||||
## http://www.gccxml.org/Bug/view.php?id=9915
|
||||
if isinstance(pygccxml_definition, calldef_t):
|
||||
for arg in pygccxml_definition.arguments:
|
||||
if arg.default_value is None:
|
||||
continue
|
||||
elif arg.default_value == "ns3::MilliSeconds( )":
|
||||
arg.default_value = "ns3::MilliSeconds(0)"
|
||||
elif arg.default_value == "ns3::Seconds( )":
|
||||
arg.default_value = "ns3::Seconds(0)"
|
||||
|
||||
## classes
|
||||
if isinstance(pygccxml_definition, class_t):
|
||||
print >> sys.stderr, pygccxml_definition
|
||||
# no need for helper classes to allow subclassing in Python, I think...
|
||||
#if pygccxml_definition.name.endswith('Helper'):
|
||||
# global_annotations['allow_subclassing'] = 'false'
|
||||
|
||||
#
|
||||
# If a class is template instantiation, even if the
|
||||
# template was defined in some other module, if a template
|
||||
# argument belongs to this module then the template
|
||||
# instantiation will belong to this module.
|
||||
#
|
||||
if templates.is_instantiation(pygccxml_definition.decl_string):
|
||||
cls_name, template_parameters = templates.split(pygccxml_definition.name)
|
||||
template_parameters_decls = [find_declaration_from_name(module_parser.global_ns, templ_param)
|
||||
for templ_param in template_parameters]
|
||||
#print >> sys.stderr, "********************", cls_name, repr(template_parameters_decls)
|
||||
|
||||
template_parameters_modules = []
|
||||
for templ in template_parameters_decls:
|
||||
if not hasattr(templ, 'location'):
|
||||
continue
|
||||
try:
|
||||
h = get_ns3_relative_path(templ.location.file_name)
|
||||
except ValueError:
|
||||
continue
|
||||
template_parameters_modules.append(self.headers_map[h])
|
||||
|
||||
for templ_mod in template_parameters_modules:
|
||||
if templ_mod == self.module:
|
||||
definition_module = templ_mod
|
||||
break
|
||||
#print >> sys.stderr, "********************", cls_name, repr(template_parameters_modules)
|
||||
|
||||
|
||||
if definition_module != self.module:
|
||||
global_annotations['import_from_module'] = 'ns.%s' % (definition_module.replace('-', '_'),)
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::SimpleRefCount<'):
|
||||
global_annotations['incref_method'] = 'Ref'
|
||||
global_annotations['decref_method'] = 'Unref'
|
||||
global_annotations['peekref_method'] = 'GetReferenceCount'
|
||||
global_annotations['automatic_type_narrowing'] = 'true'
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
|
||||
# manually handled in ns3modulegen_core_customizations.py
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'):
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
|
||||
# handled by pybindgen "type transformation"
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
# table driven class customization
|
||||
try:
|
||||
annotations = type_annotations[pygccxml_definition.decl_string]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
global_annotations.update(annotations)
|
||||
|
||||
## enums
|
||||
if isinstance(pygccxml_definition, enumeration_t):
|
||||
if definition_module != self.module:
|
||||
global_annotations['import_from_module'] = 'ns.%s' % definition_module
|
||||
|
||||
## free functions
|
||||
if isinstance(pygccxml_definition, free_function_t):
|
||||
|
||||
if definition_module != self.module:
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
if pygccxml_definition.name == 'PeekPointer':
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
## table driven methods/constructors/functions customization
|
||||
if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
|
||||
try:
|
||||
annotations = type_annotations[str(pygccxml_definition)]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for key,value in annotations.items():
|
||||
if key == 'params':
|
||||
parameter_annotations.update (value)
|
||||
del annotations['params']
|
||||
global_annotations.update(annotations)
|
||||
|
||||
|
||||
# def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
|
||||
# ## classes
|
||||
# if isinstance(pybindgen_wrapper, CppClass):
|
||||
# if pybindgen_wrapper.name.endswith('Checker'):
|
||||
# print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
|
||||
# #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
|
||||
|
||||
|
||||
def scan_callback_classes(module_parser, callback_classes_file):
|
||||
callback_classes_file.write("callback_classes = [\n")
|
||||
for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
|
||||
recursive=False):
|
||||
if not cls.name.startswith("Callback<"):
|
||||
continue
|
||||
assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
|
||||
dummy_cls_name, template_parameters = templates.split(cls.decl_string)
|
||||
callback_classes_file.write(" %r,\n" % template_parameters)
|
||||
callback_classes_file.write("]\n")
|
||||
|
||||
|
||||
def ns3_module_scan(top_builddir, module_name, headers_map, output_file_name, cflags):
|
||||
module_parser = ModuleParser('ns.%s' % module_name.replace('-', '_'), 'ns3')
|
||||
module_parser.add_pre_scan_hook(PreScanHook(headers_map, module_name))
|
||||
#module_parser.add_post_scan_hook(post_scan_hook)
|
||||
|
||||
gccxml_options = dict(
|
||||
include_paths=[top_builddir],
|
||||
define_symbols={
|
||||
#'NS3_ASSERT_ENABLE': None,
|
||||
#'NS3_LOG_ENABLE': None,
|
||||
},
|
||||
cflags=('--gccxml-cxxflags "%s -DPYTHON_SCAN"' % cflags)
|
||||
)
|
||||
|
||||
try:
|
||||
os.unlink(output_file_name)
|
||||
except OSError:
|
||||
pass
|
||||
try:
|
||||
os.makedirs(os.path.dirname(output_file_name))
|
||||
except OSError:
|
||||
pass
|
||||
output_file = open(output_file_name, "wt")
|
||||
output_sink = FileCodeSink(output_file)
|
||||
|
||||
# if there exists a scan-header.h file in src/<module>/bindings,
|
||||
# scan it, otherwise scan ns3/xxxx-module.h.
|
||||
scan_header = os.path.join(os.path.dirname(output_file_name), "scan-header.h")
|
||||
if not os.path.exists(scan_header):
|
||||
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % module_name)
|
||||
|
||||
module_parser.parse_init([scan_header],
|
||||
None, whitelist_paths=[top_builddir],
|
||||
#includes=['"ns3/everything.h"'],
|
||||
pygen_sink=output_sink,
|
||||
gccxml_options=gccxml_options)
|
||||
module_parser.scan_types()
|
||||
|
||||
callback_classes_file = open(os.path.join(os.path.dirname(output_file_name), "callbacks_list.py"), "wt")
|
||||
scan_callback_classes(module_parser, callback_classes_file)
|
||||
callback_classes_file.close()
|
||||
|
||||
|
||||
module_parser.scan_methods()
|
||||
module_parser.scan_functions()
|
||||
module_parser.parse_finalize()
|
||||
|
||||
output_file.close()
|
||||
os.chmod(output_file_name, 0400)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
if len(sys.argv) != 6:
|
||||
print "ns3modulescan-modular.py top_builddir module_path module_headers output_file_name cflags"
|
||||
sys.exit(1)
|
||||
ns3_module_scan(sys.argv[1], sys.argv[2], eval(sys.argv[3]), sys.argv[4], sys.argv[5])
|
||||
sys.exit(0)
|
||||
@@ -1,333 +0,0 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
import sys
|
||||
import os.path
|
||||
|
||||
import pybindgen.settings
|
||||
from pybindgen.gccxmlparser import ModuleParser, PygenClassifier, PygenSection, WrapperWarning
|
||||
from pybindgen.typehandlers.codesink import FileCodeSink
|
||||
from pygccxml.declarations import templates
|
||||
from pygccxml.declarations.class_declaration import class_t
|
||||
from pygccxml.declarations.calldef import free_function_t, member_function_t, constructor_t, calldef_t
|
||||
|
||||
|
||||
## we need the smart pointer type transformation to be active even
|
||||
## during gccxml scanning.
|
||||
import ns3modulegen_core_customizations
|
||||
|
||||
|
||||
## silence gccxmlparser errors; we only want error handling in the
|
||||
## generated python script, not while scanning.
|
||||
class ErrorHandler(pybindgen.settings.ErrorHandler):
|
||||
def handle_error(self, dummy_wrapper, dummy_exception, dummy_traceback_):
|
||||
return True
|
||||
pybindgen.settings.error_handler = ErrorHandler()
|
||||
import warnings
|
||||
warnings.filterwarnings(category=WrapperWarning, action='ignore')
|
||||
|
||||
type_annotations = {
|
||||
'::ns3::AttributeChecker': {
|
||||
'automatic_type_narrowing': 'true',
|
||||
'allow_subclassing': 'false',
|
||||
},
|
||||
'::ns3::AttributeValue': {
|
||||
'automatic_type_narrowing': 'true',
|
||||
'allow_subclassing': 'false',
|
||||
},
|
||||
|
||||
'::ns3::CommandLine': {
|
||||
'allow_subclassing': 'true', # needed so that AddValue is able to set attributes on the object
|
||||
},
|
||||
|
||||
'::ns3::NscTcpL4Protocol': {
|
||||
'ignore': 'true', # this class is implementation detail
|
||||
},
|
||||
|
||||
|
||||
'ns3::RandomVariable::RandomVariable(ns3::RandomVariableBase const & variable) [constructor]': {
|
||||
'ignore': None,
|
||||
},
|
||||
'ns3::RandomVariableBase * ns3::RandomVariable::Peek() const [member function]': {
|
||||
'ignore': None,
|
||||
},
|
||||
'void ns3::RandomVariable::GetSeed(uint32_t * seed) const [member function]': {
|
||||
'params': {'seed':{'direction':'out',
|
||||
'array_length':'6'}}
|
||||
},
|
||||
'bool ns3::TypeId::LookupAttributeByName(std::string name, ns3::TypeId::AttributeInformation * info) const [member function]': {
|
||||
'params': {'info':{'transfer_ownership': 'false'}}
|
||||
},
|
||||
'static bool ns3::TypeId::LookupByNameFailSafe(std::string name, ns3::TypeId * tid) [member function]': {
|
||||
'ignore': None, # manually wrapped in
|
||||
},
|
||||
'bool ns3::TraceSourceAccessor::ConnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
|
||||
'params': {'obj': {'transfer_ownership':'false'}}
|
||||
},
|
||||
'bool ns3::TraceSourceAccessor::Connect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
|
||||
'params': {'obj': {'transfer_ownership':'false'}}
|
||||
},
|
||||
'bool ns3::TraceSourceAccessor::DisconnectWithoutContext(ns3::ObjectBase * obj, ns3::CallbackBase const & cb) const [member function]': {
|
||||
'params': {'obj': {'transfer_ownership':'false'}}
|
||||
},
|
||||
'bool ns3::TraceSourceAccessor::Disconnect(ns3::ObjectBase * obj, std::string context, ns3::CallbackBase const & cb) const [member function]': {
|
||||
'params': {'obj': {'transfer_ownership':'false'}}
|
||||
},
|
||||
'bool ns3::AttributeAccessor::Set(ns3::ObjectBase * object, ns3::AttributeValue const & value) const [member function]': {
|
||||
'params': {'object': {'transfer_ownership':'false'}}
|
||||
},
|
||||
'ns3::EmpiricalVariable::EmpiricalVariable(ns3::RandomVariableBase const & variable) [constructor]': {
|
||||
'ignore': None
|
||||
},
|
||||
'static ns3::AttributeList * ns3::AttributeList::GetGlobal() [member function]': {
|
||||
'caller_owns_return': 'false'
|
||||
},
|
||||
'void ns3::CommandLine::Parse(int argc, char * * argv) const [member function]': {
|
||||
'ignore': None # manually wrapped
|
||||
},
|
||||
'extern void ns3::PythonCompleteConstruct(ns3::Ptr<ns3::Object> object, ns3::TypeId typeId, ns3::AttributeList const & attributes) [free function]': {
|
||||
'ignore': None # used transparently by, should not be wrapped
|
||||
},
|
||||
|
||||
'ns3::Ptr<ns3::Ipv4RoutingProtocol> ns3::Ipv4ListRouting::GetRoutingProtocol(uint32_t index, int16_t & priority) const [member function]': {
|
||||
'params': {'priority':{'direction':'out'}}
|
||||
},
|
||||
'ns3::Ipv4RoutingTableEntry * ns3::GlobalRouter::GetInjectedRoute(uint32_t i) [member function]': {
|
||||
'params': {'return': { 'caller_owns_return': 'false',}},
|
||||
},
|
||||
'ns3::Ipv4RoutingTableEntry * ns3::Ipv4GlobalRouting::GetRoute(uint32_t i) const [member function]': {
|
||||
'params': {'return': { 'caller_owns_return': 'false',}},
|
||||
},
|
||||
|
||||
'::ns3::TestCase': {
|
||||
'ignore': 'true', # we don't need to write test cases in Python
|
||||
},
|
||||
'::ns3::TestRunner': {
|
||||
'ignore': 'true', # we don't need to write test cases in Python
|
||||
},
|
||||
'::ns3::TestSuite': {
|
||||
'ignore': 'true', # we don't need to write test cases in Python
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
def get_ns3_relative_path(path):
|
||||
l = []
|
||||
head = path
|
||||
while head:
|
||||
head, tail = os.path.split(head)
|
||||
if tail == 'ns3':
|
||||
return os.path.join(*l)
|
||||
l.insert(0, tail)
|
||||
raise AssertionError("is the path %r inside ns3?!" % path)
|
||||
|
||||
|
||||
def pre_scan_hook(dummy_module_parser,
|
||||
pygccxml_definition,
|
||||
global_annotations,
|
||||
parameter_annotations):
|
||||
ns3_header = get_ns3_relative_path(pygccxml_definition.location.file_name)
|
||||
|
||||
## Note: we don't include line numbers in the comments because
|
||||
## those numbers are very likely to change frequently, which would
|
||||
## cause needless changes, since the generated python files are
|
||||
## kept under version control.
|
||||
|
||||
#global_annotations['pygen_comment'] = "%s:%i: %s" % \
|
||||
# (ns3_header, pygccxml_definition.location.line, pygccxml_definition)
|
||||
global_annotations['pygen_comment'] = "%s: %s" % \
|
||||
(ns3_header, pygccxml_definition)
|
||||
|
||||
|
||||
## handle ns3::Object::GetObject (left to its own devices,
|
||||
## pybindgen will generate a mangled name containing the template
|
||||
## argument type name).
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Object' \
|
||||
and pygccxml_definition.name == 'GetObject':
|
||||
template_args = templates.args(pygccxml_definition.demangled_name)
|
||||
if template_args == ['ns3::Object']:
|
||||
global_annotations['template_instance_names'] = 'ns3::Object=>GetObject'
|
||||
|
||||
## Don't wrap Simulator::Schedule* (manually wrapped)
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Simulator' \
|
||||
and pygccxml_definition.name.startswith('Schedule'):
|
||||
global_annotations['ignore'] = None
|
||||
|
||||
# manually wrapped
|
||||
if isinstance(pygccxml_definition, member_function_t) \
|
||||
and pygccxml_definition.parent.name == 'Simulator' \
|
||||
and pygccxml_definition.name == 'Run':
|
||||
global_annotations['ignore'] = True
|
||||
|
||||
## http://www.gccxml.org/Bug/view.php?id=9915
|
||||
if isinstance(pygccxml_definition, calldef_t):
|
||||
for arg in pygccxml_definition.arguments:
|
||||
if arg.default_value is None:
|
||||
continue
|
||||
if "ns3::MilliSeconds( )" == arg.default_value:
|
||||
arg.default_value = "ns3::MilliSeconds(0)"
|
||||
if "ns3::Seconds( )" == arg.default_value:
|
||||
arg.default_value = "ns3::Seconds(0)"
|
||||
|
||||
## classes
|
||||
if isinstance(pygccxml_definition, class_t):
|
||||
# no need for helper classes to allow subclassing in Python, I think...
|
||||
#if pygccxml_definition.name.endswith('Helper'):
|
||||
# global_annotations['allow_subclassing'] = 'false'
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::SimpleRefCount<'):
|
||||
global_annotations['incref_method'] = 'Ref'
|
||||
global_annotations['decref_method'] = 'Unref'
|
||||
global_annotations['peekref_method'] = 'GetReferenceCount'
|
||||
global_annotations['automatic_type_narrowing'] = 'true'
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::Callback<'):
|
||||
# manually handled in ns3modulegen_core_customizations.py
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::TracedCallback<'):
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
if pygccxml_definition.decl_string.startswith('::ns3::Ptr<'):
|
||||
# handled by pybindgen "type transformation"
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
# table driven class customization
|
||||
try:
|
||||
annotations = type_annotations[pygccxml_definition.decl_string]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
global_annotations.update(annotations)
|
||||
|
||||
## free functions
|
||||
if isinstance(pygccxml_definition, free_function_t):
|
||||
if pygccxml_definition.name == 'PeekPointer':
|
||||
global_annotations['ignore'] = None
|
||||
return
|
||||
|
||||
## table driven methods/constructors/functions customization
|
||||
if isinstance(pygccxml_definition, (free_function_t, member_function_t, constructor_t)):
|
||||
try:
|
||||
annotations = type_annotations[str(pygccxml_definition)]
|
||||
except KeyError:
|
||||
pass
|
||||
else:
|
||||
for key,value in annotations.items():
|
||||
if key == 'params':
|
||||
parameter_annotations.update (value)
|
||||
del annotations['params']
|
||||
global_annotations.update(annotations)
|
||||
|
||||
|
||||
# def post_scan_hook(dummy_module_parser, dummy_pygccxml_definition, pybindgen_wrapper):
|
||||
# ## classes
|
||||
# if isinstance(pybindgen_wrapper, CppClass):
|
||||
# if pybindgen_wrapper.name.endswith('Checker'):
|
||||
# print >> sys.stderr, "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!", pybindgen_wrapper
|
||||
# #pybindgen_wrapper.set_instance_creation_function(AttributeChecker_instance_creation_function)
|
||||
|
||||
|
||||
def scan_callback_classes(module_parser, callback_classes_file):
|
||||
callback_classes_file.write("callback_classes = [\n")
|
||||
for cls in module_parser.module_namespace.classes(function=module_parser.location_filter,
|
||||
recursive=False):
|
||||
if not cls.name.startswith("Callback<"):
|
||||
continue
|
||||
assert templates.is_instantiation(cls.decl_string), "%s is not a template instantiation" % cls
|
||||
dummy_cls_name, template_parameters = templates.split(cls.decl_string)
|
||||
callback_classes_file.write(" %r,\n" % template_parameters)
|
||||
callback_classes_file.write("]\n")
|
||||
|
||||
|
||||
class MyPygenClassifier(PygenClassifier):
|
||||
def __init__(self, headers_map, section_precendences):
|
||||
self.headers_map = headers_map
|
||||
self.section_precendences = section_precendences
|
||||
|
||||
def classify(self, pygccxml_definition):
|
||||
name = os.path.basename(pygccxml_definition.location.file_name)
|
||||
try:
|
||||
return self.headers_map[name]
|
||||
except KeyError:
|
||||
return '__main__'
|
||||
|
||||
def get_section_precedence(self, section_name):
|
||||
if section_name == '__main__':
|
||||
return -1
|
||||
return self.section_precendences[section_name]
|
||||
|
||||
|
||||
def ns3_module_scan(top_builddir, pygen_file_name, everything_h, cflags):
|
||||
|
||||
ns3_modules = eval(sys.stdin.readline())
|
||||
|
||||
## do a topological sort on the modules graph
|
||||
from topsort import topsort
|
||||
graph = []
|
||||
module_names = ns3_modules.keys()
|
||||
module_names.sort()
|
||||
for ns3_module_name in module_names:
|
||||
ns3_module_deps = list(ns3_modules[ns3_module_name][0])
|
||||
ns3_module_deps.sort()
|
||||
for dep in ns3_module_deps:
|
||||
graph.append((dep, ns3_module_name))
|
||||
sorted_ns3_modules = topsort(graph)
|
||||
#print >> sys.stderr, "******* topological sort: ", sorted_ns3_modules
|
||||
|
||||
sections = [PygenSection('__main__', FileCodeSink(open(pygen_file_name, "wt")))]
|
||||
headers_map = {} # header_name -> section_name
|
||||
section_precendences = {} # section_name -> precedence
|
||||
for prec, ns3_module in enumerate(sorted_ns3_modules):
|
||||
section_name = "ns3_module_%s" % ns3_module.replace('-', '_')
|
||||
file_name = os.path.join(os.path.dirname(pygen_file_name), "%s.py" % section_name)
|
||||
sections.append(PygenSection(section_name, FileCodeSink(open(file_name, "wt")),
|
||||
section_name + "__local"))
|
||||
for header in ns3_modules[ns3_module][1]:
|
||||
headers_map[header] = section_name
|
||||
section_precendences[section_name] = prec
|
||||
|
||||
module_parser = ModuleParser('ns3', 'ns3')
|
||||
|
||||
module_parser.add_pre_scan_hook(pre_scan_hook)
|
||||
#module_parser.add_post_scan_hook(post_scan_hook)
|
||||
|
||||
gccxml_options = dict(
|
||||
include_paths=[top_builddir],
|
||||
define_symbols={
|
||||
#'NS3_ASSERT_ENABLE': None,
|
||||
#'NS3_LOG_ENABLE': None,
|
||||
},
|
||||
cflags=('--gccxml-cxxflags "%s -DPYTHON_SCAN"' % cflags)
|
||||
)
|
||||
|
||||
module_parser.parse_init([everything_h],
|
||||
None, whitelist_paths=[top_builddir, os.path.dirname(everything_h)],
|
||||
#includes=['"ns3/everything.h"'],
|
||||
pygen_sink=sections,
|
||||
pygen_classifier=MyPygenClassifier(headers_map, section_precendences),
|
||||
gccxml_options=gccxml_options)
|
||||
module_parser.scan_types()
|
||||
|
||||
callback_classes_file = open(os.path.join(os.path.dirname(pygen_file_name), "callbacks_list.py"), "wt")
|
||||
scan_callback_classes(module_parser, callback_classes_file)
|
||||
callback_classes_file.close()
|
||||
|
||||
|
||||
module_parser.scan_methods()
|
||||
module_parser.scan_functions()
|
||||
module_parser.parse_finalize()
|
||||
|
||||
for section in sections:
|
||||
section.code_sink.file.close()
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ns3_module_scan(sys.argv[1], sys.argv[3], sys.argv[2], sys.argv[4])
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
placeholder
|
||||
@@ -1,909 +0,0 @@
|
||||
# Copyright (c) 2007 RADLogic
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
"""Provide various handy Python functions.
|
||||
|
||||
Running this script directly will execute the doctests.
|
||||
|
||||
Functions:
|
||||
int2bin(i, n) -- Convert integer to binary string.
|
||||
bin2int(bin_string) -- Convert binary string to integer.
|
||||
reverse(input_string) -- Reverse a string.
|
||||
transpose(matrix) -- Transpose a list of lists.
|
||||
polygon_area(points_list) -- Calculate the area of an arbitrary polygon.
|
||||
timestamp() -- Return string containing current time stamp.
|
||||
pt2str(point) -- Return prettier string version of point tuple.
|
||||
gcf(a, b) -- Return the greatest common factor of two numbers.
|
||||
lcm(a, b) -- Return the least common multiple of two numbers.
|
||||
permutations(input_list) -- Generate all permutations of a list of items.
|
||||
reduce_fraction(fraction) -- Reduce fraction (num, denom) to simplest form.
|
||||
quantile(l, p) -- Return p quantile of list l. E.g. p=0.25 for q1.
|
||||
trim(l) -- Discard values in list more than 1.5*IQR outside IQR.
|
||||
nice_units(value) -- Return value converted to human readable units.
|
||||
uniquify(seq) -- Return sequence with duplicate items in sequence seq removed.
|
||||
reverse_dict(d) -- Return the dictionary with the items as keys and vice-versa.
|
||||
lsb(x, n) -- Return the n least significant bits of x.
|
||||
gray_encode(i) -- Gray encode the given integer.
|
||||
random_vec(bits, max_value=None) -- Return a random binary vector.
|
||||
binary_range(bits) -- Return list of all possible binary numbers width=bits.
|
||||
float_range([start], stop, [step]) -- Return range of floats.
|
||||
find_common_fixes(s1, s2) -- Find common (prefix, suffix) of two strings.
|
||||
is_rotated(seq1, seq2) -- Return true if the list is a rotation of other list.
|
||||
getmodule(obj) -- Return the module that contains the object definition of obj.
|
||||
(use inspect.getmodule instead, though)
|
||||
get_args(argv) -- Store command-line args in a dictionary.
|
||||
|
||||
This module requires Python >= 2.2
|
||||
|
||||
"""
|
||||
__author__ = 'Tim Wegener <twegener@radlogic.com.au>'
|
||||
__date__ = '$Date: 2007/03/27 03:15:06 $'
|
||||
__version__ = '$Revision: 0.45 $'
|
||||
__credits__ = """
|
||||
David Chandler, for polygon area algorithm.
|
||||
(http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf)
|
||||
"""
|
||||
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import random
|
||||
|
||||
try:
|
||||
True, False
|
||||
except NameError:
|
||||
True, False = (1==1, 0==1)
|
||||
|
||||
|
||||
def int2bin(i, n):
|
||||
"""Convert decimal integer i to n-bit binary number (string).
|
||||
|
||||
>>> int2bin(0, 8)
|
||||
'00000000'
|
||||
|
||||
>>> int2bin(123, 8)
|
||||
'01111011'
|
||||
|
||||
>>> int2bin(123L, 8)
|
||||
'01111011'
|
||||
|
||||
>>> int2bin(15, 2)
|
||||
Traceback (most recent call last):
|
||||
ValueError: Value too large for given number of bits.
|
||||
|
||||
"""
|
||||
hex2bin = {'0': '0000', '1': '0001', '2': '0010', '3': '0011',
|
||||
'4': '0100', '5': '0101', '6': '0110', '7': '0111',
|
||||
'8': '1000', '9': '1001', 'a': '1010', 'b': '1011',
|
||||
'c': '1100', 'd': '1101', 'e': '1110', 'f': '1111'}
|
||||
# Convert to hex then map each hex digit to binary equivalent.
|
||||
result = ''.join([hex2bin[x] for x in hex(i).lower().replace('l','')[2:]])
|
||||
|
||||
# Shrink result to appropriate length.
|
||||
# Raise an error if the value is changed by the truncation.
|
||||
if '1' in result[:-n]:
|
||||
raise ValueError("Value too large for given number of bits.")
|
||||
result = result[-n:]
|
||||
# Zero-pad if length longer than mapped result.
|
||||
result = '0'*(n-len(result)) + result
|
||||
return result
|
||||
|
||||
|
||||
def bin2int(bin_string):
|
||||
"""Convert binary number string to decimal integer.
|
||||
|
||||
Note: Python > v2 has int(bin_string, 2)
|
||||
|
||||
>>> bin2int('1111')
|
||||
15
|
||||
|
||||
>>> bin2int('0101')
|
||||
5
|
||||
|
||||
"""
|
||||
## result = 0
|
||||
## bin_list = list(bin_string)
|
||||
## if len(filter(lambda x: x in ('1','0'), bin_list)) < len(bin_list):
|
||||
## raise Exception ("bin2int: Error - not a binary number: %s"
|
||||
## % bin_string)
|
||||
## bit_list = map(int, bin_list)
|
||||
## bit_list.reverse() # Make most significant bit have highest index.
|
||||
## for bit_place in range(len(bit_list)):
|
||||
## result = result + ((2**bit_place) * bit_list[bit_place])
|
||||
## return result
|
||||
return int(bin_string, 2)
|
||||
|
||||
|
||||
def reverse(input_string):
|
||||
"""Reverse a string. Useful for strings of binary numbers.
|
||||
|
||||
>>> reverse('abc')
|
||||
'cba'
|
||||
|
||||
"""
|
||||
str_list = list(input_string)
|
||||
str_list.reverse()
|
||||
return ''.join(str_list)
|
||||
|
||||
|
||||
def transpose(matrix):
|
||||
"""Transpose a list of lists.
|
||||
|
||||
>>> transpose([['a', 'b', 'c'], ['d', 'e', 'f'], ['g', 'h', 'i']])
|
||||
[['a', 'd', 'g'], ['b', 'e', 'h'], ['c', 'f', 'i']]
|
||||
|
||||
>>> transpose([['a', 'b', 'c'], ['d', 'e', 'f']])
|
||||
[['a', 'd'], ['b', 'e'], ['c', 'f']]
|
||||
|
||||
>>> transpose([['a', 'b'], ['d', 'e'], ['g', 'h']])
|
||||
[['a', 'd', 'g'], ['b', 'e', 'h']]
|
||||
|
||||
"""
|
||||
result = zip(*matrix)
|
||||
# Convert list of tuples to list of lists.
|
||||
# map is faster than a list comprehension since it is being used with
|
||||
# a built-in function as an argument.
|
||||
result = map(list, result)
|
||||
return result
|
||||
|
||||
|
||||
def polygon_area(points_list, precision=100):
|
||||
"""Calculate area of an arbitrary polygon using an algorithm from the web.
|
||||
|
||||
Return the area of the polygon as a positive float.
|
||||
|
||||
Arguments:
|
||||
points_list -- list of point tuples [(x0, y0), (x1, y1), (x2, y2), ...]
|
||||
(Unclosed polygons will be closed automatically.
|
||||
precision -- Internal arithmetic precision (integer arithmetic).
|
||||
|
||||
>>> polygon_area([(0, 0), (0, 1), (1, 1), (1, 2), (2, 2), (2, 0), (0, 0)])
|
||||
3.0
|
||||
|
||||
Credits:
|
||||
Area of a General Polygon by David Chandler
|
||||
http://www.davidchandler.com/AreaOfAGeneralPolygon.pdf
|
||||
|
||||
"""
|
||||
# Scale up co-ordinates and convert them to integers.
|
||||
for i in range(len(points_list)):
|
||||
points_list[i] = (int(points_list[i][0] * precision),
|
||||
int(points_list[i][1] * precision))
|
||||
# Close polygon if not closed.
|
||||
if points_list[-1] != points_list[0]:
|
||||
points_list.append(points_list[0])
|
||||
# Calculate area.
|
||||
area = 0
|
||||
for i in range(len(points_list)-1):
|
||||
(x_i, y_i) = points_list[i]
|
||||
(x_i_plus_1, y_i_plus_1) = points_list[i+1]
|
||||
area = area + (x_i_plus_1 * y_i) - (y_i_plus_1 * x_i)
|
||||
area = abs(area / 2)
|
||||
# Unscale area.
|
||||
area = float(area)/(precision**2)
|
||||
return area
|
||||
|
||||
|
||||
def timestamp():
|
||||
"""Return string containing current time stamp.
|
||||
|
||||
Note: In Python 2 onwards can use time.asctime() with no arguments.
|
||||
|
||||
"""
|
||||
|
||||
return time.asctime()
|
||||
|
||||
|
||||
def pt2str(point):
|
||||
"""Return prettier string version of point tuple.
|
||||
|
||||
>>> pt2str((1.8, 1.9))
|
||||
'(1.8, 1.9)'
|
||||
|
||||
"""
|
||||
return "(%s, %s)" % (str(point[0]), str(point[1]))
|
||||
|
||||
|
||||
def gcf(a, b, epsilon=1e-16):
|
||||
"""Return the greatest common factor of a and b, using Euclidean algorithm.
|
||||
|
||||
Arguments:
|
||||
a, b -- two numbers
|
||||
If both numbers are integers return an integer result,
|
||||
otherwise return a float result.
|
||||
epsilon -- floats less than this magnitude are considered to be zero
|
||||
(default: 1e-16)
|
||||
|
||||
Examples:
|
||||
|
||||
>>> gcf(12, 34)
|
||||
2
|
||||
|
||||
>>> gcf(13.5, 4)
|
||||
0.5
|
||||
|
||||
>>> gcf(-2, 4)
|
||||
2
|
||||
|
||||
>>> gcf(5, 0)
|
||||
5
|
||||
|
||||
By (a convenient) definition:
|
||||
>>> gcf(0, 0)
|
||||
0
|
||||
|
||||
"""
|
||||
result = max(a, b)
|
||||
remainder = min(a, b)
|
||||
while remainder and abs(remainder) > epsilon:
|
||||
new_remainder = result % remainder
|
||||
result = remainder
|
||||
remainder = new_remainder
|
||||
return abs(result)
|
||||
|
||||
def lcm(a, b, precision=None):
|
||||
"""Return the least common multiple of a and b, using the gcf function.
|
||||
|
||||
Arguments:
|
||||
a, b -- two numbers. If both are integers return an integer result,
|
||||
otherwise a return a float result.
|
||||
precision -- scaling factor if a and/or b are floats.
|
||||
|
||||
>>> lcm(21, 6)
|
||||
42
|
||||
|
||||
>>> lcm(2.5, 3.5)
|
||||
17.5
|
||||
|
||||
>>> str(lcm(1.5e-8, 2.5e-8, precision=1e9))
|
||||
'7.5e-08'
|
||||
|
||||
By (an arbitary) definition:
|
||||
>>> lcm(0, 0)
|
||||
0
|
||||
|
||||
"""
|
||||
# Note: Dummy precision argument is for backwards compatibility.
|
||||
# Do the division first.
|
||||
# (See http://en.wikipedia.org/wiki/Least_common_multiple )
|
||||
denom = gcf(a, b)
|
||||
if denom == 0:
|
||||
result = 0
|
||||
else:
|
||||
result = a * (b / denom)
|
||||
return result
|
||||
|
||||
|
||||
def permutations(input_list):
|
||||
"""Return a list containing all permutations of the input list.
|
||||
|
||||
Note: This is a recursive function.
|
||||
|
||||
>>> perms = permutations(['a', 'b', 'c'])
|
||||
>>> perms.sort()
|
||||
>>> for perm in perms:
|
||||
... print perm
|
||||
['a', 'b', 'c']
|
||||
['a', 'c', 'b']
|
||||
['b', 'a', 'c']
|
||||
['b', 'c', 'a']
|
||||
['c', 'a', 'b']
|
||||
['c', 'b', 'a']
|
||||
|
||||
"""
|
||||
out_lists = []
|
||||
if len(input_list) > 1:
|
||||
# Extract first item in list.
|
||||
item = input_list[0]
|
||||
# Find all permutations of remainder of list. (Recursive call.)
|
||||
sub_lists = permutations(input_list[1:])
|
||||
# For every permutation of the sub list...
|
||||
for sub_list in sub_lists:
|
||||
# Insert the extracted first item at every position of the list.
|
||||
for i in range(len(input_list)):
|
||||
new_list = sub_list[:]
|
||||
new_list.insert(i, item)
|
||||
out_lists.append(new_list)
|
||||
else:
|
||||
# Termination condition: only one item in input list.
|
||||
out_lists = [input_list]
|
||||
return out_lists
|
||||
|
||||
|
||||
def reduce_fraction(fraction):
|
||||
"""Reduce fraction tuple to simplest form. fraction=(num, denom)
|
||||
|
||||
>>> reduce_fraction((14, 7))
|
||||
(2, 1)
|
||||
|
||||
>>> reduce_fraction((-2, 4))
|
||||
(-1, 2)
|
||||
|
||||
>>> reduce_fraction((0, 4))
|
||||
(0, 1)
|
||||
|
||||
>>> reduce_fraction((4, 0))
|
||||
(1, 0)
|
||||
|
||||
"""
|
||||
(numerator, denominator) = fraction
|
||||
common_factor = abs(gcf(numerator, denominator))
|
||||
result = (numerator/common_factor, denominator/common_factor)
|
||||
return result
|
||||
|
||||
|
||||
def quantile(l, p):
|
||||
"""Return p quantile of list l. E.g. p=0.25 for q1.
|
||||
|
||||
See:
|
||||
http://rweb.stat.umn.edu/R/library/base/html/quantile.html
|
||||
|
||||
"""
|
||||
l_sort = l[:]
|
||||
l_sort.sort()
|
||||
n = len(l)
|
||||
r = 1 + ((n - 1) * p)
|
||||
i = int(r)
|
||||
f = r - i
|
||||
if i < n:
|
||||
result = (1-f)*l_sort[i-1] + f*l_sort[i]
|
||||
else:
|
||||
result = l_sort[i-1]
|
||||
return result
|
||||
|
||||
|
||||
def trim(l):
|
||||
"""Discard values in list more than 1.5*IQR outside IQR.
|
||||
|
||||
(IQR is inter-quartile-range)
|
||||
|
||||
This function uses rad_util.quantile
|
||||
|
||||
1.5*IQR -- mild outlier
|
||||
3*IQR -- extreme outlier
|
||||
|
||||
See:
|
||||
http://wind.cc.whecn.edu/~pwildman/statnew/section_7_-_exploratory_data_analysis.htm
|
||||
|
||||
"""
|
||||
l_sort = l[:]
|
||||
l_sort.sort()
|
||||
# Calculate medianscore (based on stats.py lmedianscore by Gary Strangman)
|
||||
if len(l_sort) % 2 == 0:
|
||||
# If even number of scores, average middle 2.
|
||||
index = int(len(l_sort) / 2) # Integer division correct
|
||||
median = float(l_sort[index] + l_sort[index-1]) / 2
|
||||
else:
|
||||
# int divsion gives mid value when count from 0
|
||||
index = int(len(l_sort) / 2)
|
||||
median = l_sort[index]
|
||||
# Calculate IQR.
|
||||
q1 = quantile(l_sort, 0.25)
|
||||
q3 = quantile(l_sort, 0.75)
|
||||
iqr = q3 - q1
|
||||
iqr_extra = iqr * 1.5
|
||||
def in_interval(x, i=iqr_extra, q1=q1, q3=q3):
|
||||
return (x >= q1-i and x <= q3+i)
|
||||
l_trimmed = [x for x in l_sort if in_interval(x)]
|
||||
return l_trimmed
|
||||
|
||||
|
||||
def nice_units(value, dp=0, sigfigs=None, suffix='', space=' ',
|
||||
use_extra_prefixes=False, use_full_name=False, mode='si'):
|
||||
"""Return value converted to human readable units eg milli, micro, etc.
|
||||
|
||||
Arguments:
|
||||
value -- number in base units
|
||||
dp -- number of decimal places to display (rounded)
|
||||
sigfigs -- number of significant figures to display (rounded)
|
||||
This overrides dp if set.
|
||||
suffix -- optional unit suffix to append to unit multiplier
|
||||
space -- seperator between value and unit multiplier (default: ' ')
|
||||
use_extra_prefixes -- use hecto, deka, deci and centi as well if set.
|
||||
(default: False)
|
||||
use_full_name -- use full name for multiplier symbol,
|
||||
e.g. milli instead of m
|
||||
(default: False)
|
||||
mode -- 'si' for SI prefixes, 'bin' for binary multipliers (1024, etc.)
|
||||
(Default: 'si')
|
||||
|
||||
SI prefixes from:
|
||||
http://physics.nist.gov/cuu/Units/prefixes.html
|
||||
(Greek mu changed to u.)
|
||||
Binary prefixes based on:
|
||||
http://physics.nist.gov/cuu/Units/binary.html
|
||||
|
||||
>>> nice_units(2e-11)
|
||||
'20 p'
|
||||
|
||||
>>> nice_units(2e-11, space='')
|
||||
'20p'
|
||||
|
||||
"""
|
||||
si_prefixes = {1e24: ('Y', 'yotta'),
|
||||
1e21: ('Z', 'zetta'),
|
||||
1e18: ('E', 'exa'),
|
||||
1e15: ('P', 'peta'),
|
||||
1e12: ('T', 'tera'),
|
||||
1e9: ('G', 'giga'),
|
||||
1e6: ('M', 'mega'),
|
||||
1e3: ('k', 'kilo'),
|
||||
1e-3: ('m', 'milli'),
|
||||
1e-6: ('u', 'micro'),
|
||||
1e-9: ('n', 'nano'),
|
||||
1e-12: ('p', 'pico'),
|
||||
1e-15: ('f', 'femto'),
|
||||
1e-18: ('a', 'atto'),
|
||||
1e-21: ('z', 'zepto'),
|
||||
1e-24: ('y', 'yocto')
|
||||
}
|
||||
if use_extra_prefixes:
|
||||
si_prefixes.update({1e2: ('h', 'hecto'),
|
||||
1e1: ('da', 'deka'),
|
||||
1e-1: ('d', 'deci'),
|
||||
1e-2: ('c', 'centi')
|
||||
})
|
||||
bin_prefixes = {2**10: ('K', 'kilo'),
|
||||
2**20: ('M', 'mega'),
|
||||
2**30: ('G', 'mega'),
|
||||
2**40: ('T', 'tera'),
|
||||
2**50: ('P', 'peta'),
|
||||
2**60: ('E', 'exa')
|
||||
}
|
||||
if mode == 'bin':
|
||||
prefixes = bin_prefixes
|
||||
else:
|
||||
prefixes = si_prefixes
|
||||
prefixes[1] = ('', '') # Unity.
|
||||
# Determine appropriate multiplier.
|
||||
multipliers = prefixes.keys()
|
||||
multipliers.sort()
|
||||
mult = None
|
||||
for i in range(len(multipliers) - 1):
|
||||
lower_mult = multipliers[i]
|
||||
upper_mult = multipliers[i+1]
|
||||
if lower_mult <= value < upper_mult:
|
||||
mult_i = i
|
||||
break
|
||||
if mult is None:
|
||||
if value < multipliers[0]:
|
||||
mult_i = 0
|
||||
elif value >= multipliers[-1]:
|
||||
mult_i = len(multipliers) - 1
|
||||
mult = multipliers[mult_i]
|
||||
# Convert value for this multiplier.
|
||||
new_value = value / mult
|
||||
# Deal with special case due to rounding.
|
||||
if sigfigs is None:
|
||||
if mult_i < (len(multipliers) - 1) and \
|
||||
round(new_value, dp) == \
|
||||
round((multipliers[mult_i+1] / mult), dp):
|
||||
mult = multipliers[mult_i + 1]
|
||||
new_value = value / mult
|
||||
# Concatenate multiplier symbol.
|
||||
if use_full_name:
|
||||
label_type = 1
|
||||
else:
|
||||
label_type = 0
|
||||
# Round and truncate to appropriate precision.
|
||||
if sigfigs is None:
|
||||
str_value = eval('"%.'+str(dp)+'f" % new_value', locals(), {})
|
||||
else:
|
||||
str_value = eval('"%.'+str(sigfigs)+'g" % new_value', locals(), {})
|
||||
return str_value + space + prefixes[mult][label_type] + suffix
|
||||
|
||||
|
||||
def uniquify(seq, preserve_order=False):
|
||||
"""Return sequence with duplicate items in sequence seq removed.
|
||||
|
||||
The code is based on usenet post by Tim Peters.
|
||||
|
||||
This code is O(N) if the sequence items are hashable, O(N**2) if not.
|
||||
|
||||
Peter Bengtsson has a blog post with an empirical comparison of other
|
||||
approaches:
|
||||
http://www.peterbe.com/plog/uniqifiers-benchmark
|
||||
|
||||
If order is not important and the sequence items are hashable then
|
||||
list(set(seq)) is readable and efficient.
|
||||
|
||||
If order is important and the sequence items are hashable generator
|
||||
expressions can be used (in py >= 2.4) (useful for large sequences):
|
||||
seen = set()
|
||||
do_something(x for x in seq if x not in seen or seen.add(x))
|
||||
|
||||
Arguments:
|
||||
seq -- sequence
|
||||
preserve_order -- if not set the order will be arbitrary
|
||||
Using this option will incur a speed penalty.
|
||||
(default: False)
|
||||
|
||||
Example showing order preservation:
|
||||
|
||||
>>> uniquify(['a', 'aa', 'b', 'b', 'ccc', 'ccc', 'd'], preserve_order=True)
|
||||
['a', 'aa', 'b', 'ccc', 'd']
|
||||
|
||||
Example using a sequence of un-hashable items:
|
||||
|
||||
>>> uniquify([['z'], ['x'], ['y'], ['z']], preserve_order=True)
|
||||
[['z'], ['x'], ['y']]
|
||||
|
||||
The sorted output or the non-order-preserving approach should equal
|
||||
that of the sorted order-preserving approach output:
|
||||
|
||||
>>> unordered = uniquify([3, 3, 1, 2], preserve_order=False)
|
||||
>>> unordered.sort()
|
||||
>>> ordered = uniquify([3, 3, 1, 2], preserve_order=True)
|
||||
>>> ordered.sort()
|
||||
>>> ordered
|
||||
[1, 2, 3]
|
||||
>>> int(ordered == unordered)
|
||||
1
|
||||
|
||||
"""
|
||||
try:
|
||||
# Attempt fast algorithm.
|
||||
d = {}
|
||||
if preserve_order:
|
||||
# This is based on Dave Kirby's method (f8) noted in the post:
|
||||
# http://www.peterbe.com/plog/uniqifiers-benchmark
|
||||
return [x for x in seq if (x not in d) and not d.__setitem__(x, 0)]
|
||||
else:
|
||||
for x in seq:
|
||||
d[x] = 0
|
||||
return d.keys()
|
||||
except TypeError:
|
||||
# Have an unhashable object, so use slow algorithm.
|
||||
result = []
|
||||
app = result.append
|
||||
for x in seq:
|
||||
if x not in result:
|
||||
app(x)
|
||||
return result
|
||||
|
||||
# Alias to noun form for backward compatibility.
|
||||
unique = uniquify
|
||||
|
||||
|
||||
def reverse_dict(d):
|
||||
"""Reverse a dictionary so the items become the keys and vice-versa.
|
||||
|
||||
Note: The results will be arbitrary if the items are not unique.
|
||||
|
||||
>>> d = reverse_dict({'a': 1, 'b': 2})
|
||||
>>> d_items = d.items()
|
||||
>>> d_items.sort()
|
||||
>>> d_items
|
||||
[(1, 'a'), (2, 'b')]
|
||||
|
||||
"""
|
||||
result = {}
|
||||
for key, value in d.items():
|
||||
result[value] = key
|
||||
return result
|
||||
|
||||
|
||||
def lsb(x, n):
|
||||
"""Return the n least significant bits of x.
|
||||
|
||||
>>> lsb(13, 3)
|
||||
5
|
||||
|
||||
"""
|
||||
return x & ((2 ** n) - 1)
|
||||
|
||||
|
||||
def gray_encode(i):
|
||||
"""Gray encode the given integer."""
|
||||
|
||||
return i ^ (i >> 1)
|
||||
|
||||
|
||||
def random_vec(bits, max_value=None):
|
||||
"""Generate a random binary vector of length bits and given max value."""
|
||||
|
||||
vector = ""
|
||||
for _ in range(int(bits / 10) + 1):
|
||||
i = int((2**10) * random.random())
|
||||
vector += int2bin(i, 10)
|
||||
|
||||
if max_value and (max_value < 2 ** bits - 1):
|
||||
vector = int2bin((int(vector, 2) / (2 ** bits - 1)) * max_value, bits)
|
||||
|
||||
return vector[0:bits]
|
||||
|
||||
|
||||
def binary_range(bits):
|
||||
"""Return a list of all possible binary numbers in order with width=bits.
|
||||
|
||||
It would be nice to extend it to match the
|
||||
functionality of python's range() built-in function.
|
||||
|
||||
"""
|
||||
l = []
|
||||
v = ['0'] * bits
|
||||
|
||||
toggle = [1] + [0] * bits
|
||||
|
||||
while toggle[bits] != 1:
|
||||
v_copy = v[:]
|
||||
v_copy.reverse()
|
||||
l.append(''.join(v_copy))
|
||||
|
||||
toggle = [1] + [0]*bits
|
||||
i = 0
|
||||
while i < bits and toggle[i] == 1:
|
||||
if toggle[i]:
|
||||
if v[i] == '0':
|
||||
v[i] = '1'
|
||||
toggle[i+1] = 0
|
||||
else:
|
||||
v[i] = '0'
|
||||
toggle[i+1] = 1
|
||||
i += 1
|
||||
return l
|
||||
|
||||
|
||||
def float_range(start, stop=None, step=None):
|
||||
"""Return a list containing an arithmetic progression of floats.
|
||||
|
||||
Return a list of floats between 0.0 (or start) and stop with an
|
||||
increment of step.
|
||||
|
||||
This is in functionality to python's range() built-in function
|
||||
but can accept float increments.
|
||||
|
||||
As with range(), stop is omitted from the list.
|
||||
|
||||
"""
|
||||
if stop is None:
|
||||
stop = float(start)
|
||||
start = 0.0
|
||||
|
||||
if step is None:
|
||||
step = 1.0
|
||||
|
||||
cur = float(start)
|
||||
l = []
|
||||
while cur < stop:
|
||||
l.append(cur)
|
||||
cur += step
|
||||
|
||||
return l
|
||||
|
||||
|
||||
def find_common_fixes(s1, s2):
|
||||
"""Find common (prefix, suffix) of two strings.
|
||||
|
||||
>>> find_common_fixes('abc', 'def')
|
||||
('', '')
|
||||
|
||||
>>> find_common_fixes('abcelephantdef', 'abccowdef')
|
||||
('abc', 'def')
|
||||
|
||||
>>> find_common_fixes('abcelephantdef', 'abccow')
|
||||
('abc', '')
|
||||
|
||||
>>> find_common_fixes('elephantdef', 'abccowdef')
|
||||
('', 'def')
|
||||
|
||||
"""
|
||||
prefix = []
|
||||
suffix = []
|
||||
|
||||
i = 0
|
||||
common_len = min(len(s1), len(s2))
|
||||
while i < common_len:
|
||||
if s1[i] != s2[i]:
|
||||
break
|
||||
|
||||
prefix.append(s1[i])
|
||||
i += 1
|
||||
|
||||
i = 1
|
||||
while i < (common_len + 1):
|
||||
if s1[-i] != s2[-i]:
|
||||
break
|
||||
|
||||
suffix.append(s1[-i])
|
||||
i += 1
|
||||
|
||||
suffix.reverse()
|
||||
|
||||
prefix = ''.join(prefix)
|
||||
suffix = ''.join(suffix)
|
||||
|
||||
return (prefix, suffix)
|
||||
|
||||
|
||||
def is_rotated(seq1, seq2):
|
||||
"""Return true if the first sequence is a rotation of the second sequence.
|
||||
|
||||
>>> seq1 = ['A', 'B', 'C', 'D']
|
||||
>>> seq2 = ['C', 'D', 'A', 'B']
|
||||
>>> int(is_rotated(seq1, seq2))
|
||||
1
|
||||
|
||||
>>> seq2 = ['C', 'D', 'B', 'A']
|
||||
>>> int(is_rotated(seq1, seq2))
|
||||
0
|
||||
|
||||
>>> seq1 = ['A', 'B', 'C', 'A']
|
||||
>>> seq2 = ['A', 'A', 'B', 'C']
|
||||
>>> int(is_rotated(seq1, seq2))
|
||||
1
|
||||
|
||||
>>> seq2 = ['A', 'B', 'C', 'A']
|
||||
>>> int(is_rotated(seq1, seq2))
|
||||
1
|
||||
|
||||
>>> seq2 = ['A', 'A', 'C', 'B']
|
||||
>>> int(is_rotated(seq1, seq2))
|
||||
0
|
||||
|
||||
"""
|
||||
# Do a sanity check.
|
||||
if len(seq1) != len(seq2):
|
||||
return False
|
||||
# Look for occurrences of second sequence head item in first sequence.
|
||||
start_indexes = []
|
||||
head_item = seq2[0]
|
||||
for index1 in range(len(seq1)):
|
||||
if seq1[index1] == head_item:
|
||||
start_indexes.append(index1)
|
||||
# Check that wrapped sequence matches.
|
||||
double_seq1 = seq1 + seq1
|
||||
for index1 in start_indexes:
|
||||
if double_seq1[index1:index1+len(seq1)] == seq2:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getmodule(obj):
|
||||
"""Return the module that contains the object definition of obj.
|
||||
|
||||
Note: Use inspect.getmodule instead.
|
||||
|
||||
Arguments:
|
||||
obj -- python obj, generally a class or a function
|
||||
|
||||
Examples:
|
||||
|
||||
A function:
|
||||
>>> module = getmodule(random.choice)
|
||||
>>> module.__name__
|
||||
'random'
|
||||
>>> module is random
|
||||
1
|
||||
|
||||
A class:
|
||||
>>> module = getmodule(random.Random)
|
||||
>>> module.__name__
|
||||
'random'
|
||||
>>> module is random
|
||||
1
|
||||
|
||||
A class inheriting from a class in another module:
|
||||
(note: The inheriting class must define at least one function.)
|
||||
>>> class MyRandom(random.Random):
|
||||
... def play(self):
|
||||
... pass
|
||||
>>> module = getmodule(MyRandom)
|
||||
>>> if __name__ == '__main__':
|
||||
... name = 'rad_util'
|
||||
... else:
|
||||
... name = module.__name__
|
||||
>>> name
|
||||
'rad_util'
|
||||
>>> module is sys.modules[__name__]
|
||||
1
|
||||
|
||||
Discussion:
|
||||
This approach is slightly hackish, and won't work in various situations.
|
||||
However, this was the approach recommended by GvR, so it's as good as
|
||||
you'll get.
|
||||
|
||||
See GvR's post in this thread:
|
||||
http://groups.google.com.au/group/comp.lang.python/browse_thread/thread/966a7bdee07e3b34/c3cab3f41ea84236?lnk=st&q=python+determine+class+module&rnum=4&hl=en#c3cab3f41ea84236
|
||||
|
||||
"""
|
||||
if hasattr(obj, 'func_globals'):
|
||||
func = obj
|
||||
else:
|
||||
# Handle classes.
|
||||
func = None
|
||||
for item in obj.__dict__.values():
|
||||
if hasattr(item, 'func_globals'):
|
||||
func = item
|
||||
break
|
||||
if func is None:
|
||||
raise ValueError("No functions attached to object: %r" % obj)
|
||||
module_name = func.func_globals['__name__']
|
||||
# Get module.
|
||||
module = sys.modules[module_name]
|
||||
return module
|
||||
|
||||
|
||||
def round_grid(value, grid, mode=0):
|
||||
"""Round off the given value to the given grid size.
|
||||
|
||||
Arguments:
|
||||
value -- value to be roudne
|
||||
grid -- result must be a multiple of this
|
||||
mode -- 0 nearest, 1 up, -1 down
|
||||
|
||||
Examples:
|
||||
|
||||
>>> round_grid(7.5, 5)
|
||||
10
|
||||
|
||||
>>> round_grid(7.5, 5, mode=-1)
|
||||
5
|
||||
|
||||
>>> round_grid(7.3, 5, mode=1)
|
||||
10
|
||||
|
||||
>>> round_grid(7.3, 5.0, mode=1)
|
||||
10.0
|
||||
|
||||
"""
|
||||
off_grid = value % grid
|
||||
if mode == 0:
|
||||
add_one = int(off_grid >= (grid / 2.0))
|
||||
elif mode == 1 and off_grid:
|
||||
add_one = 1
|
||||
elif mode == -1 and off_grid:
|
||||
add_one = 0
|
||||
result = ((int(value / grid) + add_one) * grid)
|
||||
return result
|
||||
|
||||
|
||||
def get_args(argv):
|
||||
"""Store command-line args in a dictionary.
|
||||
|
||||
-, -- prefixes are removed
|
||||
Items not prefixed with - or -- are stored as a list, indexed by 'args'
|
||||
|
||||
For options that take a value use --option=value
|
||||
|
||||
Consider using optparse or getopt (in Python standard library) instead.
|
||||
|
||||
"""
|
||||
d = {}
|
||||
args = []
|
||||
|
||||
for arg in argv:
|
||||
|
||||
if arg.startswith('-'):
|
||||
parts = re.sub(r'^-+', '', arg).split('=')
|
||||
if len(parts) == 2:
|
||||
d[parts[0]] = parts[1]
|
||||
else:
|
||||
d[parts[0]] = None
|
||||
else:
|
||||
args.append(arg)
|
||||
|
||||
d['args'] = args
|
||||
|
||||
return d
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import doctest
|
||||
doctest.testmod(sys.modules['__main__'])
|
||||
|
||||
@@ -1,392 +0,0 @@
|
||||
# topsort - dependency (topological) sorting and cycle finding functions
|
||||
# Copyright (C) 2007 RADLogic
|
||||
#
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation;
|
||||
# version 2.1 of the License.
|
||||
#
|
||||
# This library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# See http://www.fsf.org/licensing/licenses/lgpl.txt for full license text.
|
||||
"""Provide toplogical sorting (i.e. dependency sorting) functions.
|
||||
|
||||
The topsort function is based on code posted on Usenet by Tim Peters.
|
||||
|
||||
Modifications:
|
||||
- added doctests
|
||||
- changed some bits to use current Python idioms
|
||||
(listcomp instead of filter, +=/-=, inherit from Exception)
|
||||
- added a topsort_levels version that ports items in each dependency level
|
||||
into a sub-list
|
||||
- added find_cycles to aid in cycle debugging
|
||||
|
||||
Run this module directly to run the doctests (unittests).
|
||||
Make sure they all pass before checking in any modifications.
|
||||
|
||||
Requires Python >= 2.2
|
||||
(For Python 2.2 also requires separate sets.py module)
|
||||
|
||||
This requires the rad_util.py module.
|
||||
|
||||
"""
|
||||
|
||||
# Provide support for Python 2.2*
|
||||
from __future__ import generators
|
||||
|
||||
__version__ = '$Revision: 0.9 $'
|
||||
__date__ = '$Date: 2007/03/27 04:15:26 $'
|
||||
__credits__ = '''Tim Peters -- original topsort code
|
||||
Tim Wegener -- doctesting, updating to current idioms, topsort_levels,
|
||||
find_cycles
|
||||
'''
|
||||
|
||||
# Make Python 2.3 sets look like Python 2.4 sets.
|
||||
try:
|
||||
set
|
||||
except NameError:
|
||||
from sets import Set as set
|
||||
|
||||
from rad_util import is_rotated
|
||||
|
||||
|
||||
class CycleError(Exception):
|
||||
"""Cycle Error"""
|
||||
pass
|
||||
|
||||
|
||||
def topsort(pairlist):
|
||||
"""Topologically sort a list of (parent, child) pairs.
|
||||
|
||||
Return a list of the elements in dependency order (parent to child order).
|
||||
|
||||
>>> print topsort( [(1,2), (3,4), (5,6), (1,3), (1,5), (1,6), (2,5)] )
|
||||
[1, 2, 3, 5, 4, 6]
|
||||
|
||||
>>> print topsort( [(1,2), (1,3), (2,4), (3,4), (5,6), (4,5)] )
|
||||
[1, 2, 3, 4, 5, 6]
|
||||
|
||||
>>> print topsort( [(1,2), (2,3), (3,2)] )
|
||||
Traceback (most recent call last):
|
||||
CycleError: ([1], {2: 1, 3: 1}, {2: [3], 3: [2]})
|
||||
|
||||
"""
|
||||
num_parents = {} # element -> # of predecessors
|
||||
children = {} # element -> list of successors
|
||||
for parent, child in pairlist:
|
||||
# Make sure every element is a key in num_parents.
|
||||
if not num_parents.has_key( parent ):
|
||||
num_parents[parent] = 0
|
||||
if not num_parents.has_key( child ):
|
||||
num_parents[child] = 0
|
||||
|
||||
# Since child has a parent, increment child's num_parents count.
|
||||
num_parents[child] += 1
|
||||
|
||||
# ... and parent gains a child.
|
||||
children.setdefault(parent, []).append(child)
|
||||
|
||||
# Suck up everything without a parent.
|
||||
answer = [x for x in num_parents.keys() if num_parents[x] == 0]
|
||||
|
||||
# For everything in answer, knock down the parent count on its children.
|
||||
# Note that answer grows *in* the loop.
|
||||
for parent in answer:
|
||||
del num_parents[parent]
|
||||
if children.has_key( parent ):
|
||||
for child in children[parent]:
|
||||
num_parents[child] -= 1
|
||||
if num_parents[child] == 0:
|
||||
answer.append( child )
|
||||
# Following "del" isn't needed; just makes
|
||||
# CycleError details easier to grasp.
|
||||
del children[parent]
|
||||
|
||||
if num_parents:
|
||||
# Everything in num_parents has at least one child ->
|
||||
# there's a cycle.
|
||||
raise CycleError(answer, num_parents, children)
|
||||
return answer
|
||||
|
||||
def topsort_levels(pairlist):
|
||||
"""Topologically sort a list of (parent, child) pairs into depth levels.
|
||||
|
||||
This returns a generator.
|
||||
Turn this into a an iterator using the iter built-in function.
|
||||
(if you iterate over the iterator, each element gets generated when
|
||||
it is asked for, rather than generating the whole list up-front.)
|
||||
|
||||
Each generated element is a list of items at that dependency level.
|
||||
|
||||
>>> dependency_pairs = [(1,2), (3,4), (5,6), (1,3), (1,5), (1,6), (2,5)]
|
||||
>>> for level in iter(topsort_levels( dependency_pairs )):
|
||||
... print level
|
||||
[1]
|
||||
[2, 3]
|
||||
[4, 5]
|
||||
[6]
|
||||
|
||||
>>> dependency_pairs = [(1,2), (1,3), (2,4), (3,4), (5,6), (4,5)]
|
||||
>>> for level in iter(topsort_levels( dependency_pairs )):
|
||||
... print level
|
||||
[1]
|
||||
[2, 3]
|
||||
[4]
|
||||
[5]
|
||||
[6]
|
||||
|
||||
>>> dependency_pairs = [(1,2), (2,3), (3,4), (4, 3)]
|
||||
>>> try:
|
||||
... for level in iter(topsort_levels( dependency_pairs )):
|
||||
... print level
|
||||
... except CycleError, exc:
|
||||
... print 'CycleError:', exc
|
||||
[1]
|
||||
[2]
|
||||
CycleError: ({3: 1, 4: 1}, {3: [4], 4: [3]})
|
||||
|
||||
|
||||
The cycle error should look like.
|
||||
CycleError: ({3: 1, 4: 1}, {3: [4], 4: [3]})
|
||||
# todo: Make the doctest more robust (i.e. handle arbitrary dict order).
|
||||
|
||||
"""
|
||||
num_parents = {} # element -> # of predecessors
|
||||
children = {} # element -> list of successors
|
||||
for parent, child in pairlist:
|
||||
# Make sure every element is a key in num_parents.
|
||||
if not num_parents.has_key( parent ):
|
||||
num_parents[parent] = 0
|
||||
if not num_parents.has_key( child ):
|
||||
num_parents[child] = 0
|
||||
|
||||
# Since child has a parent, increment child's num_parents count.
|
||||
num_parents[child] += 1
|
||||
|
||||
# ... and parent gains a child.
|
||||
children.setdefault(parent, []).append(child)
|
||||
|
||||
return topsort_levels_core(num_parents, children)
|
||||
|
||||
def topsort_levels_core(num_parents, children):
|
||||
"""Topologically sort a bunch of interdependent items based on dependency.
|
||||
|
||||
This returns a generator.
|
||||
Turn this into a an iterator using the iter built-in function.
|
||||
(if you iterate over the iterator, each element gets generated when
|
||||
it is asked for, rather than generating the whole list up-front.)
|
||||
|
||||
Each generated element is a list of items at that dependency level.
|
||||
|
||||
>>> list(topsort_levels_core(
|
||||
... {1: 0, 2: 1, 3: 1, 4: 1, 5: 2, 6: 2},
|
||||
... {1: [2, 3, 5, 6], 2: [5], 3: [4], 4: [], 5: [6]}))
|
||||
[[1], [2, 3], [4, 5], [6]]
|
||||
|
||||
>>> list(topsort_levels_core(
|
||||
... {1: 0, 2: 2, 3: 1},
|
||||
... {1: [2], 2: [3], 3: [2]}))
|
||||
Traceback (most recent call last):
|
||||
CycleError: ({2: 1, 3: 1}, {2: [3], 3: [2]})
|
||||
|
||||
This function has a more complicated interface than topsort_levels,
|
||||
but is useful if the data is easier to generate in this form.
|
||||
|
||||
Arguments:
|
||||
num_parents -- key: item, value: number of parents (predecessors)
|
||||
children -- key: item, value: list of children (successors)
|
||||
|
||||
"""
|
||||
while 1:
|
||||
# Suck up everything without a predecessor.
|
||||
level_parents = [x for x in num_parents.keys() if num_parents[x] == 0]
|
||||
|
||||
if not level_parents:
|
||||
break
|
||||
|
||||
# Offer the next generated item,
|
||||
# which is a list of the items at this dependency level.
|
||||
yield level_parents
|
||||
|
||||
# For everything item in this level,
|
||||
# decrement the parent count,
|
||||
# since we have accounted for its parent.
|
||||
for level_parent in level_parents:
|
||||
|
||||
del num_parents[level_parent]
|
||||
|
||||
if children.has_key(level_parent):
|
||||
for level_parent_child in children[level_parent]:
|
||||
num_parents[level_parent_child] -= 1
|
||||
del children[level_parent]
|
||||
|
||||
if num_parents:
|
||||
# Everything in num_parents has at least one child ->
|
||||
# there's a cycle.
|
||||
raise CycleError(num_parents, children)
|
||||
else:
|
||||
# This is the end of the generator.
|
||||
raise StopIteration
|
||||
|
||||
|
||||
def find_cycles(parent_children):
|
||||
"""Yield cycles. Each result is a list of items comprising a cycle.
|
||||
|
||||
Use a 'stack' based approach to find all the cycles.
|
||||
This is a generator, so yields each cycle as it finds it.
|
||||
|
||||
It is implicit that the last item in each cycle list is a parent of the
|
||||
first item (thereby forming a cycle).
|
||||
|
||||
Arguments:
|
||||
parent_children -- parent -> collection of children
|
||||
|
||||
Simplest cycle:
|
||||
>>> cycles = list(find_cycles({'A': ['B'], 'B': ['A']}))
|
||||
>>> len(cycles)
|
||||
1
|
||||
>>> cycle = cycles[0]
|
||||
>>> cycle.sort()
|
||||
>>> print cycle
|
||||
['A', 'B']
|
||||
|
||||
Simplest cycle with extra baggage at the start and the end:
|
||||
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
|
||||
... 'B': ['C'],
|
||||
... 'C': ['B', 'D'],
|
||||
... 'D': [],
|
||||
... }))
|
||||
>>> len(cycles)
|
||||
1
|
||||
>>> cycle = cycles[0]
|
||||
>>> cycle.sort()
|
||||
>>> print cycle
|
||||
['B', 'C']
|
||||
|
||||
Double cycle:
|
||||
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
|
||||
... 'B': ['C1', 'C2'],
|
||||
... 'C1': ['D1'],
|
||||
... 'D1': ['E1'],
|
||||
... 'E1': ['D1'],
|
||||
... 'C2': ['D2'],
|
||||
... 'D2': ['E2'],
|
||||
... 'E2': ['D2'],
|
||||
... }))
|
||||
>>> len(cycles)
|
||||
2
|
||||
>>> for cycle in cycles:
|
||||
... cycle.sort()
|
||||
>>> cycles.sort()
|
||||
>>> cycle1 = cycles[0]
|
||||
>>> cycle1.sort()
|
||||
>>> print cycle1
|
||||
['D1', 'E1']
|
||||
>>> cycle2 = cycles[1]
|
||||
>>> cycle2.sort()
|
||||
>>> print cycle2
|
||||
['D2', 'E2']
|
||||
|
||||
Simple cycle with children not specified for one item:
|
||||
# todo: Should this barf instead?
|
||||
>>> cycles = list(find_cycles(parent_children={'A': ['B'],
|
||||
... 'B': ['A'],
|
||||
... 'C': ['D']}))
|
||||
>>> len(cycles)
|
||||
1
|
||||
>>> cycle = cycles[0]
|
||||
>>> cycle.sort()
|
||||
>>> print cycle
|
||||
['A', 'B']
|
||||
|
||||
Diamond cycle
|
||||
>>> cycles = list(find_cycles(parent_children={'A': ['B1', 'B2'],
|
||||
... 'B1': ['C'],
|
||||
... 'B2': ['C'],
|
||||
... 'C': ['A', 'B1']}))
|
||||
>>> len(cycles)
|
||||
3
|
||||
>>> sorted_cycles = []
|
||||
>>> for cycle in cycles:
|
||||
... cycle = list(cycle)
|
||||
... cycle.sort()
|
||||
... sorted_cycles.append(cycle)
|
||||
>>> sorted_cycles.sort()
|
||||
>>> for cycle in sorted_cycles:
|
||||
... print cycle
|
||||
['A', 'B1', 'C']
|
||||
['A', 'B2', 'C']
|
||||
['B1', 'C']
|
||||
|
||||
Hairy case (order can matter if something is wrong):
|
||||
(Note order of B and C in the list.)
|
||||
>>> cycles = list(find_cycles(parent_children={
|
||||
... 'TD': ['DD'],
|
||||
... 'TC': ['DC'],
|
||||
... 'DC': ['DQ'],
|
||||
... 'C': ['DQ'],
|
||||
... 'DQ': ['IA', 'TO'],
|
||||
... 'IA': ['A'],
|
||||
... 'A': ['B', 'C'],
|
||||
... }))
|
||||
>>> len(cycles)
|
||||
1
|
||||
>>> cycle = cycles[0]
|
||||
>>> cycle.sort()
|
||||
>>> print cycle
|
||||
['A', 'C', 'DQ', 'IA']
|
||||
|
||||
"""
|
||||
cycles = []
|
||||
visited_nodes = set()
|
||||
|
||||
for parent in parent_children:
|
||||
if parent in visited_nodes:
|
||||
# This node is part of a path that has already been traversed.
|
||||
continue
|
||||
|
||||
paths = [[parent]]
|
||||
while paths:
|
||||
path = paths.pop()
|
||||
|
||||
parent = path[-1]
|
||||
|
||||
try:
|
||||
children = parent_children[parent]
|
||||
except KeyError:
|
||||
continue
|
||||
|
||||
for child in children:
|
||||
# Keeping a set of the path nodes, for O(1) lookups at the
|
||||
# expense of more memory and complexity, actually makes speed
|
||||
# worse. (Due to construction of sets.)
|
||||
# This is O(N).
|
||||
if child in path:
|
||||
# This is a cycle.
|
||||
cycle = path[path.index(child):]
|
||||
# Check that this is not a dup cycle.
|
||||
is_dup = False
|
||||
for other_cycle in cycles:
|
||||
if is_rotated(other_cycle, cycle):
|
||||
is_dup = True
|
||||
break
|
||||
if not is_dup:
|
||||
cycles.append(cycle)
|
||||
yield cycle
|
||||
else:
|
||||
# Push this new path onto the 'stack'.
|
||||
# This is probably the most expensive part of the algorithm
|
||||
# (a list copy).
|
||||
paths.append(path + [child])
|
||||
# Mark the node as visited.
|
||||
visited_nodes.add(child)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# Run the doctest tests.
|
||||
import sys
|
||||
import doctest
|
||||
doctest.testmod(sys.modules['__main__'])
|
||||
@@ -1,502 +0,0 @@
|
||||
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
import types
|
||||
import re
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
from waflib import Task, Options, Configure, TaskGen, Logs, Build, Utils, Errors
|
||||
from waflib.Errors import WafError
|
||||
|
||||
# feature = TaskGen.feature
|
||||
# after = TaskGen.after
|
||||
|
||||
# https://github.com/named-data-ndnSIM/pybindgen
|
||||
REQUIRED_PYBINDGEN_VERSION = '0.17.0.post45+ng4806e4f'
|
||||
REQUIRED_PYGCCXML_VERSION = (0, 9, 5)
|
||||
|
||||
RUN_ME=-3
|
||||
|
||||
def add_to_python_path(path):
|
||||
if os.environ.get('PYTHONPATH', ''):
|
||||
os.environ['PYTHONPATH'] = path + os.pathsep + os.environ.get('PYTHONPATH')
|
||||
else:
|
||||
os.environ['PYTHONPATH'] = path
|
||||
|
||||
def set_pybindgen_pythonpath(env):
|
||||
if env['WITH_PYBINDGEN']:
|
||||
add_to_python_path(env['WITH_PYBINDGEN'])
|
||||
|
||||
|
||||
def options(opt):
|
||||
opt.load('python')
|
||||
opt.add_option('--disable-python',
|
||||
help=("Don't build Python bindings."),
|
||||
action="store_true", default=False,
|
||||
dest='python_disable')
|
||||
opt.add_option('--apiscan',
|
||||
help=("Rescan the API for the indicated module(s), for Python bindings. "
|
||||
"Needs working GCCXML / pygccxml environment. "
|
||||
"The metamodule 'all' expands to all available ns-3 modules."),
|
||||
default=None, dest='apiscan', metavar="MODULE[,MODULE...]")
|
||||
opt.add_option('--with-pybindgen',
|
||||
help=('Path to an existing pybindgen source tree to use.'),
|
||||
default=None,
|
||||
dest='with_pybindgen', type="string")
|
||||
opt.add_option('--with-python',
|
||||
help=('Path to the Python interpreter to use.'),
|
||||
default=None, dest='with_python', type="string")
|
||||
opt.add_option('--no32bit-scan',
|
||||
help=("Don't scan for the 32-bit variant of the bindings on 64-bit platforms."),
|
||||
action="store_true", default=False,
|
||||
dest='no32bit_scan')
|
||||
|
||||
|
||||
def configure(conf):
|
||||
conf.env['ENABLE_PYTHON_BINDINGS'] = False
|
||||
if Options.options.python_disable:
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"disabled by user request")
|
||||
return
|
||||
# Disable python in static builds (bug #1253)
|
||||
if ((conf.env['ENABLE_STATIC_NS3']) or \
|
||||
(conf.env['ENABLE_SHARED_AND_STATIC_NS3'])):
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"bindings incompatible with static build")
|
||||
return
|
||||
|
||||
enabled_modules = list(conf.env['NS3_ENABLED_MODULES'])
|
||||
enabled_modules.sort()
|
||||
available_modules = list(conf.env['NS3_MODULES'])
|
||||
available_modules.sort()
|
||||
all_modules_enabled = (enabled_modules == available_modules)
|
||||
|
||||
conf.load('misc', tooldir=['waf-tools'])
|
||||
|
||||
if sys.platform == 'cygwin':
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"unsupported platform 'cygwin'")
|
||||
Logs.warn("Python is not supported in CygWin environment. Try MingW instead.")
|
||||
return
|
||||
|
||||
|
||||
## Check for Python
|
||||
|
||||
if Options.options.with_python is not None:
|
||||
conf.env.PYTHON = Options.options.with_python
|
||||
|
||||
try:
|
||||
conf.load('python')
|
||||
except Errors.ConfigurationError, ex:
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"The python interpreter was not found")
|
||||
return
|
||||
try:
|
||||
conf.check_python_version((2,3))
|
||||
except Errors.ConfigurationError, ex:
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"The python found version is too low (2.3 required)")
|
||||
return
|
||||
try:
|
||||
conf.check_python_headers()
|
||||
except Errors.ConfigurationError, ex:
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"Python library or headers missing")
|
||||
return
|
||||
|
||||
# stupid Mac OSX Python wants to build extensions as "universal
|
||||
# binaries", i386, x86_64, and ppc, but this way the type
|
||||
# __uint128_t is not available. We need to disable the multiarch
|
||||
# crap by removing the -arch parameters.
|
||||
for flags_var in ["CFLAGS_PYEXT", "CFLAGS_PYEMBED", "CXXFLAGS_PYEMBED",
|
||||
"CXXFLAGS_PYEXT", "LINKFLAGS_PYEMBED", "LINKFLAGS_PYEXT"]:
|
||||
flags = conf.env[flags_var]
|
||||
i = 0
|
||||
while i < len(flags):
|
||||
if flags[i] == '-arch':
|
||||
del flags[i]
|
||||
del flags[i]
|
||||
continue
|
||||
i += 1
|
||||
conf.env[flags_var] = flags
|
||||
|
||||
# -fvisibility=hidden optimization
|
||||
if (conf.env['CXX_NAME'] == 'gcc' and [int(x) for x in conf.env['CC_VERSION']] >= [4,0,0]
|
||||
and conf.check_compilation_flag('-fvisibility=hidden')):
|
||||
conf.env.append_value('CXXFLAGS_PYEXT', '-fvisibility=hidden')
|
||||
conf.env.append_value('CCFLAGS_PYEXT', '-fvisibility=hidden')
|
||||
|
||||
if conf.check_compilation_flag('-Wno-array-bounds'):
|
||||
conf.env.append_value('CXXFLAGS_PYEXT', '-Wno-array-bounds')
|
||||
|
||||
# Check for the location of pybindgen
|
||||
if Options.options.with_pybindgen is not None:
|
||||
if os.path.isdir(Options.options.with_pybindgen):
|
||||
conf.msg("Checking for pybindgen location", ("%s (given)" % Options.options.with_pybindgen))
|
||||
conf.env['WITH_PYBINDGEN'] = os.path.abspath(Options.options.with_pybindgen)
|
||||
else:
|
||||
# ns-3-dev uses ../pybindgen, while ns-3 releases use ../REQUIRED_PYBINDGEN_VERSION
|
||||
pybindgen_dir = os.path.join('..', "pybindgen")
|
||||
pybindgen_release_str = "pybindgen-" + '.'.join([str(x) for x in REQUIRED_PYBINDGEN_VERSION])
|
||||
pybindgen_release_dir = os.path.join('..', pybindgen_release_str)
|
||||
if os.path.isdir(pybindgen_dir):
|
||||
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_dir))
|
||||
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_dir)
|
||||
elif os.path.isdir(pybindgen_release_dir):
|
||||
conf.msg("Checking for pybindgen location", ("%s (guessed)" % pybindgen_release_dir))
|
||||
conf.env['WITH_PYBINDGEN'] = os.path.abspath(pybindgen_release_dir)
|
||||
del pybindgen_dir
|
||||
del pybindgen_release_dir
|
||||
if not conf.env['WITH_PYBINDGEN']:
|
||||
conf.msg("Checking for pybindgen location", False)
|
||||
|
||||
# Check for pybindgen
|
||||
|
||||
set_pybindgen_pythonpath(conf.env)
|
||||
|
||||
try:
|
||||
conf.check_python_module('pybindgen')
|
||||
except Errors.ConfigurationError:
|
||||
Logs.warn("pybindgen missing => no python bindings")
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"PyBindGen missing")
|
||||
return
|
||||
else:
|
||||
def getVersion():
|
||||
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
|
||||
"try:\n"
|
||||
" import pybindgen.version\n"
|
||||
" print(pybindgen.__version__)\n"
|
||||
"except:\n"
|
||||
" pass"],
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
return out.strip()
|
||||
pybindgen_version = getVersion()
|
||||
if pybindgen_version == '': # version is not generated yet (e.g., cloned from git repo directly)
|
||||
subprocess.Popen([conf.env['PYTHON'][0], "setup.py", "--version"],
|
||||
cwd=conf.env['WITH_PYBINDGEN'],
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
pybindgen_version = getVersion()
|
||||
|
||||
conf.msg('Checking for pybindgen version', pybindgen_version)
|
||||
if not (pybindgen_version == REQUIRED_PYBINDGEN_VERSION):
|
||||
Logs.warn("pybindgen (found %r), (need %r)" %
|
||||
(pybindgen_version, REQUIRED_PYBINDGEN_VERSION))
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"PyBindGen version not correct and newer version could not be retrieved")
|
||||
return
|
||||
|
||||
|
||||
def test(t1, t2):
|
||||
test_program = '''
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
int main ()
|
||||
{
|
||||
std::vector< %(type1)s > t = std::vector< %(type2)s > ();
|
||||
return 0;
|
||||
}
|
||||
''' % dict(type1=t1, type2=t2)
|
||||
|
||||
try:
|
||||
ret = conf.check(compiler='cxx', fragment=test_program, features='cxx')
|
||||
except Errors.ConfigurationError:
|
||||
ret = False
|
||||
conf.msg('Checking for types %s and %s equivalence' % (t1, t2), (ret and 'no' or 'yes'))
|
||||
return ret
|
||||
|
||||
uint64_is_long = test("uint64_t", "unsigned long")
|
||||
uint64_is_long_long = test("uint64_t", "unsigned long long")
|
||||
|
||||
if uint64_is_long:
|
||||
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-LP64'
|
||||
elif uint64_is_long_long:
|
||||
conf.env['PYTHON_BINDINGS_APIDEFS'] = 'gcc-ILP32'
|
||||
else:
|
||||
conf.env['PYTHON_BINDINGS_APIDEFS'] = None
|
||||
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
|
||||
msg = 'none available'
|
||||
else:
|
||||
msg = conf.env['PYTHON_BINDINGS_APIDEFS']
|
||||
|
||||
conf.msg('Checking for the apidefs that can be used for Python bindings', msg)
|
||||
|
||||
if conf.env['PYTHON_BINDINGS_APIDEFS'] is None:
|
||||
conf.report_optional_feature("python", "Python Bindings", False,
|
||||
"No apidefs are available that can be used in this system")
|
||||
return
|
||||
|
||||
|
||||
## If all has gone well, we finally enable the Python bindings
|
||||
conf.env['ENABLE_PYTHON_BINDINGS'] = True
|
||||
conf.report_optional_feature("python", "Python Bindings", True, None)
|
||||
|
||||
|
||||
# check cxxabi stuff (which Mac OS X Lion breaks)
|
||||
fragment = r"""
|
||||
# include <cxxabi.h>
|
||||
int main ()
|
||||
{
|
||||
const abi::__si_class_type_info *_typeinfo __attribute__((unused)) = NULL;
|
||||
return 0;
|
||||
}
|
||||
"""
|
||||
gcc_rtti_abi = conf.check_nonfatal(fragment=fragment, msg="Checking for internal GCC cxxabi",
|
||||
okmsg="complete", errmsg='incomplete',
|
||||
mandatory=False)
|
||||
conf.env["GCC_RTTI_ABI_COMPLETE"] = str(bool(gcc_rtti_abi))
|
||||
|
||||
|
||||
|
||||
## Check for pygccxml
|
||||
try:
|
||||
conf.check_python_module('pygccxml')
|
||||
except Errors.ConfigurationError:
|
||||
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
|
||||
"Missing 'pygccxml' Python module")
|
||||
return
|
||||
|
||||
out = subprocess.Popen([conf.env['PYTHON'][0], "-c",
|
||||
"import pygccxml; print pygccxml.__version__"],
|
||||
stdout=subprocess.PIPE).communicate()[0]
|
||||
pygccxml_version_str = out.strip()
|
||||
# Bug 2013: pygccxml versions > 1.0.0 prepend a 'v' to version number
|
||||
pygccxml_version_str = pygccxml_version_str.lstrip('v')
|
||||
pygccxml_version = tuple([int(x) for x in pygccxml_version_str.split('.')])
|
||||
conf.msg('Checking for pygccxml version', pygccxml_version_str)
|
||||
if not (pygccxml_version >= REQUIRED_PYGCCXML_VERSION):
|
||||
Logs.warn("pygccxml (found %s) is too old (need %s) => "
|
||||
"automatic scanning of API definitions will not be possible" %
|
||||
(pygccxml_version_str,
|
||||
'.'.join([str(x) for x in REQUIRED_PYGCCXML_VERSION])))
|
||||
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
|
||||
"pygccxml too old")
|
||||
return
|
||||
|
||||
|
||||
## Check gccxml version
|
||||
try:
|
||||
gccxml = conf.find_program('gccxml', var='GCCXML')
|
||||
except WafError:
|
||||
gccxml = None
|
||||
if not gccxml:
|
||||
Logs.warn("gccxml missing; automatic scanning of API definitions will not be possible")
|
||||
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
|
||||
"gccxml missing")
|
||||
return
|
||||
|
||||
gccxml_version_line = os.popen(gccxml[0] + " --version").readline().strip()
|
||||
m = re.match( "^GCC-XML version (\d\.\d(\.\d)?)$", gccxml_version_line)
|
||||
try:
|
||||
gccxml_version = m.group(1)
|
||||
gccxml_version_ok = ([int(s) for s in gccxml_version.split('.')] >= [0, 9])
|
||||
except AttributeError:
|
||||
gccxml_version = gccxml_version_line
|
||||
gccxml_version_ok = False
|
||||
conf.msg('Checking for gccxml version', gccxml_version)
|
||||
if not gccxml_version_ok:
|
||||
Logs.warn("gccxml version unknown or too old, need version >= 0.9; automatic scanning of API definitions will not be possible")
|
||||
conf.report_optional_feature("pygccxml", "Python API Scanning Support", False,
|
||||
"gccxml too old")
|
||||
return
|
||||
|
||||
## If we reached
|
||||
conf.env['ENABLE_PYTHON_SCANNING'] = True
|
||||
conf.report_optional_feature("pygccxml", "Python API Scanning Support", True, None)
|
||||
|
||||
# ---------------------
|
||||
|
||||
def get_headers_map(bld):
|
||||
headers_map = {} # header => module
|
||||
for ns3headers in bld.all_task_gen:
|
||||
if 'ns3header' in getattr(ns3headers, "features", []):
|
||||
if ns3headers.module.endswith('-test'):
|
||||
continue
|
||||
for h in ns3headers.to_list(ns3headers.headers):
|
||||
headers_map[os.path.basename(h)] = ns3headers.module
|
||||
return headers_map
|
||||
|
||||
def get_module_path(bld, module):
|
||||
for ns3headers in bld.all_task_gen:
|
||||
if 'ns3header' in getattr(ns3headers, "features", []):
|
||||
if ns3headers.module == module:
|
||||
break
|
||||
else:
|
||||
raise ValueError("Module %r not found" % module)
|
||||
return ns3headers.path.abspath()
|
||||
|
||||
class apiscan_task(Task.Task):
|
||||
"""Uses gccxml to scan the file 'everything.h' and extract API definitions.
|
||||
"""
|
||||
after = 'gen_ns3_module_header ns3header'
|
||||
before = 'cxx command'
|
||||
color = "BLUE"
|
||||
def __init__(self, curdirnode, env, bld, target, cflags, module):
|
||||
self.bld = bld
|
||||
super(apiscan_task, self).__init__(generator=self, env=env)
|
||||
self.curdirnode = curdirnode
|
||||
self.env = env
|
||||
self.target = target
|
||||
self.cflags = cflags
|
||||
self.module = module
|
||||
|
||||
def display(self):
|
||||
return 'api-scan-%s\n' % (self.target,)
|
||||
|
||||
def uid(self):
|
||||
try:
|
||||
return self.uid_
|
||||
except AttributeError:
|
||||
m = Utils.md5()
|
||||
up = m.update
|
||||
up(self.__class__.__name__.encode())
|
||||
up(self.curdirnode.abspath().encode())
|
||||
up(self.target)
|
||||
self.uid_ = m.digest()
|
||||
return self.uid_
|
||||
|
||||
def run(self):
|
||||
self.inputs = [self.bld.bldnode.find_resource("ns3/{0}-module.h".format(self.module))]
|
||||
self.outputs = [self.bld.srcnode.find_resource("src/{}/bindings/modulegen__{}.py".format(self.module, self.target))]
|
||||
top_builddir = self.bld.bldnode.abspath()
|
||||
module_path = get_module_path(self.bld, self.module)
|
||||
headers_map = get_headers_map(self.bld)
|
||||
scan_header = os.path.join(top_builddir, "ns3", "%s-module.h" % self.module)
|
||||
|
||||
if not os.path.exists(scan_header):
|
||||
Logs.error("Cannot apiscan module %r: %s does not exist" % (self.module, scan_header))
|
||||
return 0
|
||||
|
||||
argv = [
|
||||
self.env['PYTHON'][0],
|
||||
os.path.join(self.curdirnode.abspath(), 'ns3modulescan-modular.py'), # scanning script
|
||||
top_builddir,
|
||||
self.module,
|
||||
repr(get_headers_map(self.bld)),
|
||||
os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)), # output file
|
||||
self.cflags,
|
||||
]
|
||||
scan = subprocess.Popen(argv, stdin=subprocess.PIPE)
|
||||
retval = scan.wait()
|
||||
return retval
|
||||
|
||||
def runnable_status(self):
|
||||
# By default, Waf Task will skip running a task if the signature of
|
||||
# the build has not changed. We want this task to always run if
|
||||
# invoked by the user, particularly since --apiscan=all will require
|
||||
# invoking this task many times, once per module.
|
||||
return RUN_ME
|
||||
|
||||
def get_modules_and_headers(bld):
|
||||
"""
|
||||
Gets a dict of
|
||||
module_name => ([module_dep1, module_dep2, ...], [module_header1, module_header2, ...])
|
||||
tuples, one for each module.
|
||||
"""
|
||||
|
||||
retval = {}
|
||||
for module in bld.all_task_gen:
|
||||
if not module.name.startswith('ns3-'):
|
||||
continue
|
||||
if module.name.endswith('-test'):
|
||||
continue
|
||||
module_name = module.name[4:] # strip the ns3- prefix
|
||||
## find the headers object for this module
|
||||
headers = []
|
||||
for ns3headers in bld.all_task_gen:
|
||||
if 'ns3header' not in getattr(ns3headers, "features", []):
|
||||
continue
|
||||
if ns3headers.module != module_name:
|
||||
continue
|
||||
for source in ns3headers.to_list(ns3headers.headers):
|
||||
headers.append(os.path.basename(source))
|
||||
retval[module_name] = (list(module.module_deps), headers)
|
||||
return retval
|
||||
|
||||
|
||||
|
||||
|
||||
class gen_ns3_compat_pymod_task(Task.Task):
|
||||
"""Generates a 'ns3.py' compatibility module."""
|
||||
before = 'cxx'
|
||||
color = 'BLUE'
|
||||
|
||||
def run(self):
|
||||
assert len(self.outputs) == 1
|
||||
outfile = file(self.outputs[0].abspath(), "w")
|
||||
print >> outfile, "import warnings"
|
||||
print >> outfile, 'warnings.warn("the ns3 module is a compatibility layer '\
|
||||
'and should not be used in newly written code", DeprecationWarning, stacklevel=2)'
|
||||
print >> outfile
|
||||
for module in self.bld.env['PYTHON_MODULES_BUILT']:
|
||||
print >> outfile, "from ns.%s import *" % (module.replace('-', '_'))
|
||||
outfile.close()
|
||||
return 0
|
||||
|
||||
|
||||
|
||||
def build(bld):
|
||||
if Options.options.python_disable:
|
||||
return
|
||||
|
||||
env = bld.env
|
||||
set_pybindgen_pythonpath(env)
|
||||
|
||||
if Options.options.apiscan:
|
||||
if not env['ENABLE_PYTHON_SCANNING']:
|
||||
raise WafError("Cannot re-scan python bindings: (py)gccxml not available")
|
||||
scan_targets = []
|
||||
if sys.platform == 'cygwin':
|
||||
scan_targets.append(('gcc_cygwin', ''))
|
||||
else:
|
||||
import struct
|
||||
if struct.calcsize('I') == 4 and struct.calcsize('L') == 8 and struct.calcsize('P') == 8:
|
||||
if not Options.options.no32bit_scan:
|
||||
scan_targets.append(('gcc_ILP32', '-m32'))
|
||||
scan_targets.append(('gcc_LP64', '-m64'))
|
||||
elif struct.calcsize('I') == 4 and struct.calcsize('L') == 4 and struct.calcsize('P') == 4:
|
||||
scan_targets.append(('gcc_ILP32', ''))
|
||||
else:
|
||||
raise WafError("Cannot scan python bindings for unsupported data model")
|
||||
|
||||
test_module_path = bld.path.find_dir("../../src/test")
|
||||
if Options.options.apiscan == 'all':
|
||||
scan_modules = []
|
||||
for mod in bld.all_task_gen:
|
||||
if not mod.name.startswith('ns3-'):
|
||||
continue
|
||||
if mod.path.is_child_of(test_module_path):
|
||||
continue
|
||||
if mod.name.endswith('-test'):
|
||||
continue
|
||||
bindings_enabled = (mod.name in env.MODULAR_BINDINGS_MODULES)
|
||||
#print mod.name, bindings_enabled
|
||||
if bindings_enabled:
|
||||
scan_modules.append(mod.name.split('ns3-')[1])
|
||||
else:
|
||||
scan_modules = Options.options.apiscan.split(',')
|
||||
print "Modules to scan: ", scan_modules
|
||||
for target, cflags in scan_targets:
|
||||
group = bld.get_group(bld.current_group)
|
||||
for module in scan_modules:
|
||||
group.append(apiscan_task(bld.path, env, bld, target, cflags, module))
|
||||
return
|
||||
|
||||
|
||||
if env['ENABLE_PYTHON_BINDINGS']:
|
||||
task = gen_ns3_compat_pymod_task(env=env.derive())
|
||||
task.set_outputs(bld.path.find_or_declare("ns3.py"))
|
||||
task.dep_vars = ['PYTHON_MODULES_BUILT']
|
||||
task.bld = bld
|
||||
grp = bld.get_group(bld.current_group)
|
||||
grp.append(task)
|
||||
|
||||
bld(features='copy', source="ns__init__.py", target='ns/__init__.py')
|
||||
bld.install_as('${PYTHONARCHDIR}/ns/__init__.py', 'ns__init__.py')
|
||||
|
||||
|
||||
# note: the actual build commands for the python bindings are in
|
||||
# src/wscript, not here.
|
||||
|
||||
@@ -0,0 +1,561 @@
|
||||
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
|
||||
# Copyright (c) 2006 INRIA
|
||||
# All rights reserved.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License version 2 as
|
||||
# published by the Free Software Foundation;
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#
|
||||
# Author: Mathieu Lacage <mathieu.lacage@sophia.inria.fr>
|
||||
|
||||
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
from SCons.Environment import Environment
|
||||
from SCons.Builder import Builder
|
||||
from SCons.Action import Action
|
||||
import SCons
|
||||
|
||||
# hack stolen from wengo
|
||||
# to get an ARGUMENTS defined correctly
|
||||
try:
|
||||
ARGUMENTS = SCons.Script.ARGUMENTS
|
||||
COMMAND_LINE_TARGETS = SCons.Script.COMMAND_LINE_TARGETS
|
||||
except AttributeError:
|
||||
from SCons.Script.SConscript import Arguments
|
||||
from SCons.Script.SConscript import CommandLineTargets
|
||||
ARGUMENTS = Arguments
|
||||
COMMAND_LINE_TARGETS = CommandLineTargets
|
||||
|
||||
class Ns3Module:
|
||||
def __init__(self, name, dir):
|
||||
self.sources = []
|
||||
self.inst_headers = []
|
||||
self.headers = []
|
||||
self.extra_dist = []
|
||||
self.deps = []
|
||||
self.external_deps = []
|
||||
self.config = []
|
||||
self.name = name
|
||||
self.dir = dir
|
||||
self.executable = False
|
||||
self.library = True
|
||||
self.ldflags = []
|
||||
self.header_inst_dir = ''
|
||||
def set_library(self):
|
||||
self.library = True
|
||||
self.executable = False
|
||||
def set_executable(self):
|
||||
self.library = False
|
||||
self.executable = True
|
||||
def add_config(self, config_fn):
|
||||
self.config.append(config_fn)
|
||||
def add_extra_dist(self, dist):
|
||||
self.extra_dist.append(dist)
|
||||
def add_external_dep(self, dep):
|
||||
self.external_deps.append(dep)
|
||||
def add_dep(self, dep):
|
||||
self.deps.append(dep)
|
||||
def add_deps(self, deps):
|
||||
self.deps.extend(deps)
|
||||
def add_source(self, source):
|
||||
self.sources.append(source)
|
||||
def add_sources(self, sources):
|
||||
self.sources.extend(sources)
|
||||
def add_header(self, header):
|
||||
self.headers.append(header)
|
||||
def add_headers(self, headers):
|
||||
self.headers.extend(headers)
|
||||
def add_inst_header(self, header):
|
||||
self.inst_headers.append(header)
|
||||
def add_inst_headers(self, headers):
|
||||
self.inst_headers.extend(headers)
|
||||
def add_ldflags (self, ldflags):
|
||||
self.ldflags.extend (ldflags)
|
||||
def add_ldflag (self, ldflag):
|
||||
self.add_ldflags ([ldflag])
|
||||
def set_header_inst_dir (self, inst_dir):
|
||||
self.header_inst_dir = inst_dir
|
||||
|
||||
|
||||
def MyCopyAction(target, source, env):
|
||||
try:
|
||||
if len(target) == len(source):
|
||||
for i in range(len(target)):
|
||||
shutil.copy(source[i].path, target[i].path)
|
||||
return 0
|
||||
else:
|
||||
return 'invalid target/source match'
|
||||
except:
|
||||
print
|
||||
return 'exception'
|
||||
def MyCopyActionPrint(target, source, env):
|
||||
if len(target) == len(source):
|
||||
output = ''
|
||||
for i in range(len(target)):
|
||||
output = output + 'copy \'' + source[i].path + '\' to \'' + target[i].path + '\''
|
||||
if i < len(target) - 1:
|
||||
output = output + '\n'
|
||||
return output
|
||||
else:
|
||||
return 'error in copy'
|
||||
def GcxxEmitter(target, source, env):
|
||||
if os.path.exists(source[0].path):
|
||||
return [target, source]
|
||||
else:
|
||||
return [[], []]
|
||||
def MyRmTree(target, source, env):
|
||||
shutil.rmtree(env['RM_DIR'])
|
||||
return 0
|
||||
def MyRmTreePrint(target, source, env):
|
||||
return ''
|
||||
def print_cmd_line(s, target, src, env):
|
||||
print 'Building ' + (' and '.join([str(x) for x in target])) + '...'
|
||||
|
||||
class Ns3BuildVariant:
|
||||
def __init__(self):
|
||||
self.static = False
|
||||
self.gcxx_deps = False
|
||||
self.gcxx_root = ''
|
||||
self.build_root = ''
|
||||
self.env = None
|
||||
|
||||
class Ns3:
|
||||
def __init__(self):
|
||||
self.__modules = []
|
||||
self.extra_dist = []
|
||||
self.build_dir = 'build'
|
||||
self.version = '0.0.1'
|
||||
self.name = 'noname'
|
||||
self.distname = 'noname'
|
||||
self.doxygen_config = ''
|
||||
def add(self, module):
|
||||
self.__modules.append(module)
|
||||
def add_extra_dist(self, dist):
|
||||
self.extra_dist.append(dist)
|
||||
def __get_module(self, name):
|
||||
for module in self.__modules:
|
||||
if module.name == name:
|
||||
return module
|
||||
return None
|
||||
def get_mod_output(self, module, variant):
|
||||
if module.executable:
|
||||
suffix = variant.env.subst(variant.env['PROGSUFFIX'])
|
||||
filename = os.path.join(variant.build_root, 'bin',
|
||||
module.name + suffix)
|
||||
else:
|
||||
if variant.static:
|
||||
prefix = variant.env['LIBPREFIX']
|
||||
suffix = variant.env['LIBSUFFIX']
|
||||
else:
|
||||
prefix = variant.env['SHLIBPREFIX']
|
||||
suffix = variant.env['SHLIBSUFFIX']
|
||||
prefix = variant.env.subst(prefix)
|
||||
suffix = variant.env.subst(suffix)
|
||||
filename = os.path.join(variant.build_root, 'lib',
|
||||
prefix + module.name + suffix)
|
||||
return filename
|
||||
def get_obj_builders(self, variant, module):
|
||||
env = variant.env.Copy ()
|
||||
objects = []
|
||||
hash = {}
|
||||
self.get_internal_deps (module, hash)
|
||||
for dep in hash.values ():
|
||||
if dep.header_inst_dir != '':
|
||||
inc_dir = os.path.join(variant.build_root, 'include',
|
||||
self.name, dep.header_inst_dir)
|
||||
env.Append (CPPPATH = [inc_dir])
|
||||
|
||||
if len(module.config) > 0:
|
||||
src_config_file = os.path.join(self.build_dir, 'config', module.name + '-config.h')
|
||||
tgt_config_file = os.path.join(variant.build_root, 'include',
|
||||
self.name, module.name + '-config.h')
|
||||
|
||||
for source in module.sources:
|
||||
obj_file = os.path.splitext(source)[0] + '.o'
|
||||
tgt = os.path.join(variant.build_root, module.dir, obj_file)
|
||||
src = os.path.join(module.dir, source)
|
||||
if variant.static:
|
||||
obj_builder = env.StaticObject(target = tgt, source = src)
|
||||
else:
|
||||
obj_builder = env.SharedObject(target = tgt, source = src)
|
||||
if len(module.config) > 0:
|
||||
config_file = env.MyCopyBuilder(target = [tgt_config_file],
|
||||
source = [src_config_file])
|
||||
env.Depends(obj_builder, config_file)
|
||||
if variant.gcxx_deps:
|
||||
gcno_tgt = os.path.join(variant.build_root, module.dir,
|
||||
os.path.splitext(source)[0] + '.gcno')
|
||||
gcda_tgt = os.path.join(variant.build_root, module.dir,
|
||||
os.path.splitext(source)[0] + '.gcda')
|
||||
gcda_src = os.path.join(variant.gcxx_root, module.dir,
|
||||
os.path.splitext(source)[0] + '.gcda')
|
||||
gcno_src = os.path.join(variant.gcxx_root, module.dir,
|
||||
os.path.splitext(source)[0] + '.gcno')
|
||||
gcno_builder = env.CopyGcxxBuilder(target = gcno_tgt, source = gcno_src)
|
||||
gcda_builder = env.CopyGcxxBuilder(target = gcda_tgt, source = gcda_src)
|
||||
env.Depends(obj_builder, gcda_builder)
|
||||
env.Depends(obj_builder, gcno_builder)
|
||||
objects.append(obj_builder)
|
||||
return objects
|
||||
def get_internal_deps(self, module, hash):
|
||||
for dep_name in module.deps:
|
||||
dep = self.__get_module(dep_name)
|
||||
hash[dep_name] = dep
|
||||
self.get_internal_deps(dep, hash)
|
||||
def get_external_deps(self, module):
|
||||
hash = {}
|
||||
self.get_internal_deps(module, hash)
|
||||
ext_hash = {}
|
||||
for mod in hash.values():
|
||||
for ext_dep in mod.external_deps:
|
||||
ext_hash[ext_dep] = 1
|
||||
return ext_hash.keys()
|
||||
def get_sorted_deps(self, module):
|
||||
h = {}
|
||||
self.get_internal_deps(module, h)
|
||||
modules = []
|
||||
for dep in h.keys():
|
||||
deps_copy = []
|
||||
mod = h[dep]
|
||||
deps_copy.extend(mod.deps)
|
||||
modules.append([mod, deps_copy])
|
||||
sorted = []
|
||||
while len(modules) > 0:
|
||||
to_remove = []
|
||||
for item in modules:
|
||||
if len(item[1]) == 0:
|
||||
to_remove.append(item[0].name)
|
||||
for item in to_remove:
|
||||
for i in modules:
|
||||
if item in i[1]:
|
||||
i[1].remove(item)
|
||||
new_modules = []
|
||||
for mod in modules:
|
||||
found = False
|
||||
for i in to_remove:
|
||||
if i == mod[0].name:
|
||||
found = True
|
||||
break
|
||||
if not found:
|
||||
new_modules.append(mod)
|
||||
modules = new_modules
|
||||
sorted.extend(to_remove)
|
||||
sorted.reverse()
|
||||
# append external deps
|
||||
ext_deps = self.get_external_deps(module)
|
||||
for dep in ext_deps:
|
||||
sorted.append(dep)
|
||||
return sorted
|
||||
|
||||
def gen_mod_dep(self, variant):
|
||||
build_root = variant.build_root
|
||||
cpp_path = os.path.join(variant.build_root, 'include')
|
||||
env = variant.env
|
||||
env.Append(CPPPATH = [cpp_path])
|
||||
header_dir = os.path.join(build_root, 'include', self.name)
|
||||
lib_path = os.path.join(build_root, 'lib')
|
||||
env.Append (LIBPATH = [lib_path])
|
||||
module_builders = []
|
||||
for module in self.__modules:
|
||||
my_env = env.Copy ();
|
||||
objects = self.get_obj_builders(variant, module)
|
||||
libs = self.get_sorted_deps(module)
|
||||
my_env.Append (LIBS = libs)
|
||||
my_env.Append (LINKFLAGS = module.ldflags)
|
||||
|
||||
filename = self.get_mod_output(module, variant)
|
||||
if module.executable:
|
||||
module_builder = my_env.Program(target=filename, source=objects)
|
||||
else:
|
||||
if variant.static:
|
||||
module_builder = my_env.StaticLibrary(target=filename, source=objects)
|
||||
else:
|
||||
module_builder = my_env.SharedLibrary(target=filename, source=objects)
|
||||
|
||||
for dep_name in module.deps:
|
||||
dep = self.__get_module(dep_name)
|
||||
my_env.Depends(module_builder, self.get_mod_output(dep, variant))
|
||||
|
||||
for header in module.inst_headers:
|
||||
if module.header_inst_dir != '':
|
||||
tgt = os.path.join(header_dir, module.header_inst_dir, header)
|
||||
else:
|
||||
tgt = os.path.join(header_dir, header)
|
||||
src = os.path.join(module.dir, header)
|
||||
#builder = env.Install(target = tgt, source = src)
|
||||
header_builder = my_env.MyCopyBuilder(target=tgt, source=src)
|
||||
my_env.Depends(module_builder, header_builder)
|
||||
|
||||
module_builders.append(module_builder)
|
||||
return module_builders
|
||||
def gen_mod_config(self, env):
|
||||
config_dir = os.path.join(self.build_dir, 'config')
|
||||
for module in self.__modules:
|
||||
if len(module.config) > 0:
|
||||
config_file = os.path.join(config_dir, module.name + '-config.h')
|
||||
config_file_guard = module.name + '_CONFIG_H'
|
||||
config_file_guard.upper()
|
||||
if not os.path.isfile(config_file):
|
||||
if not os.path.isdir(config_dir):
|
||||
os.makedirs(config_dir)
|
||||
outfile = open(config_file, 'w')
|
||||
outfile.write('#ifndef ' + config_file_guard + '\n')
|
||||
outfile.write('#define ' + config_file_guard + '\n')
|
||||
config = env.Configure()
|
||||
for fn in module.config:
|
||||
output = fn(env, config)
|
||||
for o in output:
|
||||
outfile.write(o)
|
||||
outfile.write('\n')
|
||||
outfile.write('#endif /*' + config_file_guard + '*/\n')
|
||||
config.Finish()
|
||||
def generate_dependencies(self):
|
||||
inheritenv = (ARGUMENTS.get('inheritenv', 'n') in 'yY1')
|
||||
if inheritenv:
|
||||
env = Environment(ENV=os.environ)
|
||||
else:
|
||||
env = Environment()
|
||||
self.gen_mod_config(env)
|
||||
cc = env['CC']
|
||||
cxx = env.subst(env['CXX'])
|
||||
if cc == '':
|
||||
print "Missing C compiler."
|
||||
env.Exit (1);
|
||||
if cxx == '':
|
||||
print "Missing C++ compiler."
|
||||
env.Exit (1);
|
||||
common_flags = ARGUMENTS.get('cflags', '').split(' ')
|
||||
cxxflags = ARGUMENTS.get('cxxflags', '').split(' ')
|
||||
ldflags = ARGUMENTS.get('ldflags', '').split(' ')
|
||||
if cc == 'cl' and cxx == 'cl':
|
||||
env = Environment(tools=['mingw'])
|
||||
cc = env['CC']
|
||||
cxx = env.subst(env['CXX'])
|
||||
if cc == 'gcc' and cxx == 'g++':
|
||||
common_flags.extend(['-g3', '-Wall', '-Werror'])
|
||||
debug_flags = []
|
||||
opti_flags = ['-O3']
|
||||
elif cc == 'cl' and cxx == 'cl':
|
||||
env = Environment(ENV=os.environ)
|
||||
debug_flags = ['-W1', '-GX', '-EHsc', '-D_DEBUG', '/MDd']
|
||||
opti_flags = ['-O2', '-EHsc', '-DNDEBUG', '/MD']
|
||||
cc = ARGUMENTS.get ('cc', '')
|
||||
cxx = ARGUMENTS.get ('cxx', '')
|
||||
if cc != '':
|
||||
env.Replace (CC = cc)
|
||||
if cxx != '':
|
||||
env.Replace (CXX = cxx)
|
||||
env.Append(CCFLAGS = common_flags,
|
||||
CPPDEFINES = ['RUN_SELF_TESTS'],
|
||||
TARFLAGS = '-c -z',
|
||||
CPPFLAGS = cxxflags,
|
||||
LINKFLAGS = ldflags)
|
||||
if env['PLATFORM'] == 'posix':
|
||||
env.Append(LINKFLAGS = ' -z origin')
|
||||
env.Append(RPATH=env.Literal(os.path.join('\\$$ORIGIN', os.pardir, 'lib')))
|
||||
verbose = ARGUMENTS.get('verbose', 'n')
|
||||
if verbose == 'n':
|
||||
env['PRINT_CMD_LINE_FUNC'] = print_cmd_line
|
||||
header_builder = Builder(action = Action(MyCopyAction, strfunction=MyCopyActionPrint))
|
||||
env.Append(BUILDERS = {'MyCopyBuilder':header_builder})
|
||||
gcxx_builder = Builder(action = Action(MyCopyAction, strfunction=MyCopyActionPrint),
|
||||
emitter = GcxxEmitter)
|
||||
env.Append(BUILDERS = {'CopyGcxxBuilder':gcxx_builder})
|
||||
variant = Ns3BuildVariant()
|
||||
builders = []
|
||||
|
||||
|
||||
gcov_env = env.Copy()
|
||||
gcov_env.Append(CFLAGS = ['-fprofile-arcs', '-ftest-coverage'],
|
||||
CXXFLAGS = ['-fprofile-arcs', '-ftest-coverage'],
|
||||
LINKFLAGS = ['-fprofile-arcs'])
|
||||
# code coverage analysis
|
||||
variant.static = False
|
||||
variant.env = gcov_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'gcov')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
gcov_env.Alias('gcov', builder)
|
||||
gcov_env.Alias('lcov-report')
|
||||
if 'lcov-report' in COMMAND_LINE_TARGETS:
|
||||
lcov_report_dir = os.path.join(self.build_dir, 'lcov-report')
|
||||
create_dir_command = "rm -rf " + lcov_report_dir
|
||||
create_dir_command += " && mkdir " + lcov_report_dir + ";"
|
||||
gcov_env.Execute(create_dir_command)
|
||||
info_file = os.path.join(lcov_report_dir, self.name + '.info')
|
||||
lcov_command = "utils/lcov/lcov -c -d . -o " + info_file
|
||||
lcov_command += " --source-dirs=" + os.getcwd()
|
||||
lcov_command += ":" + os.path.join(os.getcwd(),
|
||||
variant.build_root,
|
||||
'include')
|
||||
gcov_env.Execute(lcov_command)
|
||||
genhtml_command = "utils/lcov/genhtml -o " + lcov_report_dir
|
||||
genhtml_command += " " + info_file
|
||||
gcov_env.Execute(genhtml_command)
|
||||
|
||||
|
||||
|
||||
opt_env = env.Copy()
|
||||
opt_env.Append(CFLAGS=opti_flags,
|
||||
CXXFLAGS=opti_flags,
|
||||
CPPDEFINES=['NDEBUG'])
|
||||
# optimized static support
|
||||
variant.static = True
|
||||
variant.env = opt_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'opt-static')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
opt_env.Alias('opt-static', builder)
|
||||
|
||||
|
||||
opt_env = env.Copy()
|
||||
opt_env.Append(CFLAGS = opti_flags,
|
||||
CXXFLAGS = opti_flags,
|
||||
CPPDEFINES = ['NDEBUG'])
|
||||
# optimized shared support
|
||||
variant.static = False
|
||||
variant.env = opt_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'opt-shared')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
opt_env.Alias('opt-shared', builder)
|
||||
|
||||
|
||||
arc_env = env.Copy()
|
||||
arc_env.Append(CFLAGS=opti_flags,
|
||||
CXXFLAGS=opti_flags,
|
||||
CPPDEFINES=['NDEBUG'])
|
||||
arc_env.Append(CFLAGS=['-frandom-seed=0', '-fprofile-generate'],
|
||||
CXXFLAGS=['-frandom-seed=0', '-fprofile-generate'],
|
||||
LINKFLAGS=['-frandom-seed=0', '-fprofile-generate'])
|
||||
# arc profiling
|
||||
variant.static = False
|
||||
variant.env = arc_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'opt-arc')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
arc_env.Alias('opt-arc', builder)
|
||||
|
||||
|
||||
arcrebuild_env = env.Copy()
|
||||
arcrebuild_env.Append(CFLAGS=opti_flags,
|
||||
CXXFLAGS=opti_flags,
|
||||
CPPDEFINES=['NDEBUG'])
|
||||
arcrebuild_env.Append(CFLAGS=['-frandom-seed=0', '-fprofile-use'],
|
||||
CXXFLAGS=['-frandom-seed=0', '-fprofile-use'],
|
||||
LINKFLAGS=['-frandom-seed=0', '-fprofile-use'])
|
||||
# arc rebuild
|
||||
variant.static = False
|
||||
variant.env = arcrebuild_env
|
||||
variant.gcxx_deps = True
|
||||
variant.gcxx_root = os.path.join(self.build_dir, 'opt-arc')
|
||||
variant.build_root = os.path.join(self.build_dir, 'opt-arc-rebuild')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
arcrebuild_env.Alias('opt-arc-rebuild', builder)
|
||||
variant.gcxx_deps = False
|
||||
|
||||
|
||||
|
||||
|
||||
dbg_env = env.Copy()
|
||||
dbg_env.Append(CFLAGS = debug_flags,
|
||||
CXXFLAGS = debug_flags,
|
||||
CPPDEFINES = ['NS3_DEBUG_ENABLE',
|
||||
'NS3_ASSERT_ENABLE'])
|
||||
# debug static support
|
||||
variant.static = True
|
||||
variant.env = dbg_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'dbg-static')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
dbg_env.Alias('dbg-static', builder)
|
||||
|
||||
dbg_env = env.Copy()
|
||||
dbg_env.Append(CFLAGS=debug_flags,
|
||||
CXXFLAGS=debug_flags,
|
||||
CPPDEFINES = ['NS3_DEBUG_ENABLE',
|
||||
'NS3_ASSERT_ENABLE'])
|
||||
# debug shared support
|
||||
variant.static = False
|
||||
variant.env = dbg_env
|
||||
variant.build_root = os.path.join(self.build_dir, 'dbg-shared')
|
||||
builders = self.gen_mod_dep(variant)
|
||||
for builder in builders:
|
||||
dbg_env.Alias('dbg-shared', builder)
|
||||
|
||||
env.Alias('dbg', 'dbg-shared')
|
||||
env.Alias('opt', 'opt-shared')
|
||||
env.Default('dbg')
|
||||
env.Alias('all', ['dbg-shared', 'dbg-static', 'opt-shared', 'opt-static'])
|
||||
|
||||
|
||||
# dist support
|
||||
dist_env = env.Copy()
|
||||
if dist_env['PLATFORM'] == 'posix' or dist_env['PLATFORM'] == 'darwin':
|
||||
dist_list = []
|
||||
for module in self.__modules:
|
||||
for f in module.sources:
|
||||
dist_list.append(os.path.join(module.dir, f))
|
||||
for f in module.headers:
|
||||
dist_list.append(os.path.join(module.dir, f))
|
||||
for f in module.inst_headers:
|
||||
dist_list.append(os.path.join(module.dir, f))
|
||||
for f in module.extra_dist:
|
||||
dist_list.append(os.path.join(module.dir, f))
|
||||
for f in self.extra_dist:
|
||||
dist_list.append(f)
|
||||
dist_list.append (self.doxygen_config)
|
||||
dist_list.append('SConstruct')
|
||||
dist_list.append('build.py')
|
||||
|
||||
targets = []
|
||||
basename = self.distname + '-' + self.version
|
||||
for src in dist_list:
|
||||
tgt = os.path.join(basename, src)
|
||||
targets.append(dist_env.MyCopyBuilder(target=tgt, source=src))
|
||||
tar = basename + '.tar.gz'
|
||||
zip = basename + '.zip'
|
||||
tmp_tar = os.path.join(self.build_dir, tar)
|
||||
tmp_zip = os.path.join(self.build_dir, zip)
|
||||
dist_tgt = [tar, zip]
|
||||
dist_src = [tmp_tar, tmp_zip]
|
||||
dist_env.Tar(tmp_tar, targets)
|
||||
dist_env.Zip(tmp_zip, targets)
|
||||
dist_builder = dist_env.MyCopyBuilder(target=dist_tgt, source=dist_src)
|
||||
dist_env.Alias('dist', dist_builder)
|
||||
dist_env.Append(RM_DIR=basename)
|
||||
dist_env.AddPostAction(dist_builder, dist_env.Action(MyRmTree,
|
||||
strfunction=MyRmTreePrint))
|
||||
dist_builder = dist_env.MyCopyBuilder(target=dist_tgt, source=dist_src)
|
||||
dist_env.Alias('fastdist', dist_tgt)
|
||||
|
||||
# distcheck
|
||||
distcheck_list = []
|
||||
for src in dist_list:
|
||||
tgt = os.path.join(self.build_dir, basename, src)
|
||||
distcheck_list.append(tgt)
|
||||
untar = env.Command(distcheck_list, tar,
|
||||
['cd ' + self.build_dir + ' && tar -zxf ../' + tar])
|
||||
scons_dir = os.path.join(self.build_dir, basename)
|
||||
distcheck_builder = env.Command('x', distcheck_list,
|
||||
['cd ' + scons_dir + ' && scons'])
|
||||
env.AlwaysBuild(distcheck_builder)
|
||||
env.Alias('distcheck', distcheck_builder)
|
||||
if self.doxygen_config != '':
|
||||
doxy = env.Command('doc/html/*', self.doxygen_config,
|
||||
['doxygen ' + self.doxygen_config])
|
||||
env.AlwaysBuild(doxy)
|
||||
env.Alias('doc', doxy)
|
||||
@@ -0,0 +1,102 @@
|
||||
The main ns-3 build system is SCons. Read the file build.txt
|
||||
for SCons instructions.
|
||||
|
||||
Waf is an alternative build system, similar to SCons. ns-3 now is
|
||||
able to build with Waf, in parallel to SCons.
|
||||
|
||||
(http://www.freehackers.org/~tnagy/waf.html)
|
||||
|
||||
Note: the Waf build scripts are experimental at this stage.
|
||||
Gustavo Carneiro (gjcarneiro@gmail.com) is the maintainer.
|
||||
|
||||
=== Building with Waf ===
|
||||
|
||||
To build ns-3 with waf type the commands:
|
||||
1. ./waf configure [options]
|
||||
2. ./waf
|
||||
|
||||
[ Note: if ./waf does not exist, see the section "Note for developers" below ]
|
||||
|
||||
To see valid configure options, type ./waf --help. The most important
|
||||
option is -d <debug level>. Valid debug levels (which are listed in
|
||||
./waf --help) are: ultradebug, debug, release, and optimized.
|
||||
|
||||
The resulting binaries are placed in build/<debuglevel>/srcpath.
|
||||
|
||||
Other waf usages include:
|
||||
|
||||
1. ./waf check
|
||||
Runs the unit tests
|
||||
|
||||
2. ./waf --doxygen
|
||||
Run doxygen to generate documentation
|
||||
|
||||
3. ./waf --lcov-report
|
||||
Run code coverage analysis (assuming the project was configured
|
||||
with --enable-gcov)
|
||||
|
||||
4. ./waf --run "program [args]"
|
||||
Run a ns3 program, given its target name, with the given
|
||||
arguments. This takes care of automatically modifying the the
|
||||
path for finding the ns3 dynamic libraries in the environment
|
||||
before running the program. Note: the "program [args]" string is
|
||||
parsed using POSIX shell rules.
|
||||
|
||||
5. ./waf --shell
|
||||
Starts a nested system shell with modified environment to run ns3 programs.
|
||||
|
||||
|
||||
=== Extending ns-3 ===
|
||||
|
||||
To add new modules:
|
||||
1. Create the module directory under src (or src/devices, or whatever);
|
||||
2. Add the source files to it;
|
||||
3. Add a 'wscript' describing it;
|
||||
4. Add the module subdirectory name to the all_modules list in src/wscript.
|
||||
|
||||
A module's wscript file is basically a regular Waf script. A ns-3
|
||||
module is created as a cpp/shlib object, like this:
|
||||
|
||||
def build(bld):
|
||||
obj = bld.create_obj('cpp', 'shlib')
|
||||
|
||||
## set module name; by convention it starts with ns3-
|
||||
obj.name = 'ns3-mymodule'
|
||||
obj.target = obj.name
|
||||
|
||||
## list dependencies to other modules
|
||||
obj.uselib_local = ['ns3-core']
|
||||
|
||||
## list source files (private or public header files excluded)
|
||||
obj.source = [
|
||||
'mymodule.cc',
|
||||
]
|
||||
|
||||
## list module public header files
|
||||
headers = bld.create_obj('ns3header')
|
||||
headers.source = [
|
||||
'mymodule-header.h',
|
||||
]
|
||||
|
||||
|
||||
=== Note for developers ===
|
||||
|
||||
The ns-3 code repository does not contain the waf script. Instead,
|
||||
developers should check it out from a subversion repository:
|
||||
|
||||
svn checkout http://waf.googlecode.com/svn/tags/ns3/ waf
|
||||
|
||||
[ note: 'tags/ns3' is a tag that represents the last svn version
|
||||
tested to work correctly with ns3, although 'trunk' will likely work
|
||||
as well ]
|
||||
|
||||
Then it can be installed system-wide with 'sudo ./waf-light install'.
|
||||
When preparing a distribution, the resulting 'waf' script, which is
|
||||
self contained (no external files needed), can be easily included in
|
||||
the tarball so that users downloading ns-3 can easily build it without
|
||||
having Waf installed (although Python >= 2.3 is still needed).
|
||||
|
||||
The command 'waf dist' can be used to create a distribution tarball.
|
||||
It includes all files in the source directory, except some particular
|
||||
extensions that are blacklisted, such as back files (ending in ~).
|
||||
|
||||
@@ -1,107 +1,186 @@
|
||||
The Waf build system is used to build ns-3. Waf is a Python-based
|
||||
build system (http://www.freehackers.org/~tnagy/waf.html)
|
||||
If you want to build ns3, you need to install scons (see
|
||||
http://www.scons.org). scons takes care of building
|
||||
the whole source tree using your system compiler. scons
|
||||
0.91.1 and 0.91.96 have been tested and are known to
|
||||
work on linux FC5, Mac os X and MinGW.
|
||||
|
||||
Note: We've added a wiki page with more complete build instructions
|
||||
than the quick ones you find below:
|
||||
http://www.nsnam.org/wiki/Installation
|
||||
To start a build, you can just type 'scons' which
|
||||
will generate a debug shared build by default, located
|
||||
in the directory 'build-dir/dbg-shared/bin' and
|
||||
'build-dir/dbg-shared/lib'.
|
||||
|
||||
=== Installing Waf ===
|
||||
All builds are built with debugging symbols. Debugging
|
||||
builds enable asserts while optimized builds disable them.
|
||||
On platforms which support it, rpath is used which means that
|
||||
the executable binaries generated link explicitely against
|
||||
the right libraries. This saves you the pain of having to
|
||||
setup environment variables to point to the right libraries.
|
||||
|
||||
The top-level ns-3 directory should contain a current waf script, so
|
||||
there is no need to have WAF installed in the system. We are using
|
||||
some extensions to WAF, which can be found in the 'waf-tools'
|
||||
directory. The upstream location for these WAF extensions is:
|
||||
(Note: An alternative build system (Waf) is being
|
||||
evaluated in the development branch of ns-3-dev on our server
|
||||
only (i.e., not in the release tarballs)-- see doc/build-waf.txt)
|
||||
|
||||
https://code.launchpad.net/~gjc/waf/cmd
|
||||
1) Options
|
||||
----------
|
||||
|
||||
- verbose: if you have installed scons 0.91.96 or higher,
|
||||
the default build output is terse. To get a more verbose
|
||||
output, you need to set the 'verbose' variable to 'y'.
|
||||
Example: scons verbose=y
|
||||
- cflags: flags for the C compiler.
|
||||
Example: scons cflags="-O3 -ffast-math"
|
||||
- cxxflags: flags for the C++ compiler.
|
||||
Example: scons cxxflags="-O3 -ffast-math"
|
||||
- ldflags: flags for the linker:
|
||||
Example: scons ldflags="-L/foo -L/bar"
|
||||
- cc: the C compiler to use:
|
||||
Example: scons cc=gcc-4.0
|
||||
- cxx: the C++ compiler to use:
|
||||
Example: scons cxx=g++-4.0
|
||||
- high-precision-as-double: set to 'y' to make sure that the
|
||||
high-precision arithmetics performed by the Time class on
|
||||
behalf of the user will use doubles. By default, the code
|
||||
uses 128 integers.
|
||||
Example: scons high-precision-as-double=y
|
||||
- inheritenv: set to 'y' if you want to make your compiler
|
||||
execute within the same environment (env vars) as your own
|
||||
shell. This is typically used to make colorgcc work.
|
||||
Example: scons inheritenv=y
|
||||
|
||||
=== Building with Waf ===
|
||||
2) Targets
|
||||
----------
|
||||
|
||||
To build ns-3 with waf type the commands from the top-level directory:
|
||||
1. ./waf configure [options]
|
||||
2. ./waf
|
||||
- doc: build the doxygen documentation.
|
||||
Example: scons doc
|
||||
|
||||
To see valid configure options, type ./waf --help. The most important
|
||||
option is -d <debug level>. Valid debug levels (which are listed in
|
||||
waf --help) are: "debug" or "optimized", with debug being default. It is
|
||||
also possible to change the flags used for compilation with (e.g.):
|
||||
CXXFLAGS="-O3" ./waf configure. By default, ns-3 is built as debug code,
|
||||
with examples and tests disabled, and with python bindings enabled.
|
||||
- dbg-shared: a debug build using shared libraries.
|
||||
The files are built in 'build-dir/dbg-shared/'.
|
||||
Example: scons dbg-shared
|
||||
|
||||
[ Note: Unlike some other build tools, to change the build target,
|
||||
the option must be supplied during the configure stage rather than
|
||||
the build stage (i.e., "./waf -d optimized" will not work; instead, do
|
||||
"./waf -d optimized configure; ./waf" ]
|
||||
- dbg-static: a debug build using static libraries
|
||||
The files are built in 'build-dir/dbg-static/'.
|
||||
Example: scons dbg-static
|
||||
|
||||
The resulting executables and libraries are placed in build/.
|
||||
- opt-shared: an optimized build using shared libraries.
|
||||
The files are built in 'build-dir/opt-shared/'.
|
||||
Example: scons opt-shared
|
||||
|
||||
Other waf usages include:
|
||||
- opt-static: an optimized build using static libraries.
|
||||
The files are built in 'build-dir/opt-static/'.
|
||||
Example: scons opt-static
|
||||
|
||||
1. ./waf configure --enable-examples --enable-tests
|
||||
Turn on examples and tests.
|
||||
- dbg: an alias for dbg-shared
|
||||
Example: scons dbg
|
||||
|
||||
2. ./waf configure --disable-python
|
||||
Disable python bindings.
|
||||
- opt: an alias for opt-shared
|
||||
Example: scons opt
|
||||
|
||||
3. ./waf --doxygen
|
||||
Run doxygen to generate documentation
|
||||
- all: alias for dbg-shared, dbg-static, opt-shared
|
||||
and opt-static
|
||||
Example: scons all
|
||||
|
||||
4. ./waf --run "program [args]"
|
||||
Run a ns3 program, given its target name, with the given
|
||||
arguments. This takes care of automatically modifying the the
|
||||
path for finding the ns3 dynamic libraries in the environment
|
||||
before running the program. Note: the "program [args]" string is
|
||||
parsed using POSIX shell rules.
|
||||
- gcov: code coverage analysis. Build a debugging version of
|
||||
the code for code coverage analysis in 'build-dir/gcov'. Once
|
||||
the code has been built, you can run various applications to
|
||||
exercise the code paths. To generate an html report from
|
||||
the gcov data, use the lcov-report target
|
||||
|
||||
4.1 ./waf --run programname --command-template "... %s ..."
|
||||
- lcov-report: generate html report of gcov data. The output
|
||||
is stored in 'build-dir/lcov-report/'.
|
||||
|
||||
Same as --run, but uses a command template with %s replaced by the
|
||||
actual program (whose name is given by --run). This can be use to
|
||||
run ns-3 programs with helper tools. For example, to run unit
|
||||
tests with valgrind, use the command:
|
||||
- dist: generate a release tarball and zipfile from the
|
||||
source tree. The tarball and zipfile name are generated
|
||||
according to the version number stored in the SConstruct
|
||||
file.
|
||||
Example in SConstruct:
|
||||
ns3 = Ns3 ()
|
||||
ns3.name = 'foo'
|
||||
ns3.version = '0.0.10'
|
||||
Example command: scons dist
|
||||
Example output files:
|
||||
foo-0.0.10.tar.gz
|
||||
foo-0.0.10.zip
|
||||
|
||||
./waf --run run-tests --command-template "valgrind %s"
|
||||
- distcheck: generate a release tarball and zipfile and
|
||||
attempt to run the 'all' target for the release tarball.
|
||||
Example: scons distcheck
|
||||
|
||||
5. ./waf --shell
|
||||
Starts a nested system shell with modified environment to run ns3 programs.
|
||||
3) How the build system works
|
||||
-----------------------------
|
||||
|
||||
6. ./waf distclean
|
||||
Cleans out the entire build/ directory
|
||||
The current build system defines what are called "ns3 modules": each module
|
||||
is a set of source files, normal header files and installable header
|
||||
files. Each module also depends on a set of other modules. We build
|
||||
modules automatically in the correct order. That is, we always start
|
||||
from the module which does not depend on any other module (core) and
|
||||
proceed with the other modules and make sure that when a module is
|
||||
built, all the modules it depends upon have already been built.
|
||||
|
||||
7. ./waf dist
|
||||
The command 'waf dist' can be used to create a distribution tarball.
|
||||
It includes all files in the source directory, except some particular
|
||||
extensions that are blacklisted, such as back files (ending in ~).
|
||||
To build a module, we:
|
||||
1) generate the .o files
|
||||
2) link the .o files together
|
||||
3) install the installable headers in the common directory
|
||||
top_build_dir/include/ns3.
|
||||
|
||||
=== Extending ns-3 ===
|
||||
This means that if you want to use a header from your own module, you
|
||||
should just include it: #include "foo.h" but if you want to include a
|
||||
header from another module, you need to include it with #include
|
||||
"ns3/bar.h". This allows you to make sure that our "public" ns3 headers
|
||||
do not conflict with existing system-level headers. For instance,
|
||||
if you were to define a header called queue.h, you would include
|
||||
ns3/queue.h rather than queue.h, when including from a separate module,
|
||||
since many systems provide a queue.h system include file.
|
||||
|
||||
To add new modules:
|
||||
1. Create the module directory under src;
|
||||
2. Add the source files to it;
|
||||
3. Add a 'wscript' describing it;
|
||||
4) How to add files to a module ?
|
||||
---------------------------------
|
||||
|
||||
A convenience program to auto-generate the template of a new module can
|
||||
be found in src/create-module.py.
|
||||
In the main SConstruct file, you can add source code
|
||||
to the add_sources method. For example, to add a foo.cc
|
||||
file to the core module, we coud do this:
|
||||
core.add_sources ('foo.cc')
|
||||
Of course, if this file implements public API, its
|
||||
header should be installable:
|
||||
core.add_inst_headers ('foo.h')
|
||||
|
||||
A module's wscript file is basically a regular Waf script. A ns-3
|
||||
module is created as a cpp/shlib object, like this:
|
||||
5) How to create a new module ?
|
||||
-------------------------------
|
||||
|
||||
def build(bld):
|
||||
module = bld.create_ns3_module('ns3-mymodule', ['core'])
|
||||
module.source = [
|
||||
'model/ns3-mymodule.cc',
|
||||
'helper/ns3-mymodule-helper.cc',
|
||||
]
|
||||
|
||||
headers = bld.new_task_gen(features=['ns3header'])
|
||||
headers.module = 'ns3-mymodule'
|
||||
headers.source = [
|
||||
'model/ns3-mymodule.h',
|
||||
'helper/ns3-mymodule-helper.h',
|
||||
]
|
||||
|
||||
if bld.env.ENABLE_EXAMPLES:
|
||||
bld.add_subdirs('examples')
|
||||
|
||||
# bld.ns3_python_bindings()
|
||||
# create a new module. First arg is the name of
|
||||
# the new module. Second arg is the directory in
|
||||
# which all source files for this module reside.
|
||||
my_module = build.Ns3Module ('my', 'src/my_dir')
|
||||
# add it to build system
|
||||
ns3.add (my_module)
|
||||
# specify module dependencies. Here, depends
|
||||
# on the 'ipv4' and 'core' modules
|
||||
my_module.add_deps (['core', 'ipv4'])
|
||||
# add source code to build located in
|
||||
# src/my_dir
|
||||
my_module.add_sources ([
|
||||
'my_a.cc',
|
||||
'my_b.cc',
|
||||
'my_c.cc'
|
||||
])
|
||||
my_module.add_sources ([
|
||||
'my_d.cc'
|
||||
])
|
||||
# add headers which are not public
|
||||
my_module.add_headers ([
|
||||
'my_a.h',
|
||||
'my_c.h'
|
||||
])
|
||||
# add headers which are public
|
||||
my_module.add_inst_headers ([
|
||||
'my_b.h'
|
||||
])
|
||||
my_module.add_inst_headers ([
|
||||
'my_d.h'
|
||||
])
|
||||
# if you need to link against an external library,
|
||||
# you must add 'external' dependencies. Here, the
|
||||
# pthread library
|
||||
my_module.add_external_dep ('pthread')
|
||||
# by default, a module is conceptually a library. If you
|
||||
# want to generate an executable from a module you need to:
|
||||
my_module.set_executable ()
|
||||
|
||||
|
||||
@@ -1,2 +1,210 @@
|
||||
The project coding style document is maintained at:
|
||||
http://www.nsnam.org/developers/contributing-code/coding-style/
|
||||
The Ns-3 Coding Style
|
||||
|
||||
/*
|
||||
* Note: This file is incomplete and will be converted to non-text (html,pdf)
|
||||
* formats at a future date
|
||||
*/
|
||||
|
||||
1) Code layout
|
||||
-----------
|
||||
|
||||
The code layout follows the GNU coding standard layout for C and extends
|
||||
it to C++. Do not use tabs for indentation. Indentation spacing is 2
|
||||
spaces as outlined below:
|
||||
|
||||
void
|
||||
Foo (void)
|
||||
{
|
||||
if (test)
|
||||
{
|
||||
// do stuff here
|
||||
}
|
||||
else
|
||||
{
|
||||
// do other stuff here
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
// do loop
|
||||
}
|
||||
|
||||
while (test)
|
||||
{
|
||||
// do while
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
// do stuff
|
||||
} while ();
|
||||
}
|
||||
|
||||
The following is not recommended:
|
||||
|
||||
if (test) statement
|
||||
|
||||
if (test)
|
||||
statement
|
||||
|
||||
for (...) statement
|
||||
|
||||
Each statement should be put on a separate line to increase readability.
|
||||
Short one-line comments can use the C++ comment style, that is, '//'
|
||||
but longer comments should use C-style comments:
|
||||
/*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
2) Naming Patterns
|
||||
---------------
|
||||
|
||||
2.1) Name encoding
|
||||
-------------
|
||||
Function, Method, and Type names should follow the CamelCase convention:
|
||||
words are joined without spaces and are capitalized. For example,
|
||||
"my computer" is transformed into MyComputer. Do not use all capital
|
||||
letters such as MAC or, PHY, but choose instead Mac or Phy. Do not use
|
||||
all capital letters, even for acronyms such as EDCA: use Edca instead.
|
||||
The goal of the CamelCase convention is to ensure that the words which
|
||||
make up a name can be separated by the eye: the initial Caps fills
|
||||
that role.
|
||||
|
||||
Variable names should follow a slight variation on the base CamelCase
|
||||
convention: camelBack. For example, the variable "user name" would be
|
||||
named "userName". This variation on the basic naming pattern is used to
|
||||
allow a reader to distinguish a variable name from its type. For example,
|
||||
"UserName userName;" would be used to declare a variable named userName
|
||||
of type UserName.
|
||||
|
||||
Global variables should be prefixed with a "g_" and member variables
|
||||
(including static member variables) should be prefixed with a "m_". The
|
||||
goal of that prefix is to give a reader a sense of where a variable of
|
||||
a given name is declared to allow the reader to locate the variable
|
||||
declaration and infer the variable type from that declaration. For example
|
||||
you could declare in your class header my-class.h:
|
||||
|
||||
class MyClass
|
||||
{
|
||||
void MyMethod (int aVar);
|
||||
int m_aVar;
|
||||
static int m_anotherVar;
|
||||
};
|
||||
|
||||
and implement in your class file my-class.cc:
|
||||
|
||||
int MyClass::m_anotherVar = 10;
|
||||
static int g_aStaticVar = 100;
|
||||
int g_aGlobalVar = 1000;
|
||||
|
||||
void
|
||||
MyClass::MyMethod (int aVar)
|
||||
{
|
||||
m_aVar = aVar;
|
||||
}
|
||||
|
||||
2.2) Choosing names
|
||||
|
||||
Variable, function, method, and type names should be based on the
|
||||
english language. Furthermore, always try to choose descriptive
|
||||
names for them. Types are often english names such as: Packet,
|
||||
Buffer, Mac, or Phy. Functions and Methods are often named
|
||||
based on verbs and adjectives: GetX, DoDispose, ClearArray, etc.
|
||||
|
||||
A long descriptive name which requires a lot of typing is always
|
||||
better than a short name which is hard to decipher. Do not use
|
||||
abbreviations in names unless the abbreviation is really unambiguous
|
||||
and obvious to everyone. Do not use short inapropriate names such
|
||||
as foo, bar, or baz. The name of an item should always match its
|
||||
purpose. As such, names such as tmp to identify a temporary
|
||||
variable or such as 'i' to identify a loop index are ok.
|
||||
|
||||
3) File layout and code organization
|
||||
---------------------------------
|
||||
|
||||
A class named MyClass should be declared in a header named my-class.h
|
||||
and implemented in a source file named my-class.cc. The goal of this
|
||||
naming pattern is to allow a reader to quickly navigate through
|
||||
the ns-3 codebase to locate the source file relevant to a specific
|
||||
type.
|
||||
|
||||
Each my-class.h header should start with the following comments: the
|
||||
first line ensures that developers who use the emacs editor will be
|
||||
able to indent your code correctly. The following lines ensure that
|
||||
your code is licensed under the GPL, that the copyright holders
|
||||
are properly identified (typically, you or your employer), and
|
||||
that the actual author of the code is identified. The latter is
|
||||
purely informational and we use it to try to track the most
|
||||
appropriate person to review a patch or fix a bug.
|
||||
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) YEAR COPYRIGHTHOLDER
|
||||
*
|
||||
* 3-paragran GPL blurb
|
||||
*
|
||||
* Author: MyName <myemail@foo.com>
|
||||
*/
|
||||
|
||||
Below these C-style comments, always include the following which
|
||||
defines a set of header guards (MY_CLASS_H) used to avoid multiple
|
||||
header includes, which ensures that your code is included
|
||||
in the "ns3" namespace and which provides a set of doxygen comments
|
||||
for the public part of your class API. Detailed information
|
||||
on the set of tags available for doxygen documentation is described
|
||||
in the doxygen website: http://www.doxygen.org.
|
||||
|
||||
#ifndef MY_CLASS_H
|
||||
#define MY_CLASS_H
|
||||
|
||||
namespace n3 {
|
||||
|
||||
/**
|
||||
* \brief short one-line description of the purpose of your class
|
||||
*
|
||||
* A longer description of the purpose of your class after a blank
|
||||
* empty line.
|
||||
*/
|
||||
class MyClass
|
||||
{
|
||||
public:
|
||||
MyClass ();
|
||||
/**
|
||||
* \param firstParam a short description of the purpose of this parameter
|
||||
* \returns a short description of what is returned from this function.
|
||||
*
|
||||
* A detailed description of the purpose of the method.
|
||||
*/
|
||||
int DoFoo (int firstParam);
|
||||
private:
|
||||
void MyPrivateMethod (void);
|
||||
int m_myPrivateMemberVariable;
|
||||
};
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
#endif /* MY_CLASS_H */
|
||||
|
||||
The my-class.cc file is structured similarly:
|
||||
|
||||
/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
|
||||
/*
|
||||
* Copyright (c) YEAR COPYRIGHTHOLDER
|
||||
*
|
||||
* 3-paragran GPL blurb
|
||||
*
|
||||
* Author: MyName <myemail@foo.com>
|
||||
*/
|
||||
#include "my-class.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
MyClass::MyClass ()
|
||||
{}
|
||||
|
||||
...
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
|
||||
@@ -1,7 +1,63 @@
|
||||
Contributing to the ns-3 project
|
||||
--------------------------------
|
||||
|
||||
ns-3 is a free, open source software project that welcomes contributions
|
||||
from users worldwide. Please see the following web page for how to
|
||||
contribute:
|
||||
http://www.nsnam.org/developers/contributing-code/
|
||||
ns-3 is an open source project backed by an NSF CISE CRI grant.
|
||||
Although the NSF PIs have specific aims to fulfill, we want others to
|
||||
contribute, and we'd like to have a broad community of users and
|
||||
developers, with the goal of a self-sustaining project downstream.
|
||||
The project is currently in a bootstrapping phase, but we welcome
|
||||
ambitious developers who might want to help shape the early design.
|
||||
|
||||
Despite the lack of a formal contribution process to the ns-3
|
||||
project, there are a number of steps which we expect every
|
||||
potential contributor to follow. These naturally stem from
|
||||
the open-source roots of the project:
|
||||
|
||||
- first, you should subscribe to the ns-developers
|
||||
mailing-list (see http://www.nsnam.org/mailing_lists.html)
|
||||
|
||||
- then, you should send an email there stating your interest
|
||||
in working on a specific part of the models and trying
|
||||
to explain how you would like to implement it, what
|
||||
resources you have, etc.
|
||||
|
||||
- you should be prepared to work together with the other
|
||||
potential contributors who want to work on the same models.
|
||||
|
||||
- you should be prepared to go through code reviews with the
|
||||
ns-3 development team prior to integration. The goal of these
|
||||
code reviews is to:
|
||||
- ensure adherence to the coding style of the project
|
||||
(see doc/codingstyle.html)
|
||||
- ensure that the structure of your code has a certain
|
||||
coherence with regard to the rest of the ns-3 codebase
|
||||
- improve the quality of the code: we strongly believe in
|
||||
the old saying: "many eyes make all bugs shallow".
|
||||
- increase code reuse by trying to generalize certain
|
||||
useful pieces of your code to make them available to
|
||||
other models.
|
||||
|
||||
- you should be prepared to try to integrate as many tests
|
||||
in the codebase as possible: we understand that writing
|
||||
tests is not sexy and that not everyone is convinced that
|
||||
they improve the code-writing poductivity which is why
|
||||
we do not enforce strict rules about testing. However,
|
||||
we expect model authors to answer basic questions about
|
||||
how they plan to test and validate their models.
|
||||
|
||||
- you should be prepared to maintain your model once it is
|
||||
integrated: while we consider every bug filed against the
|
||||
simulator as being a bug we must deal with and while we
|
||||
will try to fix as many bugs as humanly possible, we
|
||||
also expect model authors to act as responsible maintainers
|
||||
and be reactive to bug reports concerning their models.
|
||||
|
||||
- The project has decided upon GNU GPLv2 as the licensing structure.
|
||||
All simulation software in the ns-3 repositories will be GNU GPLv2
|
||||
or GNU GPLv2-compatible (with non-GPLv2 licensing reserved for
|
||||
ports of pre-existing code under a different license, such as BSD).
|
||||
You do not have to assign your copyright to the ns-3 project but
|
||||
you must accept the terms of the GPLv2 and attest that your
|
||||
contributions can be licensed under those terms. See the
|
||||
following link:
|
||||
http://www.fsf.org/licensing/licenses/info/GPLv2.html
|
||||
|
||||
@@ -1,393 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Process doxygen log to generate sorted list of top offenders.
|
||||
#
|
||||
|
||||
me=$(basename $0)
|
||||
DIR="$(dirname $0)"
|
||||
# Trick to get the absolute path, since doxygen prefixes errors that way
|
||||
ROOT=$(cd "$DIR/.."; pwd)
|
||||
|
||||
# Known log files
|
||||
STANDARDLOGFILE=doxygen.log
|
||||
WARNINGSLOGFILE=doxygen.warnings.log
|
||||
# Default choice: generate it
|
||||
LOG="$DIR/$WARNINGSLOGFILE"
|
||||
|
||||
# Verbose log
|
||||
VERBLOG="$DIR/doxygen.verbose.log"
|
||||
|
||||
|
||||
# Options ------------------------------
|
||||
#
|
||||
|
||||
function usage
|
||||
{
|
||||
cat <<-EOF
|
||||
|
||||
Usage: $me [-eithv] [-f <log-file> | -l | -s] [-m <module> | -F <regex>]
|
||||
|
||||
Run doxygen to generate all errors; report error counts
|
||||
by module and file.
|
||||
|
||||
-i Skip build and print-introspected-doxygen.
|
||||
|
||||
-f Skip doxygen run; use existing <log-file>.
|
||||
-s Skip doxygen run; use existing warnings log doc/$WARNINGSLOGFILE
|
||||
-l Skip doxygen run; use the normal doxygen log doc/$STANDARDLOGFILE
|
||||
|
||||
-e Filter out warnings from */examples/*
|
||||
-t Filter out warnings from */test/*
|
||||
-m Only include files matching src/<module>
|
||||
-F Only include files matching the <regex>
|
||||
|
||||
-v Show the doxygen run output
|
||||
-h Print this usage message
|
||||
|
||||
The default behavior is to modify doxygen.conf temporarily to
|
||||
report all undocumented elements, and to reduce the run time.
|
||||
The output of this special run is kept in doc/$WARNINGSLOGFILE.
|
||||
To further reduce the run time, the -i option also skips
|
||||
print-introspected-doxygen, so waf doesn\'t have to compile
|
||||
any modified files at all.
|
||||
|
||||
The -f, -l, and -s options skip the doxygen run altogether.
|
||||
The first two use a specified or the standard log file;
|
||||
the -s option uses the warnings log from a prior run.
|
||||
Only the first of -f <log-file>, -s, or -l will have effect.
|
||||
|
||||
The -e and -t options exclude examples and test directories
|
||||
from the counts. The -m option only includes a specific module.
|
||||
The -F option only includes files (or warnings) matching the <regex>.
|
||||
The -m and -F options append the relevant warnings after the
|
||||
numerical report. These can be used in any combination.
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Messaging ----------------------------
|
||||
#
|
||||
|
||||
# Arg -v Verbosity level
|
||||
verbosity=0
|
||||
|
||||
function verbose
|
||||
{
|
||||
if [ "$1" == "-n" ]; then
|
||||
echo -n "$2"
|
||||
elif [ $verbosity -eq 1 ]; then
|
||||
echo "$1 $2"
|
||||
else
|
||||
echo "$2"
|
||||
fi
|
||||
}
|
||||
|
||||
# Use file handle 6 for verbose output
|
||||
rm -f $VERBLOG
|
||||
exec 6>$VERBLOG
|
||||
|
||||
function status_report
|
||||
{
|
||||
local status="$1"
|
||||
local long_msg="$2"
|
||||
if [ $status -eq 0 ]; then
|
||||
verbose "$long_msg " "done."
|
||||
rm -f $VERBLOG
|
||||
else
|
||||
verbose "$long_msg " "FAILED. Details:"
|
||||
cat $VERBLOG
|
||||
rm -f $VERBLOG
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
# Argument processing ------------------
|
||||
#
|
||||
|
||||
# -f argument
|
||||
use_filearg=0
|
||||
logfile_arg=
|
||||
# -l
|
||||
use_standard=0
|
||||
# skip doxygen run; using existing log file
|
||||
skip_doxy=0
|
||||
# skip print-introspected-doxygen, avoiding a build
|
||||
skip_intro=0
|
||||
|
||||
# Filtering flags
|
||||
filter_examples=0
|
||||
filter_test=0
|
||||
filter_module=""
|
||||
filter_regex=""
|
||||
|
||||
echo
|
||||
echo "$me:"
|
||||
|
||||
while getopts :eitm:F:lF:svh option ; do
|
||||
|
||||
case $option in
|
||||
|
||||
(e) filter_examples=1 ;;
|
||||
|
||||
(i) skip_intro=1 ;;
|
||||
|
||||
(t) filter_test=1 ;;
|
||||
|
||||
(m) filter_module="$OPTARG" ;;
|
||||
|
||||
(F) filter_regex="$OPTARG" ;;
|
||||
|
||||
(l) use_standard=1 ;;
|
||||
|
||||
(f) use_filearg=1
|
||||
logfile_arg="$OPTARG"
|
||||
;;
|
||||
|
||||
(s) use_filearg=1
|
||||
logfile_arg="$DIR/$WARNINGSLOGFILE"
|
||||
;;
|
||||
|
||||
(v) verbosity=1
|
||||
exec 6>&1
|
||||
;;
|
||||
|
||||
(h) usage ;;
|
||||
|
||||
(:) echo "$me: Missing argument to -$OPTARG" ; usage ;;
|
||||
|
||||
(\?) echo "$me: Invalid option: -$OPTARG" ; usage ;;
|
||||
|
||||
esac
|
||||
done
|
||||
|
||||
function checklogfile
|
||||
{
|
||||
if [ -e "$1" ] ; then
|
||||
skip_doxy=1
|
||||
LOG="$1"
|
||||
else
|
||||
echo "$me: log file $1 does not exist."
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
# Which log file
|
||||
if [[ $use_filearg -eq 1 && "${logfile_arg:-}" != "" ]] ; then
|
||||
checklogfile "$logfile_arg"
|
||||
elif [ $use_standard -eq 1 ]; then
|
||||
checklogfile "$DIR/$STANDARDLOGFILE"
|
||||
fi
|
||||
|
||||
# Run doxygen -------------------------
|
||||
#
|
||||
|
||||
if [ $skip_doxy -eq 1 ]; then
|
||||
echo
|
||||
echo "Skipping doxygen run, using existing log file $LOG"
|
||||
else
|
||||
|
||||
if [ $skip_intro -eq 1 ]; then
|
||||
verbose "" "Skipping ./waf build and print-introspected-doxygen."
|
||||
else
|
||||
# Run introspection, which may require a build
|
||||
verbose -n "Building and running print-introspected-doxygen..."
|
||||
(cd "$ROOT" && ./waf --run print-introspected-doxygen >doc/introspected-doxygen.h >&6 2>&6 )
|
||||
status_report $? "./waf build"
|
||||
fi
|
||||
|
||||
# Modify doxygen.conf to generate all the warnings
|
||||
# (We also suppress dot graphs, so shorten the run time.)
|
||||
|
||||
conf=$DIR/doxygen.conf
|
||||
|
||||
sed -i.bak -E '/^EXTRACT_ALL |^HAVE_DOT |^WARNINGS /s/YES/no/' $conf
|
||||
|
||||
verbose -n "Rebuilding doxygen (v$(doxygen --version)) docs with full errors..."
|
||||
(cd "$ROOT" && ./waf --doxygen-no-build >&6 2>&6 )
|
||||
status=$?
|
||||
|
||||
rm -f $conf
|
||||
mv -f $conf.bak $conf
|
||||
|
||||
status_report $status "Doxygen run"
|
||||
|
||||
cp -f "$DIR/$STANDARDLOGFILE" "$DIR/$WARNINGSLOGFILE"
|
||||
|
||||
fi
|
||||
|
||||
# Log filters --------------------------
|
||||
#
|
||||
|
||||
# Filter regular expression for -m and -F
|
||||
filter_inRE=""
|
||||
if [ "$filter_module" != "" ] ; then
|
||||
filter_inRE="${filter_inRE:-}${filter_inRE:+\\|}src/$filter_module"
|
||||
fi
|
||||
if [ "$filter_regex" != "" ] ; then
|
||||
filter_inRE="${filter_inRE:-}${filter_inRE:+\\|}$filter_regex"
|
||||
fi
|
||||
|
||||
# Filter regular expression for -e and -t
|
||||
filter_outRE=""
|
||||
if [ $filter_examples -eq 1 ]; then
|
||||
filter_outRE="${filter_outRE:-}${filter_outRE:+\\|}/examples/"
|
||||
fi
|
||||
if [ $filter_test -eq 1 ]; then
|
||||
filter_outRE="${filter_outRE:-}${filter_outRE:+\\|}/test/"
|
||||
fi
|
||||
|
||||
if [ "${filter_inRE:-}" != "" ] ; then
|
||||
echo "Filtering in \"$filter_inRE\""
|
||||
fi
|
||||
if [ "${filter_outRE:-}" != "" ] ; then
|
||||
echo "Filtering out \"$filter_outRE\""
|
||||
fi
|
||||
echo
|
||||
|
||||
# Filter log file
|
||||
function filter_log
|
||||
{
|
||||
local flog;
|
||||
flog=$( cat "$LOG" | grep "^$ROOT" )
|
||||
|
||||
if [ "${filter_inRE:-}" != "" ] ; then
|
||||
flog=$( echo "$flog" | grep "$filter_inRE" )
|
||||
fi
|
||||
|
||||
if [ "${filter_outRE:-}" != "" ] ; then
|
||||
flog=$( echo "$flog" | grep -v "$filter_outRE" )
|
||||
fi
|
||||
|
||||
flog=$( \
|
||||
echo "$flog" | \
|
||||
sort -t ':' -k1,1 -k2,2n | \
|
||||
uniq \
|
||||
)
|
||||
|
||||
echo "$flog"
|
||||
}
|
||||
|
||||
# Analyze the log ----------------------
|
||||
#
|
||||
|
||||
# List of module directories (e.g, "src/core/model")
|
||||
undocmods=$( \
|
||||
filter_log | \
|
||||
cut -d ':' -f 1 | \
|
||||
sed "s|$ROOT/||g" | \
|
||||
cut -d '/' -f 1-3 | \
|
||||
sort | \
|
||||
uniq -c | \
|
||||
sort -nr \
|
||||
)
|
||||
|
||||
# Number of directories
|
||||
modcount=$( \
|
||||
echo "$undocmods" | \
|
||||
wc -l | \
|
||||
sed 's/^[ \t]*//;s/[ \t]*$//' \
|
||||
)
|
||||
|
||||
# For a function with multiple undocumented parameters,
|
||||
# Doxygen prints the additional parameters on separate lines,
|
||||
# so they don't show up in the totals above.
|
||||
# Rather than work too hard to get the exact number for each file,
|
||||
# we just list the total here.
|
||||
addlparam=$( \
|
||||
grep "^ parameter '" "$LOG" | \
|
||||
wc -l | \
|
||||
sed 's/^[ \t]*//;s/[ \t]*$//' \
|
||||
)
|
||||
|
||||
# Total number of warnings
|
||||
warncount=$( \
|
||||
echo "$undocmods" | \
|
||||
awk '{total += $1}; END {print total}' \
|
||||
)
|
||||
warncount=$((warncount + addlparam))
|
||||
|
||||
# List of files appearing in the log
|
||||
undocfiles=$( \
|
||||
filter_log | \
|
||||
cut -d ':' -f 1 | \
|
||||
sed "s|$ROOT||g" | \
|
||||
cut -d '/' -f 2- | \
|
||||
sort | \
|
||||
uniq -c | \
|
||||
sort -k 2 \
|
||||
)
|
||||
|
||||
# Sorted by number, decreasing
|
||||
undocsort=$(echo "$undocfiles" | sort -k1nr,2 )
|
||||
|
||||
# Total number of files
|
||||
filecount=$( \
|
||||
echo "$undocfiles" | \
|
||||
wc -l | \
|
||||
sed 's/^[ \t]*//;s/[ \t]*$//' \
|
||||
)
|
||||
|
||||
# Filtered in warnings
|
||||
filterin=
|
||||
if [ "${filter_inRE:-}" != "" ] ; then
|
||||
filterin=$( \
|
||||
filter_log | \
|
||||
sed "s|$ROOT/||g" \
|
||||
)
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Summarize the log --------------------
|
||||
#
|
||||
|
||||
echo
|
||||
echo "Report of Doxygen warnings"
|
||||
echo "----------------------------------------"
|
||||
echo
|
||||
echo "(All counts are lower bounds.)"
|
||||
echo
|
||||
echo "Warnings by module/directory:"
|
||||
echo
|
||||
echo "Count Directory"
|
||||
echo "----- ----------------------------------"
|
||||
echo "$undocmods"
|
||||
echo " $addlparam additional undocumented parameters."
|
||||
echo "----------------------------------------"
|
||||
printf "%6d total warnings\n" $warncount
|
||||
printf "%6d directories with warnings\n" $modcount
|
||||
echo
|
||||
echo
|
||||
echo "Warnings by file (alphabetical)"
|
||||
echo
|
||||
echo "Count File"
|
||||
echo "----- ----------------------------------"
|
||||
echo "$undocfiles"
|
||||
echo "----------------------------------------"
|
||||
printf "%6d files with warnings\n" $filecount
|
||||
echo
|
||||
echo
|
||||
echo "Warnings by file (numerical)"
|
||||
echo
|
||||
echo "Count File"
|
||||
echo "----- ----------------------------------"
|
||||
echo "$undocsort"
|
||||
echo "----------------------------------------"
|
||||
printf "%6d files with warnings\n" $filecount
|
||||
echo
|
||||
echo
|
||||
echo "Doxygen Warnings Summary"
|
||||
echo "----------------------------------------"
|
||||
printf "%6d directories\n" $modcount
|
||||
printf "%6d files\n" $filecount
|
||||
printf "%6d warnings\n" $warncount
|
||||
|
||||
if [ "$filterin" != "" ] ; then
|
||||
echo
|
||||
echo
|
||||
echo "Filtered Warnings"
|
||||
echo "========================================"
|
||||
echo "$filterin"
|
||||
fi
|
||||
@@ -1,97 +0,0 @@
|
||||
/**
|
||||
* \mainpage ns-3 Documentation
|
||||
*
|
||||
* \section intro-sec Introduction
|
||||
* <a href="http://www.nsnam.org/">ns-3</a> documentation is maintained using
|
||||
* <a href="http://www.doxygen.org">Doxygen</a>.
|
||||
* Doxygen is typically used for
|
||||
* API documentation, and organizes such documentation across different
|
||||
* modules. This project uses Doxygen for building the definitive
|
||||
* maintained API documentation. Additional ns-3 project documentation
|
||||
* can be found at the
|
||||
* <a href="http://www.nsnam.org/documentation/latest">project web site</a>.
|
||||
*
|
||||
* \section install-sec Building the Documentation
|
||||
*
|
||||
* ns-3 requires Doxygen version 1.8.3.1 or greater.
|
||||
*
|
||||
* Type "./waf --doxygen" or "./waf --doxygen-no-build" to build the
|
||||
* documentation. The doc/ directory contains
|
||||
* configuration for Doxygen (doxygen.conf) and main.h. The Doxygen
|
||||
* build process puts html files into the doc/html/ directory, and latex
|
||||
* filex into the doc/latex/ directory.
|
||||
*
|
||||
* \section module-sec Module overview
|
||||
*
|
||||
* The ns-3 library is split across many modules organized under the
|
||||
* <b><a href="modules.html">Modules</a></b> tab.
|
||||
* - aodv
|
||||
* - applications
|
||||
* - bridge
|
||||
* - click
|
||||
* - config-store
|
||||
* - core
|
||||
* - csma
|
||||
* - csma-layout
|
||||
* - dsdv
|
||||
* - emu
|
||||
* - energy
|
||||
* - flow-monitor
|
||||
* - internet
|
||||
* - lte
|
||||
* - mesh
|
||||
* - mobility
|
||||
* - mpi
|
||||
* - netanim
|
||||
* - network
|
||||
* - nix-vector-routing
|
||||
* - ns3tcp
|
||||
* - ns3wifi
|
||||
* - olsr
|
||||
* - openflow
|
||||
* - point-to-point
|
||||
* - point-to-point-layout
|
||||
* - propagation
|
||||
* - spectrum
|
||||
* - stats
|
||||
* - tap-bridge
|
||||
* - test
|
||||
* - topology-read
|
||||
* - uan
|
||||
* - virtual-net-device
|
||||
* - visualizer
|
||||
* - wifi
|
||||
* - wimax
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* \namespace ns3
|
||||
* \brief Every class exported by the ns3 library is enclosed in the
|
||||
* ns3 namespace.
|
||||
*/
|
||||
|
||||
// Macros defined by the build system.
|
||||
//
|
||||
// These have to be visible for doxygen to document them,
|
||||
// so we put them here in a file only seen by doxygen, not the compiler.
|
||||
/**
|
||||
* \ingroup assert
|
||||
*
|
||||
* \def NS3_ASSERT_ENABLE
|
||||
*
|
||||
* Enable asserts at compile time.
|
||||
*
|
||||
* This is normally set by `./waf configure --build-profile=debug`.
|
||||
*/
|
||||
#define NS3_ASSERT_ENABLE
|
||||
|
||||
/**
|
||||
* \ingroup logging
|
||||
*
|
||||
* \def NS3_LOG_ENABLE
|
||||
*
|
||||
* Enable logging at compile time.
|
||||
*
|
||||
* This is normally set by `./waf configure --build-profile=debug`.
|
||||
*/
|
||||
#define NS3_LOG_ENABLE
|
||||
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* \mainpage An Introduction to ns-3
|
||||
*
|
||||
* The ns-3 library is split across multiple modules:
|
||||
* - core: located in src/core and contains a number of facilities which
|
||||
* do not depend on any other module. Some of these facilities are
|
||||
* OS-dependent.
|
||||
* - simulator: located in src/simulator and contains event scheduling
|
||||
* facilities.
|
||||
* - common: located in src/common and contains facilities specific
|
||||
* to network simulations but shared by pretty much every model
|
||||
* of a network component.
|
||||
* - node: located in src/node. Defines the abstract interfaces which
|
||||
* must be implemented by every node and more specifically, by ipv4 nodes.
|
||||
* - devices: located in src/devices. Contains a set of MAC-level models
|
||||
*
|
||||
* The "core" module contains:
|
||||
* - a Functor class: ns3::Callback
|
||||
* - an os-independent interface to get access to the elapsed wall clock time: ns3::SystemWallClockMs
|
||||
* - a class to register regression tests with the test manager: ns3::Test and ns3::TestManager
|
||||
* - debugging facilities: \ref debugging, \ref assert, \ref error
|
||||
* - \ref randomvariable
|
||||
* - \ref config
|
||||
* - a base class for objects which need to support reference counting
|
||||
* and QueryInterface: ns3::Object and ns3::InterfaceId
|
||||
* - a ns3::ComponentManager which can be used to manage the creation
|
||||
* of any object which derives from ns3::Object through an ns3::ClassId
|
||||
* - a smart-pointer class ns3::Ptr designed to work together with ns3::Object
|
||||
*
|
||||
* The "simulator" module contains:
|
||||
* - a time management class to hold a time and convert between various time units: ns3::Time
|
||||
* - a scheduler base class used to implement new simulation event schedulers:
|
||||
* ns3::Scheduler and ns3::SchedulerFactory
|
||||
* - a simulator class used to create, schedule and cancel events: ns3::Simulator
|
||||
*
|
||||
* The "common" module contains:
|
||||
* - a packet class to create and manipulate simulation packets: ns3::Packet, ns3::Header,
|
||||
* and ns3::Trailer. This packet class also supports per-packet ns3::Tag which are
|
||||
* globs of data which can be attached to any packet.
|
||||
* - a set of low-level trace facilities: \ref lowleveltracing
|
||||
*
|
||||
* The "node" module contains:
|
||||
* - a ns3::Node base class which should be subclassed by any new type of
|
||||
* network Node.
|
||||
* - models which abstract the MAC-layer from the IP layer protocols:
|
||||
* ns3::NetDevice and ns3::Channel.
|
||||
* - models which abstract the application-layer API: ns3::Application,
|
||||
* ns3::Socket, ns3::SocketFactory, and, ns3::Udp
|
||||
*
|
||||
* The "internet-node" module contains a set of classes which implement the
|
||||
* APIs defined in the "node" module:
|
||||
* - an Ipv4/Udp stack with socket support
|
||||
* - an ARP module
|
||||
* - an InternetNode class which is a Node subclass.
|
||||
*
|
||||
* The "devices" module contains:
|
||||
* - a PointToPoint MAC device: ns3::PointToPointNetDevice, ns3::PointToPointChannel,
|
||||
* and ns3::PointToPointTopology.
|
||||
*/
|
||||
/**
|
||||
* \namespace ns3
|
||||
* \brief Every class exported by the ns3 library is enclosed in the
|
||||
* ns3 namespace.
|
||||
*/
|
||||
/**
|
||||
* \defgroup constants Constants
|
||||
* \brief Constants you can change
|
||||
*/
|
||||
@@ -1,254 +0,0 @@
|
||||
EPSTOPDF = epstopdf
|
||||
DIA = dia
|
||||
CONVERT = convert
|
||||
|
||||
SRC = ../../src
|
||||
# Temporary source directory, for build
|
||||
SOURCETEMP = source-temp
|
||||
FIGURES = $(SOURCETEMP)/figures
|
||||
#VPATH = $(FIGURES)
|
||||
|
||||
# list all manual .rst files that need to be copied to $SOURCETEMP
|
||||
SOURCES = \
|
||||
source/conf.py \
|
||||
source/_static \
|
||||
source/index.rst \
|
||||
source/replace.txt \
|
||||
source/attributes.rst \
|
||||
source/callbacks.rst \
|
||||
source/documentation.rst \
|
||||
source/enable-modules.rst \
|
||||
source/enable-tests.rst \
|
||||
source/events.rst \
|
||||
source/gnuplot.rst \
|
||||
source/hash-functions.rst \
|
||||
source/helpers.rst \
|
||||
source/how-to-write-tests.rst \
|
||||
source/logging.rst \
|
||||
source/new-models.rst \
|
||||
source/new-modules.rst \
|
||||
source/object-model.rst \
|
||||
source/object-names.rst \
|
||||
source/organization.rst \
|
||||
source/python.rst \
|
||||
source/random-variables.rst \
|
||||
source/realtime.rst \
|
||||
source/support.rst \
|
||||
source/test-background.rst \
|
||||
source/test-framework.rst \
|
||||
source/test-overview.rst \
|
||||
source/tests.rst \
|
||||
source/tracing.rst \
|
||||
source/troubleshoot.rst \
|
||||
${SRC}/stats/doc/data-collection.rst \
|
||||
${SRC}/stats/doc/data-collection-overview.rst \
|
||||
${SRC}/stats/doc/statistics.rst \
|
||||
${SRC}/stats/doc/data-collection-helpers.rst \
|
||||
${SRC}/stats/doc/probe.rst \
|
||||
${SRC}/stats/doc/collector.rst \
|
||||
${SRC}/stats/doc/aggregator.rst \
|
||||
${SRC}/stats/doc/adaptor.rst \
|
||||
${SRC}/stats/doc/scope-and-limitations.rst \
|
||||
|
||||
# list all manual figure files that need to be copied to
|
||||
# $SOURCETEMP/figures. For each figure to be included in all
|
||||
# documentation formats (html, latex...) the following formats are supported:
|
||||
# 1) a single .dia file (preferred option, because it can be edited)
|
||||
# 2) a single .eps file
|
||||
# 3) both a .pdf and .png file
|
||||
|
||||
SOURCEFIGS = \
|
||||
figures/software-organization.dia \
|
||||
figures/plot-2d.png \
|
||||
figures/plot-2d-with-error-bars.png \
|
||||
figures/plot-3d.png \
|
||||
${SRC}/stats/doc/Stat-framework-arch.png \
|
||||
${SRC}/stats/doc/Wifi-default.png \
|
||||
${SRC}/stats/doc/dcf-overview.dia \
|
||||
${SRC}/stats/doc/dcf-overview-with-aggregation.dia \
|
||||
${SRC}/stats/doc/gnuplot-example.png \
|
||||
${SRC}/stats/doc/file-example.png \
|
||||
${SRC}/stats/doc/seventh-packet-byte-count.png \
|
||||
${SRC}/stats/doc/gnuplot-helper-example.png \
|
||||
${SRC}/stats/doc/gnuplot-aggregator.png \
|
||||
|
||||
# specify figures from which .png and .pdf figures need to be
|
||||
# generated (all dia and eps figures)
|
||||
IMAGES_EPS = \
|
||||
$(FIGURES)/software-organization.eps \
|
||||
$(FIGURES)/dcf-overview.eps \
|
||||
$(FIGURES)/dcf-overview-with-aggregation.eps \
|
||||
|
||||
# rescale pdf figures as necessary
|
||||
$(FIGURES)/software-organization.pdf_width = 5in
|
||||
|
||||
IMAGES_PNG = $(IMAGES_EPS:.eps=.png)
|
||||
|
||||
IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
|
||||
|
||||
IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
|
||||
|
||||
RESCALE = ../../utils/rescale-pdf.sh
|
||||
|
||||
%.eps : %.dia
|
||||
@echo dia $(notdir $<)
|
||||
@$(DIA) -t eps $< -e $@ >/dev/null
|
||||
|
||||
%.png : %.dia
|
||||
@echo dia $(notdir $<)
|
||||
@$(DIA) -t png $< -e $@ >/dev/null
|
||||
|
||||
%.png : %.eps
|
||||
@echo convert $(notdir $<)
|
||||
@$(CONVERT) $< $@ >/dev/null
|
||||
|
||||
%.pdf : %.eps
|
||||
@echo epstopdf $(notdir $<)
|
||||
@$(EPSTOPDF) $< -o=$@ >/dev/null
|
||||
@if test x$($@_width) != x; then $(RESCALE) $($@_width) $@ ; fi
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCETEMP)
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
.NOTPARALLEL:
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
copy-sources: $(SOURCES)
|
||||
@rm -rf $(SOURCETEMP)
|
||||
@mkdir -p $(SOURCETEMP)
|
||||
@mkdir -p $(FIGURES)
|
||||
@cp -r $(SOURCES) $(SOURCETEMP)
|
||||
@cp -r $(SOURCEFIGS) $(FIGURES)
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)
|
||||
-rm -rf $(SOURCETEMP)
|
||||
|
||||
frag: pickle
|
||||
@if test ! -d $(BUILDDIR)/frag; then mkdir $(BUILDDIR)/frag; fi
|
||||
pushd $(BUILDDIR)/frag && ../../pickle-to-xml.py ../pickle/index.fpickle > navigation.xml && popd
|
||||
cp -r $(BUILDDIR)/pickle/_images $(BUILDDIR)/frag
|
||||
|
||||
html: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ns-3.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ns-3.qhc"
|
||||
|
||||
devhelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/ns-3"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ns-3"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
changes: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
|
||||
test: $(IMAGES)
|
||||
@@ -1,18 +0,0 @@
|
||||
Please write image files in a vector graphics format, when possible, and
|
||||
generate the .png and .pdf versions on the fly (see ../Makefile).
|
||||
|
||||
The currently supported tool is dia. xfig could be added similarly
|
||||
if someone wants to add it. The main requirement for adding another format
|
||||
is that the tool to edit it is freely available and that a cron script can
|
||||
autogenerate the pdf and png from the figure source. Tgif (.obj) files
|
||||
were once used but the file conversions require a valid X display to
|
||||
be running, and are therefore to be avoided since our code server
|
||||
does not run such a server. Tgif pdf conversions were also cumbersome.
|
||||
|
||||
Store the .dia versions in mercurial, but not the .png or .pdfs.
|
||||
If the figure is not available in a vector graphics format, store both
|
||||
a .png and a .pdf version in this directory.
|
||||
|
||||
If you add a source (.dia) file here, remember to add it to
|
||||
the list of figure sources in the Makefile in the directory above
|
||||
|
||||
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 8.8 KiB |
@@ -1,610 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Callbacks
|
||||
---------
|
||||
|
||||
Some new users to |ns3| are unfamiliar with an extensively used programming
|
||||
idiom used throughout the code: the *ns-3 callback*. This chapter provides some
|
||||
motivation on the callback, guidance on how to use it, and details on its
|
||||
implementation.
|
||||
|
||||
Callbacks Motivation
|
||||
********************
|
||||
|
||||
Consider that you have two simulation models A and B, and you wish to have them
|
||||
pass information between them during the simulation. One way that you can do
|
||||
that is that you can make A and B each explicitly knowledgeable about the other,
|
||||
so that they can invoke methods on each other::
|
||||
|
||||
class A {
|
||||
public:
|
||||
void ReceiveInput ( // parameters );
|
||||
...
|
||||
}
|
||||
|
||||
(in another source file:)
|
||||
|
||||
class B {
|
||||
public:
|
||||
void DoSomething (void);
|
||||
...
|
||||
|
||||
private:
|
||||
A* a_instance; // pointer to an A
|
||||
}
|
||||
|
||||
void
|
||||
B::DoSomething()
|
||||
{
|
||||
// Tell a_instance that something happened
|
||||
a_instance->ReceiveInput ( // parameters);
|
||||
...
|
||||
}
|
||||
|
||||
This certainly works, but it has the drawback that it introduces a dependency on
|
||||
A and B to know about the other at compile time (this makes it harder to have
|
||||
independent compilation units in the simulator) and is not generalized; if in a
|
||||
later usage scenario, B needs to talk to a completely different C object, the
|
||||
source code for B needs to be changed to add a ``c_instance`` and so forth. It
|
||||
is easy to see that this is a brute force mechanism of communication that can
|
||||
lead to programming cruft in the models.
|
||||
|
||||
This is not to say that objects should not know about one another if there is a
|
||||
hard dependency between them, but that often the model can be made more flexible
|
||||
if its interactions are less constrained at compile time.
|
||||
|
||||
This is not an abstract problem for network simulation research, but rather it
|
||||
has been a source of problems in previous simulators, when researchers want to
|
||||
extend or modify the system to do different things (as they are apt to do in
|
||||
research). Consider, for example, a user who wants to add an IPsec security
|
||||
protocol sublayer between TCP and IP::
|
||||
|
||||
------------ -----------
|
||||
| TCP | | TCP |
|
||||
------------ -----------
|
||||
| becomes -> |
|
||||
----------- -----------
|
||||
| IP | | IPsec |
|
||||
----------- -----------
|
||||
|
|
||||
-----------
|
||||
| IP |
|
||||
-----------
|
||||
|
||||
|
||||
If the simulator has made assumptions, and hard coded into the code, that IP
|
||||
always talks to a transport protocol above, the user may be forced to hack the
|
||||
system to get the desired interconnections. This is clearly not an optimal way
|
||||
to design a generic simulator.
|
||||
|
||||
Callbacks Background
|
||||
********************
|
||||
|
||||
.. note:: Readers familiar with programming callbacks may skip this tutorial
|
||||
section.
|
||||
|
||||
The basic mechanism that allows one to address the problem above is known as a
|
||||
*callback*. The ultimate goal is to allow one piece of code to call a function
|
||||
(or method in C++) without any specific inter-module dependency.
|
||||
|
||||
This ultimately means you need some kind of indirection -- you treat the address
|
||||
of the called function as a variable. This variable is called a
|
||||
pointer-to-function variable. The relationship between function and
|
||||
pointer-to-function pointer is really no different that that of object and
|
||||
pointer-to-object.
|
||||
|
||||
In C the canonical example of a pointer-to-function is a
|
||||
pointer-to-function-returning-integer (PFI). For a PFI taking one int parameter,
|
||||
this could be declared like,::
|
||||
|
||||
int (*pfi)(int arg) = 0;
|
||||
|
||||
What you get from this is a variable named simply ``pfi`` that is initialized to
|
||||
the value 0. If you want to initialize this pointer to something meaningful, you
|
||||
have to have a function with a matching signature. In this case::
|
||||
|
||||
int MyFunction (int arg) {}
|
||||
|
||||
If you have this target, you can initialize the variable to point to your
|
||||
function like::
|
||||
|
||||
pfi = MyFunction;
|
||||
|
||||
You can then call MyFunction indirectly using the more suggestive form of the
|
||||
call::
|
||||
|
||||
int result = (*pfi) (1234);
|
||||
|
||||
This is suggestive since it looks like you are dereferencing the function
|
||||
pointer just like you would dereference any pointer. Typically, however, people
|
||||
take advantage of the fact that the compiler knows what is going on and will
|
||||
just use a shorter form::
|
||||
|
||||
int result = pfi (1234);
|
||||
|
||||
Notice that the function pointer obeys value semantics, so you can pass it
|
||||
around like any other value. Typically, when you use an asynchronous interface
|
||||
you will pass some entity like this to a function which will perform an action
|
||||
and *call back* to let you know it completed. It calls back by following the
|
||||
indirection and executing the provided function.
|
||||
|
||||
In C++ you have the added complexity of objects. The analogy with the PFI above
|
||||
means you have a pointer to a member function returning an int (PMI) instead of
|
||||
the pointer to function returning an int (PFI).
|
||||
|
||||
The declaration of the variable providing the indirection looks only slightly
|
||||
different::
|
||||
|
||||
int (MyClass::*pmi) (int arg) = 0;
|
||||
|
||||
This declares a variable named ``pmi`` just as the previous example declared a
|
||||
variable named ``pfi``. Since the will be to call a method of an instance of a
|
||||
particular class, one must declare that method in a class::
|
||||
|
||||
class MyClass {
|
||||
public:
|
||||
int MyMethod (int arg);
|
||||
};
|
||||
|
||||
Given this class declaration, one would then initialize that variable like
|
||||
this::
|
||||
|
||||
pmi = &MyClass::MyMethod;
|
||||
|
||||
This assigns the address of the code implementing the method to the variable,
|
||||
completing the indirection. In order to call a method, the code needs a ``this``
|
||||
pointer. This, in turn, means there must be an object of MyClass to refer to. A
|
||||
simplistic example of this is just calling a method indirectly (think virtual
|
||||
function)::
|
||||
|
||||
int (MyClass::*pmi) (int arg) = 0; // Declare a PMI
|
||||
pmi = &MyClass::MyMethod; // Point at the implementation code
|
||||
|
||||
MyClass myClass; // Need an instance of the class
|
||||
(myClass.*pmi) (1234); // Call the method with an object ptr
|
||||
|
||||
Just like in the C example, you can use this in an asynchronous call to another
|
||||
module which will *call back* using a method and an object pointer. The
|
||||
straightforward extension one might consider is to pass a pointer to the object
|
||||
and the PMI variable. The module would just do::
|
||||
|
||||
(*objectPtr.*pmi) (1234);
|
||||
|
||||
to execute the callback on the desired object.
|
||||
|
||||
One might ask at this time, *what's the point*? The called module will have to
|
||||
understand the concrete type of the calling object in order to properly make the
|
||||
callback. Why not just accept this, pass the correctly typed object pointer and
|
||||
do ``object->Method(1234)`` in the code instead of the callback? This is
|
||||
precisely the problem described above. What is needed is a way to decouple the
|
||||
calling function from the called class completely. This requirement led to the
|
||||
development of the *Functor*.
|
||||
|
||||
A functor is the outgrowth of something invented in the 1960s called a closure.
|
||||
It is basically just a packaged-up function call, possibly with some state.
|
||||
|
||||
A functor has two parts, a specific part and a generic part, related through
|
||||
inheritance. The calling code (the code that executes the callback) will execute
|
||||
a generic overloaded ``operator ()`` of a generic functor to cause the callback
|
||||
to be called. The called code (the code that wants to be called back) will have
|
||||
to provide a specialized implementation of the ``operator ()`` that performs the
|
||||
class-specific work that caused the close-coupling problem above.
|
||||
|
||||
With the specific functor and its overloaded ``operator ()`` created, the called
|
||||
code then gives the specialized code to the module that will execute the
|
||||
callback (the calling code).
|
||||
|
||||
The calling code will take a generic functor as a parameter, so an implicit cast
|
||||
is done in the function call to convert the specific functor to a generic
|
||||
functor. This means that the calling module just needs to understand the
|
||||
generic functor type. It is decoupled from the calling code completely.
|
||||
|
||||
The information one needs to make a specific functor is the object pointer and
|
||||
the pointer-to-method address.
|
||||
|
||||
The essence of what needs to happen is that the system declares a generic part
|
||||
of the functor::
|
||||
|
||||
template <typename T>
|
||||
class Functor
|
||||
{
|
||||
public:
|
||||
virtual int operator() (T arg) = 0;
|
||||
};
|
||||
|
||||
The caller defines a specific part of the functor that really is just there to
|
||||
implement the specific ``operator()`` method::
|
||||
|
||||
template <typename T, typename ARG>
|
||||
class SpecificFunctor : public Functor<ARG>
|
||||
{
|
||||
public:
|
||||
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg))
|
||||
{
|
||||
m_p = p;
|
||||
m_pmi = _pmi;
|
||||
}
|
||||
|
||||
virtual int operator() (ARG arg)
|
||||
{
|
||||
(*m_p.*m_pmi)(arg);
|
||||
}
|
||||
private:
|
||||
int (T::*m_pmi)(ARG arg);
|
||||
T* m_p;
|
||||
};
|
||||
|
||||
Here is an example of the usage::
|
||||
|
||||
class A
|
||||
{
|
||||
public:
|
||||
A (int a0) : a (a0) {}
|
||||
int Hello (int b0)
|
||||
{
|
||||
std::cout << "Hello from A, a = " << a << " b0 = " << b0 << std::endl;
|
||||
}
|
||||
int a;
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
A a(10);
|
||||
SpecificFunctor<A, int> sf(&a, &A::Hello);
|
||||
sf(5);
|
||||
}
|
||||
|
||||
.. note:: The previous code is not real ns-3 code. It is simplistic example
|
||||
code used only to illustrate the concepts involved and to help you understand
|
||||
the system more. Do not expect to find this code anywhere in the ns-3 tree.
|
||||
|
||||
Notice that there are two variables defined in the class above. The m_p
|
||||
variable is the object pointer and m_pmi is the variable containing the
|
||||
address of the function to execute.
|
||||
|
||||
Notice that when ``operator()`` is called, it in turn calls the method provided
|
||||
with the object pointer using the C++ PMI syntax.
|
||||
|
||||
To use this, one could then declare some model code that takes a generic functor
|
||||
as a parameter::
|
||||
|
||||
void LibraryFunction (Functor functor);
|
||||
|
||||
The code that will talk to the model would build a specific functor and pass it to ``LibraryFunction``::
|
||||
|
||||
MyClass myClass;
|
||||
SpecificFunctor<MyClass, int> functor (&myclass, MyClass::MyMethod);
|
||||
|
||||
When ``LibraryFunction`` is done, it executes the callback using the
|
||||
``operator()`` on the generic functor it was passed, and in this particular
|
||||
case, provides the integer argument::
|
||||
|
||||
void
|
||||
LibraryFunction (Functor functor)
|
||||
{
|
||||
// Execute the library function
|
||||
functor(1234);
|
||||
}
|
||||
|
||||
Notice that ``LibraryFunction`` is completely decoupled from the specific
|
||||
type of the client. The connection is made through the Functor polymorphism.
|
||||
|
||||
The Callback API in |ns3| implements object-oriented callbacks using
|
||||
the functor mechanism. This callback API, being based on C++ templates, is
|
||||
type-safe; that is, it performs static type checks to enforce proper signature
|
||||
compatibility between callers and callees. It is therefore more type-safe to
|
||||
use than traditional function pointers, but the syntax may look imposing at
|
||||
first. This section is designed to walk you through the Callback system so
|
||||
that you can be comfortable using it in |ns3|.
|
||||
|
||||
Using the Callback API
|
||||
**********************
|
||||
|
||||
The Callback API is fairly minimal, providing only two services:
|
||||
|
||||
1. callback type declaration: a way to declare a type of callback
|
||||
with a given signature, and,
|
||||
|
||||
2. callback instantiation: a way to instantiate a
|
||||
template-generated forwarding callback which can forward any calls
|
||||
to another C++ class member method or C++ function.
|
||||
|
||||
This is best observed via walking through an example, based on
|
||||
``samples/main-callback.cc``.
|
||||
|
||||
Using the Callback API with static functions
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Consider a function::
|
||||
|
||||
static double
|
||||
CbOne (double a, double b)
|
||||
{
|
||||
std::cout << "invoke cbOne a=" << a << ", b=" << b << std::endl;
|
||||
return a;
|
||||
}
|
||||
|
||||
Consider also the following main program snippet::
|
||||
|
||||
int main (int argc, char *argv[])
|
||||
{
|
||||
// return type: double
|
||||
// first arg type: double
|
||||
// second arg type: double
|
||||
Callback<double, double, double> one;
|
||||
}
|
||||
|
||||
This is an example of a C-style callback -- one which does not include or need
|
||||
a ``this`` pointer. The function template ``Callback`` is essentially the
|
||||
declaration of the variable containing the pointer-to-function. In the example
|
||||
above, we explicitly showed a pointer to a function that returned an integer and
|
||||
took a single integer as a parameter, The ``Callback`` template function is
|
||||
a generic version of that -- it is used to declare the type of a callback.
|
||||
|
||||
.. note:: Readers unfamiliar with C++ templates may consult `<http://www.cplusplus.com/doc/tutorial/templates/>`_.
|
||||
|
||||
The ``Callback`` template requires one mandatory argument (the return type
|
||||
of the function to be assigned to this callback) and up to five optional
|
||||
arguments, which each specify the type of the arguments (if your particular
|
||||
callback function has more than five arguments, then this can be handled
|
||||
by extending the callback implementation).
|
||||
|
||||
So in the above example, we have a declared a callback named "one" that will
|
||||
eventually hold a function pointer. The signature of the function that it will
|
||||
hold must return double and must support two double arguments. If one tries
|
||||
to pass a function whose signature does not match the declared callback,
|
||||
a compilation error will occur. Also, if one tries to assign to a callback
|
||||
an incompatible one, compilation will succeed but a run-time
|
||||
NS_FATAL_ERROR will be raised. The sample program
|
||||
``src/core/examples/main-callback.cc`` demonstrates both of these error cases
|
||||
at the end of the ``main()`` program.
|
||||
|
||||
Now, we need to tie together this callback instance and the actual target function
|
||||
(CbOne). Notice above that CbOne has the same function signature types as the
|
||||
callback-- this is important. We can pass in any such properly-typed function
|
||||
to this callback. Let's look at this more closely::
|
||||
|
||||
static double CbOne (double a, double b) {}
|
||||
^ ^ ^
|
||||
| | |
|
||||
| | |
|
||||
Callback<double, double, double> one;
|
||||
|
||||
You can only bind a function to a callback if they have the matching signature.
|
||||
The first template argument is the return type, and the additional template
|
||||
arguments are the types of the arguments of the function signature.
|
||||
|
||||
Now, let's bind our callback "one" to the function that matches its signature::
|
||||
|
||||
// build callback instance which points to cbOne function
|
||||
one = MakeCallback (&CbOne);
|
||||
|
||||
This call to ``MakeCallback`` is, in essence, creating one of the specialized
|
||||
functors mentioned above. The variable declared using the ``Callback``
|
||||
template function is going to be playing the part of the generic functor. The
|
||||
assignment ``one = MakeCallback (&CbOne)`` is the cast that converts the
|
||||
specialized functor known to the callee to a generic functor known to the caller.
|
||||
|
||||
Then, later in the program, if the callback is needed, it can be used as follows::
|
||||
|
||||
NS_ASSERT (!one.IsNull ());
|
||||
|
||||
// invoke cbOne function through callback instance
|
||||
double retOne;
|
||||
retOne = one (10.0, 20.0);
|
||||
|
||||
The check for ``IsNull()`` ensures that the callback is not null -- that there
|
||||
is a function to call behind this callback. Then, ``one()`` executes the
|
||||
generic ``operator()`` which is really overloaded with a specific implementation
|
||||
of ``operator()`` and returns the same result as if ``CbOne()`` had been
|
||||
called directly.
|
||||
|
||||
Using the Callback API with member functions
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Generally, you will not be calling static functions but instead public member
|
||||
functions of an object. In this case, an extra argument is needed to the
|
||||
MakeCallback function, to tell the system on which object the function should be
|
||||
invoked. Consider this example, also from main-callback.cc::
|
||||
|
||||
class MyCb {
|
||||
public:
|
||||
int CbTwo (double a) {
|
||||
std::cout << "invoke cbTwo a=" << a << std::endl;
|
||||
return -5;
|
||||
}
|
||||
};
|
||||
|
||||
int main ()
|
||||
{
|
||||
...
|
||||
// return type: int
|
||||
// first arg type: double
|
||||
Callback<int, double> two;
|
||||
MyCb cb;
|
||||
// build callback instance which points to MyCb::cbTwo
|
||||
two = MakeCallback (&MyCb::CbTwo, &cb);
|
||||
...
|
||||
}
|
||||
|
||||
Here, we pass an additional object pointer to the ``MakeCallback<>`` function.
|
||||
Recall from the background section above that ``Operator()`` will use the pointer to
|
||||
member syntax when it executes on an object::
|
||||
|
||||
virtual int operator() (ARG arg)
|
||||
{
|
||||
(*m_p.*m_pmi)(arg);
|
||||
}
|
||||
|
||||
And so we needed to provide the two variables (``m_p`` and ``m_pmi``) when
|
||||
we made the specific functor. The line::
|
||||
|
||||
two = MakeCallback (&MyCb::CbTwo, &cb);
|
||||
|
||||
does precisely that. In this case, when ``two ()`` is invoked::
|
||||
|
||||
int result = two (1.0);
|
||||
|
||||
will result in a call tothe ``CbTwo`` member function (method) on the object
|
||||
pointed to by ``&cb``.
|
||||
|
||||
Building Null Callbacks
|
||||
+++++++++++++++++++++++
|
||||
|
||||
It is possible for callbacks to be null; hence it may be wise to
|
||||
check before using them. There is a special construct for a null
|
||||
callback, which is preferable to simply passing "0" as an argument;
|
||||
it is the ``MakeNullCallback<>`` construct::
|
||||
|
||||
two = MakeNullCallback<int, double> ();
|
||||
NS_ASSERT (two.IsNull ());
|
||||
|
||||
Invoking a null callback is just like invoking a null function pointer: it will
|
||||
crash at runtime.
|
||||
|
||||
Bound Callbacks
|
||||
***************
|
||||
|
||||
A very useful extension to the functor concept is that of a Bound Callback.
|
||||
Previously it was mentioned that closures were originally function calls
|
||||
packaged up for later execution. Notice that in all of the Callback
|
||||
descriptions above, there is no way to package up any parameters for use
|
||||
later -- when the ``Callback`` is called via ``operator()``. All of
|
||||
the parameters are provided by the calling function.
|
||||
|
||||
What if it is desired to allow the client function (the one that provides the
|
||||
callback) to provide some of the parameters? `Alexandrescu <http://erdani.com/book/main.html>`_ calls the process of
|
||||
allowing a client to specify one of the parameters *"binding"*. One of the
|
||||
parameters of ``operator()`` has been bound (fixed) by the client.
|
||||
|
||||
Some of our pcap tracing code provides a nice example of this. There is a
|
||||
function that needs to be called whenever a packet is received. This function
|
||||
calls an object that actually writes the packet to disk in the pcap file
|
||||
format. The signature of one of these functions will be::
|
||||
|
||||
static void DefaultSink (Ptr<PcapFileWrapper> file, Ptr<const Packet> p);
|
||||
|
||||
The static keyword means this is a static function which does not need a
|
||||
``this`` pointer, so it will be using C-style callbacks. We don't want the
|
||||
calling code to have to know about anything but the Packet. What we want in
|
||||
the calling code is just a call that looks like::
|
||||
|
||||
m_promiscSnifferTrace (m_currentPkt);
|
||||
|
||||
What we want to do is to *bind* the ``Ptr<PcapFileWriter> file`` to the
|
||||
specific callback implementation when it is created and arrange for the
|
||||
``operator()`` of the Callback to provide that parameter for free.
|
||||
|
||||
We provide the ``MakeBoundCallback`` template function for that purpose. It
|
||||
takes the same parameters as the ``MakeCallback`` template function but also
|
||||
takes the parameters to be bound. In the case of the example above::
|
||||
|
||||
MakeBoundCallback (&DefaultSink, file);
|
||||
|
||||
will create a specific callback implementation that knows to add in the extra
|
||||
bound arguments. Conceptually, it extends the specific functor described above
|
||||
with one or more bound arguments::
|
||||
|
||||
template <typename T, typename ARG, typename BOUND_ARG>
|
||||
class SpecificFunctor : public Functor
|
||||
{
|
||||
public:
|
||||
SpecificFunctor(T* p, int (T::*_pmi)(ARG arg), BOUND_ARG boundArg)
|
||||
{
|
||||
m_p = p;
|
||||
m_pmi = pmi;
|
||||
m_boundArg = boundArg;
|
||||
}
|
||||
|
||||
virtual int operator() (ARG arg)
|
||||
{
|
||||
(*m_p.*m_pmi)(m_boundArg, arg);
|
||||
}
|
||||
private:
|
||||
void (T::*m_pmi)(ARG arg);
|
||||
T* m_p;
|
||||
BOUND_ARG m_boundArg;
|
||||
};
|
||||
|
||||
You can see that when the specific functor is created, the bound argument is saved
|
||||
in the functor / callback object itself. When the ``operator()`` is invoked with
|
||||
the single parameter, as in::
|
||||
|
||||
m_promiscSnifferTrace (m_currentPkt);
|
||||
|
||||
the implementation of ``operator()`` adds the bound parameter into the actual
|
||||
function call::
|
||||
|
||||
(*m_p.*m_pmi)(m_boundArg, arg);
|
||||
|
||||
It's possible to bind two or three arguments as well. Say we have a function with
|
||||
signature::
|
||||
|
||||
static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
|
||||
|
||||
One can create bound callback binding first two arguments like::
|
||||
|
||||
MakeBoundCallback (&NotifyEvent, a1, b1);
|
||||
|
||||
assuming `a1` and `b1` are objects of type `A` and `B` respectively. Similarly for
|
||||
three arguments one would have function with a signature::
|
||||
|
||||
static void NotifyEvent (Ptr<A> a, Ptr<B> b, MyEventType e);
|
||||
|
||||
Binding three arguments in done with::
|
||||
|
||||
MakeBoundCallback (&NotifyEvent, a1, b1, c1);
|
||||
|
||||
again assuming `a1`, `b1` and `c1` are objects of type `A`, `B` and `C` respectively.
|
||||
|
||||
This kind of binding can be used for exchanging information between objects in
|
||||
simulation; specifically, bound callbacks can be used as traced callbacks, which will
|
||||
be described in the next section.
|
||||
|
||||
Traced Callbacks
|
||||
****************
|
||||
*Placeholder subsection*
|
||||
|
||||
Callback locations in ns-3
|
||||
**************************
|
||||
|
||||
Where are callbacks frequently used in |ns3|? Here are some of the
|
||||
more visible ones to typical users:
|
||||
|
||||
* Socket API
|
||||
* Layer-2/Layer-3 API
|
||||
* Tracing subsystem
|
||||
* API between IP and routing subsystems
|
||||
|
||||
Implementation details
|
||||
**********************
|
||||
|
||||
The code snippets above are simplistic and only designed to illustrate the mechanism
|
||||
itself. The actual Callback code is quite complicated and very template-intense and
|
||||
a deep understanding of the code is not required. If interested, expert users may
|
||||
find the following useful.
|
||||
|
||||
The code was originally written based on the techniques described in
|
||||
`<http://www.codeproject.com/cpp/TTLFunction.asp>`_.
|
||||
It was subsequently rewritten to follow the architecture outlined in
|
||||
`Modern C++ Design, Generic Programming and Design Patterns Applied, Alexandrescu, chapter 5, Generalized Functors <http://www.moderncppdesign.com/book/main.html>`_.
|
||||
|
||||
This code uses:
|
||||
|
||||
* default template parameters to saves users from having to
|
||||
specify empty parameters when the number of parameters
|
||||
is smaller than the maximum supported number
|
||||
* the pimpl idiom: the Callback class is passed around by
|
||||
value and delegates the crux of the work to its pimpl pointer.
|
||||
* two pimpl implementations which derive from CallbackImpl
|
||||
FunctorCallbackImpl can be used with any functor-type
|
||||
while MemPtrCallbackImpl can be used with pointers to
|
||||
member functions.
|
||||
* a reference list implementation to implement the Callback's
|
||||
value semantics.
|
||||
|
||||
This code most notably departs from the Alexandrescu implementation in that it
|
||||
does not use type lists to specify and pass around the types of the callback
|
||||
arguments. Of course, it also does not use copy-destruction semantics and
|
||||
relies on a reference list rather than autoPtr to hold the pointer.
|
||||
@@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ns-3 documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Dec 14 09:00:39 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.pngmath']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'ns-3'
|
||||
copyright = u'2010, ns-3 project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = 'ns-3-dev'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = 'ns-3-dev'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'ns3_html_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['../..']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = 'Manual'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
#html_short_title = None
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%b %d, %Y %H:%M'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'ns-3doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'ns-3-manual.tex', u'ns-3 Manual',
|
||||
u'ns-3 project', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
latex_logo = '../../ns3_html_theme/static/ns-3.png'
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
latex_preamble = '\usepackage{amssymb}'
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'ns-3-manual', u'ns-3 Manual',
|
||||
[u'ns-3 project'], 1)
|
||||
]
|
||||
@@ -1,647 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Creating Documentation
|
||||
----------------------
|
||||
|
||||
|ns3| supplies two kinds of documentation: expository "user-guide"-style
|
||||
chapters, and source code API documentation.
|
||||
|
||||
The "user-guide" chapters are written by hand in reStructuredText_
|
||||
format (``.rst``), which is processed by the Python documentation
|
||||
system Sphinx_ to generate web pages and pdf files.
|
||||
The API documentation is generated from the source code itself,
|
||||
using Doxygen_, to generate cross-linked web pages.
|
||||
Both of these are important: the Sphinx chapters explain the *why*
|
||||
and overview of using a model; the API documentation explains the
|
||||
*how* details.
|
||||
|
||||
This chapter gives a quick overview of these
|
||||
tools, emphasizing preferred usage and customizations for |ns3|.
|
||||
|
||||
To build all the standard documentation:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf docs
|
||||
|
||||
For more specialized options, read on.
|
||||
|
||||
.. _reStructuredText: http://sphinx-doc.org/rest.html
|
||||
.. _sphinx: http://sphinx-doc.org/
|
||||
.. _doxygen: http://www.doxygen.org/
|
||||
|
||||
|
||||
Documenting with Sphinx
|
||||
***********************
|
||||
|
||||
We use Sphinx_ to generate expository chapters describing
|
||||
the design and usage of each module. Right now you are reading the
|
||||
:doc:`Documentation <documentation>` Chapter.
|
||||
If you are reading the html version, the
|
||||
`Show Source <_sources/documentation.txt>`_ link in the sidebar
|
||||
will show you the reStructuredText source for this chapter.
|
||||
|
||||
Adding New Chapters
|
||||
===================
|
||||
|
||||
Adding a new chapter takes three steps (described in more detail below):
|
||||
|
||||
#. Choose `Where?`_ the documentation file(s) will live.
|
||||
#. `Link`_ from an existing page to the new documentation.
|
||||
#. Add the new file to the `Makefile`_.
|
||||
|
||||
Where?
|
||||
######
|
||||
|
||||
Documentation for a specific module, ``foo``, should normally go in
|
||||
``src/foo/doc/``. For example ``src/foo/doc/foo.rst`` would be the
|
||||
top-level document for the module. The ``src/create-module.py`` script
|
||||
will create this file for you.
|
||||
|
||||
Some models require several ``.rst`` files, and figures; these should
|
||||
all go in the ``src/foo/doc/`` directory. The docs are actually built
|
||||
by a Sphinx Makefile. For especially involved
|
||||
documentation, it may be helpful to have a local ``Makefile``
|
||||
in the ``src/foo/doc/`` directory to
|
||||
simplify building the documentation for this module
|
||||
(`Antenna`_ is an example). Setting this up
|
||||
is not particularly hard, but is beyond the scope of this chapter.
|
||||
|
||||
In some cases, documentation spans multiple models; the
|
||||
`Network`_ chapter is an example. In these cases
|
||||
adding the ``.rst`` files directly to ``doc/models/source/`` might
|
||||
be appropriate.
|
||||
|
||||
.. _antenna: http://www.nsnam.org/docs/models/html/antenna.html
|
||||
.. _network: http://www.nsnam.org/docs/models/html/network.html
|
||||
|
||||
Link
|
||||
####
|
||||
|
||||
Sphinx has to know *where* your new chapter should appear. In most
|
||||
cases, a new model chapter should appear the in `Models` book.
|
||||
To add your chapter there, edit ``doc/models/source/index.rst``
|
||||
|
||||
.. sourcecode:: rest
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
organization
|
||||
animation
|
||||
antenna
|
||||
aodv
|
||||
applications
|
||||
...
|
||||
|
||||
Add the name of your document (without the ``.rst`` extension) to
|
||||
this list. Please keep the Model chapters in alphabetical order,
|
||||
to ease visual scanning for specific chapters.
|
||||
|
||||
Makefile
|
||||
########
|
||||
|
||||
You also have to add your document to the appropriate ``Makefile``,
|
||||
so ``make`` knows to check it for updates. The Models book Makefile
|
||||
is ``doc/models/Makefile``, the Manual book Makefile is
|
||||
``doc/manual/Makefile``.
|
||||
|
||||
.. sourcecode:: make
|
||||
|
||||
# list all model library .rst files that need to be copied to $SOURCETEMP
|
||||
SOURCES = \
|
||||
source/conf.py \
|
||||
source/_static \
|
||||
source/index.rst \
|
||||
source/replace.txt \
|
||||
source/organization.rst \
|
||||
...
|
||||
$(SRC)/antenna/doc/source/antenna.rst \
|
||||
...
|
||||
|
||||
You add your ``.rst`` files to the ``SOURCES`` variable. To add figures,
|
||||
read the comments in the ``Makefile`` to see which variable should contain
|
||||
your image files. Again, please keep these in alphabetical order.
|
||||
|
||||
Building Sphinx Docs
|
||||
====================
|
||||
|
||||
Building the Sphinx documentation is pretty simple.
|
||||
To build all the Sphinx documentation:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf sphinx
|
||||
|
||||
To build just the Models documentation:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ make -C doc/models html
|
||||
|
||||
To see the generated documentation point your browser at
|
||||
``doc/models/build/html``.
|
||||
|
||||
As you can see, Sphinx uses Make to guide the process.
|
||||
The default target builds all enabled output forms, which in
|
||||
|ns3| are the multi-page ``html``, single-page ``singlehtml``, and pdf
|
||||
(``latex``). To build just the multi-page html, you add the ``html`` target:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ make -C doc/models html
|
||||
|
||||
This can be helpful to reduce the build time (and the size of the
|
||||
build chatter) as you are writing your chapter.
|
||||
|
||||
Before committing your documentation to the repo, please check
|
||||
that it builds without errors or warnings. The build process
|
||||
generates lots of output (mostly normal chatter from LaTeX),
|
||||
which can make it difficult to see if there are any Sphinx
|
||||
warnings or errors. To find important warnings and errors
|
||||
build just the ``html`` version, then search the build log
|
||||
for ``warning`` or ``error``.
|
||||
|
||||
|
||||
|ns3| Specifics
|
||||
===============
|
||||
|
||||
The Sphinx documentation_ and tutorial_ are pretty good. We won't duplicate
|
||||
the basics here, instead focusing on preferred usage for |ns3|.
|
||||
|
||||
.. _documentation: http://sphinx-doc.org/contents.html
|
||||
.. _tutorial: http://sphinx-doc.org/tutorial.html
|
||||
|
||||
|
||||
* Start documents with these two lines:
|
||||
|
||||
.. sourcecode:: rest
|
||||
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
The first line enables some simple replacements. For example,
|
||||
typing ``|ns3|`` renders as |ns3|.
|
||||
The second sets the default source code highlighting language explicitly
|
||||
for the file, since the parser guess isn't always accurate.
|
||||
(It's also possible to set the language explicitly for a single code block,
|
||||
see below.)
|
||||
|
||||
* Sections:
|
||||
|
||||
Sphinx is pretty liberal about marking section headings. By convention,
|
||||
we prefer this hierarchy:
|
||||
|
||||
.. sourcecode:: rest
|
||||
|
||||
.. heading hierarchy:
|
||||
------------- Chapter
|
||||
************* Section (#.#)
|
||||
============= Subsection (#.#.#)
|
||||
############# Sub-subsection
|
||||
|
||||
* Syntax Highlighting:
|
||||
|
||||
To use the default syntax highlighter, simply start a sourcecode block:
|
||||
|
||||
+--------------------------------------+------------------------------------+
|
||||
| Sphinx Source | Rendered Output |
|
||||
+======================================+====================================+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| The ``Frobnitz`` is accessed by:: | The ``Frobnitz`` is accessed by:: |
|
||||
| | |
|
||||
| Foo::Frobnitz frob; | Foo::Frobnitz frob; |
|
||||
| frob.Set (...); | frob.Set (...); |
|
||||
+--------------------------------------+------------------------------------+
|
||||
|
||||
To use a specific syntax highlighter, for example, ``bash`` shell commands:
|
||||
|
||||
+--------------------------------------+------------------------------------+
|
||||
| Sphinx Source | Rendered Output |
|
||||
+======================================+====================================+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| .. sourcecode:: bash | .. sourcecode:: bash |
|
||||
| | |
|
||||
| $ ls | $ ls |
|
||||
+--------------------------------------+------------------------------------+
|
||||
|
||||
* Shorthand Notations:
|
||||
|
||||
These shorthands are defined:
|
||||
|
||||
+--------------------------------------+------------------------------------+
|
||||
| Sphinx Source | Rendered Output |
|
||||
+======================================+====================================+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| |ns3| | |ns3| |
|
||||
+--------------------------------------+------------------------------------+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| |ns2| | |ns2| |
|
||||
+--------------------------------------+------------------------------------+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| |check| | |check| |
|
||||
+--------------------------------------+------------------------------------+
|
||||
| .. sourcecode:: rest | |
|
||||
| | |
|
||||
| :rfc:`6282` | :rfc:`6282` |
|
||||
+--------------------------------------+------------------------------------+
|
||||
|
||||
|
||||
Documenting with Doxygen
|
||||
************************
|
||||
|
||||
We use Doxygen_ to generate browsable_ API documentation. Doxygen
|
||||
provides a number of useful features:
|
||||
|
||||
* Summary table of all class members.
|
||||
* Graphs of inheritance and collaboration for all classes.
|
||||
* Links to the source code implementing each function.
|
||||
* Links to every place a member is used.
|
||||
* Links to every object used in implementing a function.
|
||||
* Grouping of related classes, such as all the classes related to
|
||||
a specific protocol.
|
||||
|
||||
In addition, we use the ``TypeId`` system to add to the documentation
|
||||
for every class
|
||||
|
||||
* The ``Config`` paths by which such objects can be reached.
|
||||
* Documentation for any ``Attributes``, including ``Attributes``
|
||||
defined in parent classes.
|
||||
* Documentation for any ``Trace`` sources defined by the class.
|
||||
* The memory footprint for each class.
|
||||
|
||||
Doxygen operates by scaning the source code, looking for
|
||||
specially marked comments. It also creates a cross reference,
|
||||
indicating *where* each file, class, method, and variable is used.
|
||||
|
||||
.. _browsable: https://www.nsnam.org/docs/doxygen
|
||||
|
||||
|
||||
Preferred Style
|
||||
===============
|
||||
|
||||
The preferred style for Doxygen comments is the JavaDoc style::
|
||||
|
||||
/**
|
||||
* Brief description of this class or method.
|
||||
* Adjacent lines become a single paragraph.
|
||||
*
|
||||
* Longer description, with lots of details.
|
||||
*
|
||||
* Blank lines separate paragraphs.
|
||||
*
|
||||
* Explain what the class or method does, using what algorithm.
|
||||
* Explain the units of arguments and return values.
|
||||
*
|
||||
* \note Note any limitations or gotchas.
|
||||
*
|
||||
* (For functions with arguments or return valued:)
|
||||
* \param [in] foo Brief noun phrase describing this argument. Note
|
||||
* that we indicate if the argument is input,
|
||||
* output, or both.
|
||||
* \param [in,out] bar Note Sentence case, and terminating period.
|
||||
* \param [in] baz Indicate boolean values with \c true or \c false.
|
||||
* \return Brief noun phrase describing the value.
|
||||
*
|
||||
* \internal
|
||||
*
|
||||
* You can also discuss internal implementation details.
|
||||
* Understanding this material shouldn't be necessary to using
|
||||
* the class or method.
|
||||
*/
|
||||
void ExampleFunction (const int foo, double & bar, const bool baz);
|
||||
|
||||
In this style the Doxygen comment block begins with two \`*' characters:
|
||||
``/**``, and precedes the item being documented.
|
||||
|
||||
For items needing only a brief description, either of these short forms
|
||||
is appropriate::
|
||||
|
||||
/** Destructor implementation. */
|
||||
void DoDispose ();
|
||||
|
||||
int m_count; //!< Count of ...
|
||||
|
||||
Note the special form of the end of line comment, ``//!<``, indicating
|
||||
that it refers to the *preceding* item.
|
||||
|
||||
Some items to note:
|
||||
|
||||
* Use sentence case, including the initial capital.
|
||||
* Use punctuation, especially \`.'s at the end of sentences or phrases.
|
||||
* The ``\brief`` tag is not needed; the first sentence will be
|
||||
used as the brief description.
|
||||
|
||||
Every class, method, typedef, member variable, function argument
|
||||
and return value should be documented in all source code files
|
||||
which form the formal API and implementation for |ns3|, such as
|
||||
``src/<module>/model/*``, ``src/<module>/helper/*`` and
|
||||
``src/<module>/utils/*``. Documentation for items in ``src/<module>/test/*``
|
||||
and ``src/<module>/examples/*`` is preferred, but not required.
|
||||
|
||||
Useful Features
|
||||
===============
|
||||
|
||||
* Inherited members will automatically inherit docs from the parent,
|
||||
(but can be replaced by local documentation).
|
||||
|
||||
#. Document the base class.
|
||||
#. In the sub class mark inherited functions with an ordinary comment::
|
||||
|
||||
// Inherited methods
|
||||
virtual void FooBar (void);
|
||||
virtual int BarFoo (double baz);
|
||||
|
||||
Note that the signatures have to match exactly, so include the formal
|
||||
argument ``(void)``
|
||||
|
||||
This doesn't work for static functions; see ``GetTypeId``, below, for an
|
||||
example.
|
||||
|
||||
Building Doxygen Docs
|
||||
=====================
|
||||
|
||||
Building the Doxygen documentation is pretty simple:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf doxygen
|
||||
|
||||
This builds using the default configuration, which generates
|
||||
documentation sections for *all* items, even if they do not have
|
||||
explicit comment documentation blocks. This has the effect of
|
||||
suppressing warnings for undocumented items, but makes sure everything
|
||||
appears in the generated output, which is usually what you want for
|
||||
general use. Note that we generate documentation even for modules
|
||||
which are disabled, to make it easier to see all the features
|
||||
available in |ns3|.
|
||||
|
||||
When writing documentation, it's often more useful to see which items
|
||||
are generating warnings, typically about missing documentation. To
|
||||
see the full warnings list, use the ``doc/doxygen.warnings.report.sh``
|
||||
script:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ doc/doxygen.warnings.report.sh
|
||||
|
||||
doxygen.warnings.report.sh:
|
||||
Building and running print-introspected-doxygen...done.
|
||||
Rebuilding doxygen (v1.8.10) docs with full errors...done.
|
||||
|
||||
|
||||
Report of Doxygen warnings
|
||||
----------------------------------------
|
||||
|
||||
(All counts are lower bounds.)
|
||||
|
||||
Warnings by module/directory:
|
||||
|
||||
Count Directory
|
||||
----- ----------------------------------
|
||||
3414 src/lte/model
|
||||
1532 src/wimax/model
|
||||
825 src/lte/test
|
||||
....
|
||||
1 src/applications/test
|
||||
97 additional undocumented parameters.
|
||||
----------------------------------------
|
||||
12460 total warnings
|
||||
100 directories with warnings
|
||||
|
||||
|
||||
Warnings by file (alphabetical)
|
||||
|
||||
Count File
|
||||
----- ----------------------------------
|
||||
15 examples/routing/manet-routing-compare.cc
|
||||
26 examples/stats/wifi-example-apps.h
|
||||
12 examples/tutorial/fifth.cc
|
||||
....
|
||||
17 utils/python-unit-tests.py
|
||||
----------------------------------------
|
||||
771 files with warnings
|
||||
|
||||
|
||||
Warnings by file (numerical)
|
||||
|
||||
Count File
|
||||
----- ----------------------------------
|
||||
273 src/lte/model/lte-rrc-sap.h
|
||||
272 src/core/model/simulator.h
|
||||
221 src/netanim/model/animation-interface.h
|
||||
....
|
||||
1 src/wimax/model/ul-job.cc
|
||||
----------------------------------------
|
||||
771 files with warnings
|
||||
|
||||
|
||||
Doxygen Warnings Summary
|
||||
----------------------------------------
|
||||
100 directories
|
||||
771 files
|
||||
12460 warnings
|
||||
|
||||
(This snippet has *a lot* of lines suppressed!)
|
||||
|
||||
The script modifies the configuration to show all warnings, and to
|
||||
shorten the run time. (It shortens the run time primarily by
|
||||
disabling creation of diagrams, such as call trees, and doesn't
|
||||
generate documentation for undocumented items, in order to trigger the
|
||||
warnings.) As you can see, at this writing we have *a lot* of
|
||||
undocumented items. The report summarizes warnings by module
|
||||
``src/*/*``, and by file, in alphabetically and numerical order.
|
||||
|
||||
The script has a few options to pare things down and make the output more
|
||||
manageable. For help, use the ``-h`` option. Having run it once
|
||||
to do the Doxygen build and generate the full warnings log,
|
||||
you can reprocess the log file with various "filters,"
|
||||
without having to do the full Doxygen build again by
|
||||
using the ``-s`` option. You can exclude warnings
|
||||
from ``*/examples/*`` files (``-e`` option), and/or ``*/test/*`` files
|
||||
(``-t``). Just to be clear, all of the filter options do the complete
|
||||
fast doxygen build; they just filter doxygen log and warnings output.
|
||||
|
||||
Perhaps the most useful option when writing documentation comments
|
||||
is ``-m <module>``, which will limit the report to just files matching
|
||||
``src/<module>/*``, and follow the report with the actual warning lines.
|
||||
Combine with ``-et`` and you can focus on the warnings that are most
|
||||
urgent in a single module:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ doc/doxygen.warnings.report.sh -m mesh/helper
|
||||
...
|
||||
Doxygen Warnings Summary
|
||||
----------------------------------------
|
||||
1 directories
|
||||
3 files
|
||||
149 warnings
|
||||
|
||||
|
||||
Filtered Warnings
|
||||
========================================
|
||||
src/mesh/helper/dot11s/dot11s-installer.h:72: warning: Member m_root (variable) of class ns3::Dot11sStack is not documented.
|
||||
src/mesh/helper/dot11s/dot11s-installer.h:35: warning: return type of member ns3::Dot11sStack::GetTypeId is not documented
|
||||
src/mesh/helper/dot11s/dot11s-installer.h:56: warning: return type of member ns3::Dot11sStack::InstallStack is not documented
|
||||
src/mesh/helper/flame/lfame-installer.h:40: warning: Member GetTypeId() (function) of class ns3::FlameStack is not documented.
|
||||
src/mesh/helper/flame/flame-installer.h:60: warning: return type of member ns3::FlameStack::InstallStack is not documented
|
||||
src/mesh/helper/mesh-helper.h:213: warning: Member m_nInterfaces (variable) of class ns3::MeshHelper is not documented.
|
||||
src/mesh/helper/mesh-helper.h:214: warning: Member m_spreadChannelPolicy (variable) of class ns3::MeshHelper is not documented.
|
||||
src/mesh/helper/mesh-helper.h:215: warning: Member m_stack (variable) of class ns3::MeshHelper is not documented.
|
||||
src/mesh/helper/mesh-helper.h:216: warning: Member m_stackFactory (variable) of class ns3::MeshHelper is not documented.
|
||||
src/mesh/helper/mesh-helper.h:209: warning: parameters of member ns3::MeshHelper::CreateInterface are not (all) documented
|
||||
src/mesh/helper/mesh-helper.h:119: warning: parameters of member ns3::MeshHelper::SetStandard are not (all) documented
|
||||
|
||||
|
||||
Finally, note that undocumented items (classes, methods, functions,
|
||||
typedefs, *etc.* won't produce documentation when you build with
|
||||
``doxygen.warnings.report.sh``, and only the outermost item
|
||||
will produce a warning. As a result, if you don't see documentation
|
||||
for a class method in the generated documentation, the class itself
|
||||
probably needs documentation.
|
||||
|
||||
Now it's just a matter of understanding the code, and writing some
|
||||
docs!
|
||||
|
||||
|
||||
|
||||
|ns3| Specifics
|
||||
===============
|
||||
|
||||
As for Sphinx, the Doxygen docs_ and reference_ are pretty good.
|
||||
We won't duplicate the basics here, instead focusing on preferred
|
||||
usage for |ns3|.
|
||||
|
||||
.. _docs: http://www.stack.nl/~dimitri/doxygen/index.html
|
||||
.. _reference: http://www.stack.nl/~dimitri/doxygen/manual/commands.html
|
||||
|
||||
|
||||
* Use Doxygen ``Modules`` to group related items.
|
||||
|
||||
In the main header for a module, create a Doxgyen group::
|
||||
|
||||
/**
|
||||
* \defgroup foo Foo protocol.
|
||||
* Implemenation of the Foo protocol.
|
||||
*/
|
||||
|
||||
The symbol ``foo`` is how other items can add themselves to this group.
|
||||
The string following that will be the title for the group. Any futher
|
||||
text will be the detailed description for the group page.
|
||||
|
||||
* Document each file, assigning it to the relevant group. In a header file::
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup foo
|
||||
* Class Foo declaration.
|
||||
*/
|
||||
|
||||
or in the corresponding ``.cc`` file::
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \ingroup foo
|
||||
* Class FooBar implementation.
|
||||
*/
|
||||
|
||||
* Mark each associated class as belonging to the group::
|
||||
|
||||
/**
|
||||
* \ingroup foo
|
||||
*
|
||||
* FooBar packet type.
|
||||
*/
|
||||
class FooBar
|
||||
|
||||
* Did you know ``typedefs`` can have formal arguments? This enables
|
||||
documentation of function pointer signatures::
|
||||
|
||||
/**
|
||||
* Bar callback function signature.
|
||||
*
|
||||
* \param ale The size of a pint of ale, in Imperial ounces.
|
||||
*/
|
||||
typedef void (* BarCallback)(const int ale);
|
||||
|
||||
* Copy the ``Attribute`` help strings from the ``GetTypeId`` method to use
|
||||
as the brief descriptions of associated members.
|
||||
|
||||
* ``\bugid{298}`` will create a link to bug 298 in our Bugzilla.
|
||||
|
||||
* ``\p foo`` in a description will format ``foo``
|
||||
the same as the ``\param foo`` parameter, making it clear that you
|
||||
are referring to an actual argument.
|
||||
|
||||
* ``\RFC{301}`` will create a link to RFC 301.
|
||||
|
||||
* Document the direction of function arguments with ``\param [in]``, *etc*.
|
||||
The allowed values of the direction token are ``[in]``, ``[out]``, and
|
||||
``[in,out]`` (note the explicit square brackets), as discussed in the
|
||||
Doxygen docs for ``\param``.
|
||||
|
||||
* Document template arguments with ``\tparam``, just as you use ``\param``
|
||||
for function arguments.
|
||||
|
||||
* For template arguments, indicate if they will be deduced or must be given
|
||||
explicitly::
|
||||
|
||||
/**
|
||||
* A templated function.
|
||||
* \tparam T \explicit The return type.
|
||||
* \tparam U \deduced The argument type.
|
||||
* \param [in] a The argument.
|
||||
*/
|
||||
template <typename T, typename U> T Function (U a);
|
||||
|
||||
* Use ``\tparam U \deduced`` because the type ``U`` can be deduced at
|
||||
the site where the template is invoked. Basically deduction can only
|
||||
be done for function arguments.
|
||||
|
||||
* Use ``\tparam T \explicit`` because the type ``T`` can't be deduced;
|
||||
it must be given explicitly at the invocation site.
|
||||
|
||||
* ``\internal`` should be used only to set off a discussion of implementation
|
||||
details, not to mark ``private`` functions (they are already marked,
|
||||
as ``private``!)
|
||||
|
||||
* Don't create classes with trivial names, such as ``class A``,
|
||||
even in test suites. These cause all instances of the class name
|
||||
literal \`A' to be rendered as links.
|
||||
|
||||
|
||||
As noted above, static functions don't inherit the documentation
|
||||
of the same functions in the parent class. |ns3| uses a few static
|
||||
functions ubiquitously; the suggested documentation block for these
|
||||
cases is:
|
||||
|
||||
* Default constructor/destructor::
|
||||
|
||||
MyClass (); //!< Default constructor
|
||||
~MyClass (); //!< Destructor
|
||||
|
||||
* Dummy destructor and DoDispose::
|
||||
|
||||
/** Dummy destructor, see DoDispose. */
|
||||
~MyClass ();
|
||||
|
||||
/** Destructor implementation */
|
||||
virtual void DoDispose ();
|
||||
|
||||
* GetTypeId::
|
||||
|
||||
/**
|
||||
* Register this type.
|
||||
* \return The object TypeId.
|
||||
*/
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
|
||||
|
||||
@@ -1,162 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: bash
|
||||
|
||||
Enabling Subsets of |ns3| Modules
|
||||
---------------------------------
|
||||
|
||||
As with most software projects, |ns3| is ever growing larger in terms of number of modules, lines of code, and memory footprint. Users, however, may only use a few of those modules at a time. For this reason, users may want to explicitly enable only the subset of the possible |ns3| modules that they actually need for their research.
|
||||
|
||||
This chapter discusses how to enable only the |ns3| modules that you are intersted in using.
|
||||
|
||||
How to enable a subset of |ns3|'s modules
|
||||
*****************************************
|
||||
|
||||
If shared libraries are being built, then enabling a module will cause at least one library to be built:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
libns3-modulename.so
|
||||
|
||||
If the module has a test library and test libraries are being built, then
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
libns3-modulename-test.so
|
||||
|
||||
will be built, too. Other modules that the module depends on and their test libraries will also be built.
|
||||
|
||||
By default, all modules are built in |ns3|. There are two ways to enable a subset of these modules:
|
||||
|
||||
#. Using waf's --enable-modules option
|
||||
#. Using the |ns3| configuration file
|
||||
|
||||
Enable modules using waf's --enable-modules option
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To enable only the core module with example and tests, for example,
|
||||
try these commands: ::
|
||||
|
||||
$ ./waf clean
|
||||
$ ./waf configure --enable-examples --enable-tests --enable-modules=core
|
||||
$ ./waf build
|
||||
$ cd build/debug/
|
||||
$ ls
|
||||
|
||||
and the following libraries should be present:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
bindings libns3-core.so ns3 scratch utils
|
||||
examples libns3-core-test.so samples src
|
||||
|
||||
Note the ``./waf clean`` step is done here only to make it more obvious which module libraries were built. You don't have to do ``./waf clean`` in order to enable subsets of modules.
|
||||
|
||||
Running test.py will cause only those tests that depend on module core to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
24 of 24 tests passed (24 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
Repeat the above steps for the "network" module instead of the "core" module, and the following will be built, since network depends on core:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
bindings libns3-core.so libns3-network.so ns3 scratch utils
|
||||
examples libns3-core-test.so libns3-network-test.so samples src
|
||||
|
||||
Running test.py will cause those tests that depend on only the core and network modules to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
31 of 31 tests passed (31 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
Enable modules using the |ns3| configuration file
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
A configuration file, .ns3rc, has been added to |ns3| that allows users to specify which modules are to be included in the build.
|
||||
|
||||
When enabling a subset of |ns3| modules, the precedence rules are as follows:
|
||||
|
||||
#. the --enable-modules configure string overrides any .ns3rc file
|
||||
#. the .ns3rc file in the top level |ns3| directory is next consulted, if present
|
||||
#. the system searches for ~/.ns3rc if the above two are unspecified
|
||||
|
||||
If none of the above limits the modules to be built, all modules that waf knows about will be built.
|
||||
|
||||
The maintained version of the .ns3rc file in the |ns3| source code repository resides in the ``utils`` directory. The reason for this is if it were in the top-level directory of the repository, it would be prone to accidental checkins from maintainers that enable the modules they want to use. Therefore, users need to manually copy the .ns3rc from the ``utils`` directory to their preferred place (top level directory or their home directory) to enable persistent modular build configuration.
|
||||
|
||||
Assuming that you are in the top level |ns3| directory, you can get a copy of the .ns3rc file that is in the ``utils`` directory as follows: ::
|
||||
|
||||
$ cp utils/.ns3rc .
|
||||
|
||||
The .ns3rc file should now be in your top level |ns3| directory, and it contains the following:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
#! /usr/bin/env python
|
||||
|
||||
# A list of the modules that will be enabled when ns-3 is run.
|
||||
# Modules that depend on the listed modules will be enabled also.
|
||||
#
|
||||
# All modules can be enabled by choosing 'all_modules'.
|
||||
modules_enabled = ['all_modules']
|
||||
|
||||
# Set this equal to true if you want examples to be run.
|
||||
examples_enabled = False
|
||||
|
||||
# Set this equal to true if you want tests to be run.
|
||||
tests_enabled = False
|
||||
|
||||
Use your favorite editor to modify the .ns3rc file to only enable the core module with examples and tests like this:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
#! /usr/bin/env python
|
||||
|
||||
# A list of the modules that will be enabled when ns-3 is run.
|
||||
# Modules that depend on the listed modules will be enabled also.
|
||||
#
|
||||
# All modules can be enabled by choosing 'all_modules'.
|
||||
modules_enabled = ['core']
|
||||
|
||||
# Set this equal to true if you want examples to be run.
|
||||
examples_enabled = True
|
||||
|
||||
# Set this equal to true if you want tests to be run.
|
||||
tests_enabled = True
|
||||
|
||||
Only the core module will be enabled now if you try these commands: ::
|
||||
|
||||
$ ./waf clean
|
||||
$ ./waf configure
|
||||
$ ./waf build
|
||||
$ cd build/debug/
|
||||
$ ls
|
||||
|
||||
and the following libraries should be present:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
bindings libns3-core.so ns3 scratch utils
|
||||
examples libns3-core-test.so samples src
|
||||
|
||||
Note the ``./waf clean`` step is done here only to make it more obvious which module libraries were built. You don't have to do ``./waf clean`` in order to enable subsets of modules.
|
||||
|
||||
Running test.py will cause only those tests that depend on module core to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
24 of 24 tests passed (24 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
Repeat the above steps for the "network" module instead of the "core" module, and the following will be built, since network depends on core:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
bindings libns3-core.so libns3-network.so ns3 scratch utils
|
||||
examples libns3-core-test.so libns3-network-test.so samples src
|
||||
|
||||
Running test.py will cause those tests that depend on only the core and network modules to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
31 of 31 tests passed (31 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
@@ -1,159 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: bash
|
||||
|
||||
|
||||
Enabling/disabling |ns3| Tests and Examples
|
||||
-------------------------------------------
|
||||
|
||||
The |ns3| distribution includes many examples and tests that are used to validate the |ns3| system. Users, however, may not always want these examples and tests to be run for their installation of |ns3|.
|
||||
|
||||
This chapter discusses how to build |ns3| with or without its examples and tests.
|
||||
|
||||
How to enable/disable examples and tests in |ns3|
|
||||
*************************************************
|
||||
|
||||
There are 3 ways to enable/disable examples and tests in |ns3|:
|
||||
|
||||
#. Using build.py when |ns3| is built for the first time
|
||||
#. Using waf once |ns3| has been built
|
||||
#. Using the |ns3| configuration file once |ns3| has been built
|
||||
|
||||
Enable/disable examples and tests using build.py
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
You can use build.py to enable/disable examples and tests when |ns3| is built for the first time.
|
||||
|
||||
By default, examples and tests are not built in |ns3|.
|
||||
|
||||
From the ns-3-allinone directory, you can build |ns3| without any
|
||||
examples or tests simply by doing: ::
|
||||
|
||||
$ ./build.py
|
||||
|
||||
Running test.py in the top level |ns3| directory now will cause no examples or tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
If you would like build |ns3| with examples and tests, then do the following from the ns-3-allinone directory: ::
|
||||
|
||||
$ ./build.py --enable-examples --enable-tests
|
||||
|
||||
Running test.py in the top level |ns3| directory will cause all of the examples and tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
Enable/disable examples and tests using waf
|
||||
+++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
You can use waf to enable/disable examples and tests once |ns3| has been built.
|
||||
|
||||
By default, examples and tests are not built in |ns3|.
|
||||
|
||||
From the top level |ns3| directory, you can build |ns3| without any
|
||||
examples or tests simply by doing: ::
|
||||
|
||||
$ ./waf configure
|
||||
$ ./waf build
|
||||
|
||||
Running test.py now will cause no examples or tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
If you would like build |ns3| with examples and tests, then do the following from the top level |ns3| directory: ::
|
||||
|
||||
$ ./waf configure --enable-examples --enable-tests
|
||||
$ ./waf build
|
||||
|
||||
Running test.py will cause all of the examples and tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
Enable/disable examples and tests using the |ns3| configuration file
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
A configuration file, .ns3rc, has been added to |ns3| that allows users to specify whether examples and tests should be built or not. You can use this file to enable/disable examples and tests once |ns3| has been built.
|
||||
|
||||
When enabling disabling examples and tests, the precedence rules are as follows:
|
||||
|
||||
#. the --enable-examples/--disable-examples configure strings override any .ns3rc file
|
||||
#. the --enable-tests/--disable-tests configure strings override any .ns3rc file
|
||||
#. the .ns3rc file in the top level |ns3| directory is next consulted, if present
|
||||
#. the system searches for ~/.ns3rc if the .ns3rc file was not found in the previous step
|
||||
|
||||
If none of the above exists, then examples and tests will not be built.
|
||||
|
||||
The maintained version of the .ns3rc file in the |ns3| source code repository resides in the ``utils`` directory. The reason for this is if it were in the top-level directory of the repository, it would be prone to accidental checkins from maintainers that enable the modules they want to use. Therefore, users need to manually copy the .ns3rc from the ``utils`` directory to their preferred place (top level directory or their home directory) to enable persistent enabling of examples and tests.
|
||||
|
||||
Assuming that you are in the top level |ns3| directory, you can get a copy of the .ns3rc file that is in the ``utils`` directory as follows: ::
|
||||
|
||||
$ cp utils/.ns3rc .
|
||||
|
||||
The .ns3rc file should now be in your top level |ns3| directory, and it contains the following:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
#! /usr/bin/env python
|
||||
|
||||
# A list of the modules that will be enabled when ns-3 is run.
|
||||
# Modules that depend on the listed modules will be enabled also.
|
||||
#
|
||||
# All modules can be enabled by choosing 'all_modules'.
|
||||
modules_enabled = ['all_modules']
|
||||
|
||||
# Set this equal to true if you want examples to be run.
|
||||
examples_enabled = False
|
||||
|
||||
# Set this equal to true if you want tests to be run.
|
||||
tests_enabled = False
|
||||
|
||||
From the top level |ns3| directory, you can build |ns3| without any
|
||||
examples or tests simply by doing: ::
|
||||
|
||||
$ ./waf configure
|
||||
$ ./waf build
|
||||
|
||||
Running test.py now will cause no examples or tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
0 of 0 tests passed (0 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
If you would like build |ns3| with examples and tests, use your
|
||||
favorite editor to change the values in the .ns3rc file for
|
||||
examples_enabled and tests_enabled file to be True:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
#! /usr/bin/env python
|
||||
|
||||
# A list of the modules that will be enabled when ns-3 is run.
|
||||
# Modules that depend on the listed modules will be enabled also.
|
||||
#
|
||||
# All modules can be enabled by choosing 'all_modules'.
|
||||
modules_enabled = ['all_modules']
|
||||
|
||||
# Set this equal to true if you want examples to be run.
|
||||
examples_enabled = True
|
||||
|
||||
# Set this equal to true if you want tests to be run.
|
||||
tests_enabled = True
|
||||
|
||||
From the top level |ns3| directory, you can build |ns3| with examples
|
||||
and tests simply by doing: ::
|
||||
|
||||
$ ./waf configure
|
||||
$ ./waf build
|
||||
|
||||
Running test.py will cause all of the examples and tests to be run:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
170 of 170 tests passed (170 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
@@ -1,201 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
|
||||
.. heading hierarchy:
|
||||
------------- Chapter
|
||||
************* Section (#.#)
|
||||
============= Subsection (#.#.#)
|
||||
############# Paragraph (no number)
|
||||
|
||||
Events and Simulator
|
||||
--------------------
|
||||
|
||||
|ns3| is a discrete-event network simulator. Conceptually, the simulator
|
||||
keeps track of a number of events that are scheduled to execute at a
|
||||
specified simulation time. The job of the simulator is to execute the
|
||||
events in sequential time order. Once the completion of an event occurs,
|
||||
the simulator will move to the next event (or will exit if there are no
|
||||
more events in the event queue). If, for example, an event scheduled
|
||||
for simulation time "100 seconds" is executed, and the next event is not
|
||||
scheduled until "200 seconds", the simulator will immediately jump from
|
||||
100 seconds to 200 seconds (of simulation time) to execute the next event.
|
||||
This is what is meant by "discrete-event" simulator.
|
||||
|
||||
To make this all happen, the simulator needs a few things:
|
||||
|
||||
1) a simulator object that can access an event queue where events are
|
||||
stored and that can manage the execution of events
|
||||
2) a scheduler responsible for inserting and removing events from the queue
|
||||
3) a way to represent simulation time
|
||||
4) the events themselves
|
||||
|
||||
This chapter of the manual describes these fundamental objects
|
||||
(simulator, scheduler, time, event) and how they are used.
|
||||
|
||||
Event
|
||||
*****
|
||||
|
||||
*To be completed*
|
||||
|
||||
Simulator
|
||||
*********
|
||||
|
||||
The Simulator class is the public entry point to access event scheduling
|
||||
facilities. Once a couple of events have been scheduled to start the
|
||||
simulation, the user can start to execute them by entering the simulator
|
||||
main loop (call ``Simulator::Run``). Once the main loop starts running, it
|
||||
will sequentially execute all scheduled events in order from oldest to
|
||||
most recent until there are either no more events left in the event
|
||||
queue or Simulator::Stop has been called.
|
||||
|
||||
To schedule events for execution by the simulator main loop, the
|
||||
Simulator class provides the Simulator::Schedule* family of functions.
|
||||
|
||||
1) Handling event handlers with different signatures
|
||||
|
||||
These functions are declared and implemented as C++ templates to handle
|
||||
automatically the wide variety of C++ event handler signatures used in
|
||||
the wild. For example, to schedule an event to execute 10 seconds in the
|
||||
future, and invoke a C++ method or function with specific arguments, you
|
||||
might write this:
|
||||
|
||||
::
|
||||
|
||||
void handler (int arg0, int arg1)
|
||||
{
|
||||
std::cout << "handler called with argument arg0=" << arg0 << " and
|
||||
arg1=" << arg1 << std::endl;
|
||||
}
|
||||
|
||||
Simulator::Schedule(Seconds(10), &handler, 10, 5);
|
||||
|
||||
Which will output:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
handler called with argument arg0=10 and arg1=5
|
||||
|
||||
Of course, these C++ templates can also handle transparently member
|
||||
methods on C++ objects:
|
||||
|
||||
*To be completed: member method example*
|
||||
|
||||
Notes:
|
||||
|
||||
* the ns-3 Schedule methods recognize automatically functions and
|
||||
methods only if they take less than 5 arguments. If you need them to
|
||||
support more arguments, please, file a bug report.
|
||||
* Readers familiar with the term 'fully-bound functors' will recognize
|
||||
the Simulator::Schedule methods as a way to automatically construct such
|
||||
objects.
|
||||
|
||||
2) Common scheduling operations
|
||||
|
||||
The Simulator API was designed to make it really simple to schedule most
|
||||
events. It provides three variants to do so (ordered from most commonly
|
||||
used to least commonly used):
|
||||
|
||||
* Schedule methods which allow you to schedule an event in the future
|
||||
by providing the delay between the current simulation time and the
|
||||
expiration date of the target event.
|
||||
* ScheduleNow methods which allow you to schedule an event for the
|
||||
current simulation time: they will execute _after_ the current event is
|
||||
finished executing but _before_ the simulation time is changed for the
|
||||
next event.
|
||||
* ScheduleDestroy methods which allow you to hook in the shutdown
|
||||
process of the Simulator to cleanup simulation resources: every
|
||||
'destroy' event is executed when the user calls the Simulator::Destroy
|
||||
method.
|
||||
|
||||
3) Maintaining the simulation context
|
||||
|
||||
There are two basic ways to schedule events, with and without *context*.
|
||||
What does this mean?
|
||||
|
||||
::
|
||||
|
||||
Simulator::Schedule (Time const &time, MEM mem_ptr, OBJ obj);
|
||||
|
||||
vs.
|
||||
|
||||
::
|
||||
|
||||
Simulator::ScheduleWithContext (uint32_t context, Time const &time, MEM mem_ptr, OBJ obj);
|
||||
|
||||
Readers who invest time and effort in developing or using a non-trivial
|
||||
simulation model will know the value of the ns-3 logging framework to
|
||||
debug simple and complex simulations alike. One of the important
|
||||
features that is provided by this logging framework is the automatic
|
||||
display of the network node id associated with the 'currently' running
|
||||
event.
|
||||
|
||||
The node id of the currently executing network node is in fact tracked
|
||||
by the Simulator class. It can be accessed with the
|
||||
Simulator::GetContext method which returns the 'context' (a 32-bit
|
||||
integer) associated and stored in the currently-executing event. In some
|
||||
rare cases, when an event is not associated with a specific network
|
||||
node, its 'context' is set to 0xffffffff.
|
||||
|
||||
To associate a context to each event, the Schedule, and ScheduleNow
|
||||
methods automatically reuse the context of the currently-executing event
|
||||
as the context of the event scheduled for execution later.
|
||||
|
||||
In some cases, most notably when simulating the transmission of a packet
|
||||
from a node to another, this behavior is undesirable since the expected
|
||||
context of the reception event is that of the receiving node, not the
|
||||
sending node. To avoid this problem, the Simulator class provides a
|
||||
specific schedule method: ScheduleWithContext which allows one to
|
||||
provide explicitly the node id of the receiving node associated with
|
||||
the receive event.
|
||||
|
||||
*XXX: code example*
|
||||
|
||||
In some very rare cases, developers might need to modify or understand
|
||||
how the context (node id) of the first event is set to that of its
|
||||
associated node. This is accomplished by the NodeList class: whenever a
|
||||
new node is created, the NodeList class uses ScheduleWithContext to
|
||||
schedule a 'initialize' event for this node. The 'initialize' event thus executes
|
||||
with a context set to that of the node id and can use the normal variety
|
||||
of Schedule methods. It invokes the Node::Initialize method which propagates
|
||||
the 'initialize' event by calling the DoInitialize method for each object
|
||||
associated with the node. The DoInitialize method overridden in some of these
|
||||
objects (most notably in the Application base class) will schedule some
|
||||
events (most notably Application::StartApplication) which will in turn
|
||||
scheduling traffic generation events which will in turn schedule
|
||||
network-level events.
|
||||
|
||||
Notes:
|
||||
|
||||
* Users need to be careful to propagate DoInitialize methods across objects
|
||||
by calling Initialize explicitely on their member objects
|
||||
* The context id associated with each ScheduleWithContext method has
|
||||
other uses beyond logging: it is used by an experimental branch of ns-3
|
||||
to perform parallel simulation on multicore systems using
|
||||
multithreading.
|
||||
|
||||
The Simulator::* functions do not know what the context is: they
|
||||
merely make sure that whatever context you specify with
|
||||
ScheduleWithContext is available when the corresponding event executes
|
||||
with ::GetContext.
|
||||
|
||||
It is up to the models implemented on top of Simulator::* to interpret
|
||||
the context value. In ns-3, the network models interpret the context
|
||||
as the node id of the node which generated an event. This is why it is
|
||||
important to call ScheduleWithContext in ns3::Channel subclasses
|
||||
because we are generating an event from node i to node j and we want
|
||||
to make sure that the event which will run on node j has the right
|
||||
context.
|
||||
|
||||
Time
|
||||
****
|
||||
|
||||
*To be completed*
|
||||
|
||||
|
||||
Scheduler
|
||||
*********
|
||||
|
||||
*To be completed*
|
||||
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
../figures
|
||||
@@ -1,307 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Making Plots using the Gnuplot Class
|
||||
------------------------------------
|
||||
|
||||
There are 2 common methods to make a plot using |ns3| and gnuplot (http://www.gnuplot.info):
|
||||
|
||||
#. Create a gnuplot control file using |ns3|'s Gnuplot class.
|
||||
#. Create a gnuplot data file using values generated by |ns3|.
|
||||
|
||||
This section is about method 1, i.e. it is about how to make a plot using |ns3|'s Gnuplot class. If you are interested in method 2, see the "A Real Example" subsection under the "Tracing" section in the |ns3| `Tutorial <http://www.nsnam.org/tutorials.html>`_.
|
||||
|
||||
Creating Plots Using the Gnuplot Class
|
||||
**************************************
|
||||
|
||||
The following steps must be taken in order to create a plot using |ns3|'s Gnuplot class:
|
||||
|
||||
#. Modify your code so that is uses the Gnuplot class and its functions.
|
||||
#. Run your code so that it creates a gnuplot control file.
|
||||
#. Call gnuplot with the name of the gnuplot control file.
|
||||
#. View the graphics file that was produced in your favorite graphics viewer.
|
||||
|
||||
See the code from the example plots that are discussed below for details on step 1.
|
||||
|
||||
An Example Program that Uses the Gnuplot Class
|
||||
**********************************************
|
||||
|
||||
An example program that uses |ns3|'s Gnuplot class can be found here:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
src/stats/examples/gnuplot-example.cc
|
||||
|
||||
In order to run this example, do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf shell
|
||||
$ cd build/debug/src/stats/examples
|
||||
$ ./gnuplot-example
|
||||
|
||||
This should produce the following gnuplot control files in the directory where the example is located:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
plot-2d.plt
|
||||
plot-2d-with-error-bars.plt
|
||||
plot-3d.plt
|
||||
|
||||
In order to process these gnuplot control files, do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ gnuplot plot-2d.plt
|
||||
$ gnuplot plot-2d-with-error-bars.plt
|
||||
$ gnuplot plot-3d.plt
|
||||
|
||||
This should produce the following graphics files in the directory where the example is located:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
plot-2d.png
|
||||
plot-2d-with-error-bars.png
|
||||
plot-3d.png
|
||||
|
||||
You can view these graphics files in your favorite graphics viewer. If you have gimp installed on your machine, for example, you can do this:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ gimp plot-2d.png
|
||||
$ gimp plot-2d-with-error-bars.png
|
||||
$ gimp plot-3d.png
|
||||
|
||||
An Example 2-Dimensional Plot
|
||||
*****************************
|
||||
|
||||
The following 2-Dimensional plot
|
||||
|
||||
.. _plot-2d:
|
||||
|
||||
.. figure:: figures/plot-2d.*
|
||||
|
||||
was created using the following code from gnuplot-example.cc: ::
|
||||
|
||||
using namespace std;
|
||||
|
||||
string fileNameWithNoExtension = "plot-2d";
|
||||
string graphicsFileName = fileNameWithNoExtension + ".png";
|
||||
string plotFileName = fileNameWithNoExtension + ".plt";
|
||||
string plotTitle = "2-D Plot";
|
||||
string dataTitle = "2-D Data";
|
||||
|
||||
// Instantiate the plot and set its title.
|
||||
Gnuplot plot (graphicsFileName);
|
||||
plot.SetTitle (plotTitle);
|
||||
|
||||
// Make the graphics file, which the plot file will create when it
|
||||
// is used with Gnuplot, be a PNG file.
|
||||
plot.SetTerminal ("png");
|
||||
|
||||
// Set the labels for each axis.
|
||||
plot.SetLegend ("X Values", "Y Values");
|
||||
|
||||
// Set the range for the x axis.
|
||||
plot.AppendExtra ("set xrange [-6:+6]");
|
||||
|
||||
// Instantiate the dataset, set its title, and make the points be
|
||||
// plotted along with connecting lines.
|
||||
Gnuplot2dDataset dataset;
|
||||
dataset.SetTitle (dataTitle);
|
||||
dataset.SetStyle (Gnuplot2dDataset::LINES_POINTS);
|
||||
|
||||
double x;
|
||||
double y;
|
||||
|
||||
// Create the 2-D dataset.
|
||||
for (x = -5.0; x <= +5.0; x += 1.0)
|
||||
{
|
||||
// Calculate the 2-D curve
|
||||
//
|
||||
// 2
|
||||
// y = x .
|
||||
//
|
||||
y = x * x;
|
||||
|
||||
// Add this point.
|
||||
dataset.Add (x, y);
|
||||
}
|
||||
|
||||
// Add the dataset to the plot.
|
||||
plot.AddDataset (dataset);
|
||||
|
||||
// Open the plot file.
|
||||
ofstream plotFile (plotFileName.c_str());
|
||||
|
||||
// Write the plot file.
|
||||
plot.GenerateOutput (plotFile);
|
||||
|
||||
// Close the plot file.
|
||||
plotFile.close ();
|
||||
|
||||
An Example 2-Dimensional Plot with Error Bars
|
||||
*********************************************
|
||||
|
||||
The following 2-Dimensional plot with error bars in the x and y directions
|
||||
|
||||
.. _plot-2d-with-error-bars:
|
||||
|
||||
.. figure:: figures/plot-2d-with-error-bars.*
|
||||
|
||||
was created using the following code from gnuplot-example.cc: ::
|
||||
|
||||
using namespace std;
|
||||
|
||||
string fileNameWithNoExtension = "plot-2d-with-error-bars";
|
||||
string graphicsFileName = fileNameWithNoExtension + ".png";
|
||||
string plotFileName = fileNameWithNoExtension + ".plt";
|
||||
string plotTitle = "2-D Plot With Error Bars";
|
||||
string dataTitle = "2-D Data With Error Bars";
|
||||
|
||||
// Instantiate the plot and set its title.
|
||||
Gnuplot plot (graphicsFileName);
|
||||
plot.SetTitle (plotTitle);
|
||||
|
||||
// Make the graphics file, which the plot file will create when it
|
||||
// is used with Gnuplot, be a PNG file.
|
||||
plot.SetTerminal ("png");
|
||||
|
||||
// Set the labels for each axis.
|
||||
plot.SetLegend ("X Values", "Y Values");
|
||||
|
||||
// Set the range for the x axis.
|
||||
plot.AppendExtra ("set xrange [-6:+6]");
|
||||
|
||||
// Instantiate the dataset, set its title, and make the points be
|
||||
// plotted with no connecting lines.
|
||||
Gnuplot2dDataset dataset;
|
||||
dataset.SetTitle (dataTitle);
|
||||
dataset.SetStyle (Gnuplot2dDataset::POINTS);
|
||||
|
||||
// Make the dataset have error bars in both the x and y directions.
|
||||
dataset.SetErrorBars (Gnuplot2dDataset::XY);
|
||||
|
||||
double x;
|
||||
double xErrorDelta;
|
||||
double y;
|
||||
double yErrorDelta;
|
||||
|
||||
// Create the 2-D dataset.
|
||||
for (x = -5.0; x <= +5.0; x += 1.0)
|
||||
{
|
||||
// Calculate the 2-D curve
|
||||
//
|
||||
// 2
|
||||
// y = x .
|
||||
//
|
||||
y = x * x;
|
||||
|
||||
// Make the uncertainty in the x direction be constant and make
|
||||
// the uncertainty in the y direction be a constant fraction of
|
||||
// y's value.
|
||||
xErrorDelta = 0.25;
|
||||
yErrorDelta = 0.1 * y;
|
||||
|
||||
// Add this point with uncertainties in both the x and y
|
||||
// direction.
|
||||
dataset.Add (x, y, xErrorDelta, yErrorDelta);
|
||||
}
|
||||
|
||||
// Add the dataset to the plot.
|
||||
plot.AddDataset (dataset);
|
||||
|
||||
// Open the plot file.
|
||||
ofstream plotFile (plotFileName.c_str());
|
||||
|
||||
// Write the plot file.
|
||||
plot.GenerateOutput (plotFile);
|
||||
|
||||
// Close the plot file.
|
||||
plotFile.close ();
|
||||
|
||||
An Example 3-Dimensional Plot
|
||||
*****************************
|
||||
|
||||
The following 3-Dimensional plot
|
||||
|
||||
.. _plot-3d:
|
||||
|
||||
.. figure:: figures/plot-3d.*
|
||||
|
||||
was created using the following code from gnuplot-example.cc: ::
|
||||
|
||||
using namespace std;
|
||||
|
||||
string fileNameWithNoExtension = "plot-3d";
|
||||
string graphicsFileName = fileNameWithNoExtension + ".png";
|
||||
string plotFileName = fileNameWithNoExtension + ".plt";
|
||||
string plotTitle = "3-D Plot";
|
||||
string dataTitle = "3-D Data";
|
||||
|
||||
// Instantiate the plot and set its title.
|
||||
Gnuplot plot (graphicsFileName);
|
||||
plot.SetTitle (plotTitle);
|
||||
|
||||
// Make the graphics file, which the plot file will create when it
|
||||
// is used with Gnuplot, be a PNG file.
|
||||
plot.SetTerminal ("png");
|
||||
|
||||
// Rotate the plot 30 degrees around the x axis and then rotate the
|
||||
// plot 120 degrees around the new z axis.
|
||||
plot.AppendExtra ("set view 30, 120, 1.0, 1.0");
|
||||
|
||||
// Make the zero for the z-axis be in the x-axis and y-axis plane.
|
||||
plot.AppendExtra ("set ticslevel 0");
|
||||
|
||||
// Set the labels for each axis.
|
||||
plot.AppendExtra ("set xlabel 'X Values'");
|
||||
plot.AppendExtra ("set ylabel 'Y Values'");
|
||||
plot.AppendExtra ("set zlabel 'Z Values'");
|
||||
|
||||
// Set the ranges for the x and y axis.
|
||||
plot.AppendExtra ("set xrange [-5:+5]");
|
||||
plot.AppendExtra ("set yrange [-5:+5]");
|
||||
|
||||
// Instantiate the dataset, set its title, and make the points be
|
||||
// connected by lines.
|
||||
Gnuplot3dDataset dataset;
|
||||
dataset.SetTitle (dataTitle);
|
||||
dataset.SetStyle ("with lines");
|
||||
|
||||
double x;
|
||||
double y;
|
||||
double z;
|
||||
|
||||
// Create the 3-D dataset.
|
||||
for (x = -5.0; x <= +5.0; x += 1.0)
|
||||
{
|
||||
for (y = -5.0; y <= +5.0; y += 1.0)
|
||||
{
|
||||
// Calculate the 3-D surface
|
||||
//
|
||||
// 2 2
|
||||
// z = x * y .
|
||||
//
|
||||
z = x * x * y * y;
|
||||
|
||||
// Add this point.
|
||||
dataset.Add (x, y, z);
|
||||
}
|
||||
|
||||
// The blank line is necessary at the end of each x value's data
|
||||
// points for the 3-D surface grid to work.
|
||||
dataset.AddEmptyLine ();
|
||||
}
|
||||
|
||||
// Add the dataset to the plot.
|
||||
plot.AddDataset (dataset);
|
||||
|
||||
// Open the plot file.
|
||||
ofstream plotFile (plotFileName.c_str());
|
||||
|
||||
// Write the plot file.
|
||||
plot.GenerateOutput (plotFile);
|
||||
|
||||
// Close the plot file.
|
||||
plotFile.close ();
|
||||
@@ -1,118 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Hash Functions
|
||||
----------------
|
||||
|
||||
|ns3| provides a generic interface to general purpose hash functions.
|
||||
In the simplest usage, the hash function returns the 32-bit or 64-bit
|
||||
hash of a data buffer or string. The default underlying hash function
|
||||
is murmur3_, chosen because it has good hash function properties and
|
||||
offers a 64-bit version. The venerable FNV1a_ hash is also available.
|
||||
|
||||
There is a straight-forward mechanism to
|
||||
add (or provide at run time) alternative hash function implementations.
|
||||
|
||||
.. _murmur3: http://code.google.com/p/smhasher/wiki/MurmurHash3
|
||||
.. _FNV1a: http://isthe.com/chongo/tech/comp/fnv/
|
||||
|
||||
Basic Usage
|
||||
***********
|
||||
|
||||
The simplest way to get a hash value of a data buffer or string is just::
|
||||
|
||||
#include "ns3/hash.h"
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
char * buffer = ...
|
||||
size_t buffer_size = ...
|
||||
|
||||
uint32_t buffer_hash = Hash32 ( buffer, buffer_size);
|
||||
|
||||
std::string s;
|
||||
uint32_t string_hash = Hash32 (s);
|
||||
|
||||
Equivalent functions are defined for 64-bit hash values.
|
||||
|
||||
Incremental Hashing
|
||||
*******************
|
||||
|
||||
In some situations it's useful to compute the hash of multiple buffers,
|
||||
as if they had been joined together. (For example, you might want
|
||||
the hash of a packet stream, but not want to assemble a single buffer
|
||||
with the combined contents of all the packets.)
|
||||
|
||||
This is almost as straight-forward as the first example::
|
||||
|
||||
#include "ns3/hash.h"
|
||||
|
||||
using namespace ns3;
|
||||
|
||||
char * buffer;
|
||||
size_t buffer_size;
|
||||
|
||||
Hasher hasher; // Use default hash function
|
||||
|
||||
for (<every buffer>)
|
||||
{
|
||||
buffer = get_next_buffer ();
|
||||
hasher (buffer, buffer_size);
|
||||
}
|
||||
uint32_t combined_hash = hasher.GetHash32 ();
|
||||
|
||||
By default ``Hasher`` preserves internal state to enable incremental
|
||||
hashing. If you want to reuse a ``Hasher`` object (for example
|
||||
because it's configured with a non-default hash function), but don't
|
||||
want to add to the previously computed hash, you need to ``clear()``
|
||||
first::
|
||||
|
||||
hasher.clear ().GetHash32 (buffer, buffer_size);
|
||||
|
||||
This reinitializes the internal state before hashing the buffer.
|
||||
|
||||
|
||||
Using an Alternative Hash Function
|
||||
**********************************
|
||||
|
||||
The default hash function is murmur3_. FNV1a_ is also available. To specify
|
||||
the hash function explicitly, use this contructor::
|
||||
|
||||
Hasher hasher = Hasher ( Create<Hash::Function::Fnv1a> () );
|
||||
|
||||
|
||||
Adding New Hash Function Implementations
|
||||
****************************************
|
||||
|
||||
To add the hash function ``foo``, follow the ``hash-murmur3.h``/``.cc`` pattern:
|
||||
|
||||
* Create a class declaration (``.h``) and definition (``.cc``) inheriting
|
||||
from ``Hash::Implementation``.
|
||||
* ``include`` the declaration in ``hash.h`` (at the point where
|
||||
``hash-murmur3.h`` is included.
|
||||
* In your own code, instantiate a ``Hasher`` object via the constructor
|
||||
``Hasher (Ptr<Hash::Function::Foo> ())``
|
||||
|
||||
|
||||
If your hash function is a single function, e.g. ``hashf``, you don't
|
||||
even need to create a new class derived from HashImplementation::
|
||||
|
||||
Hasher hasher =
|
||||
Hasher ( Create<Hash::Function::Hash32> (&hashf) );
|
||||
|
||||
For this to compile, your ``hashf`` has to match one of the function pointer
|
||||
signatures::
|
||||
|
||||
typedef uint32_t (*Hash32Function_ptr) (const char *, const size_t);
|
||||
typedef uint64_t (*Hash64Function_ptr) (const char *, const size_t);
|
||||
|
||||
|
||||
Sources for Hash Functions
|
||||
**************************
|
||||
|
||||
Sources for other hash function implementations include:
|
||||
|
||||
* Peter Kankowski: http://www.strchr.com
|
||||
* Arash Partow: http://www.partow.net/programming/hashfunctions/index.html
|
||||
* SMHasher: http://code.google.com/p/smhasher/
|
||||
* Sanmayce: http://www.sanmayce.com/Fastest_Hash/index.html
|
||||
@@ -1,38 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Helpers
|
||||
-------
|
||||
|
||||
The above chapters introduced you to various |ns3| programming concepts such as
|
||||
smart pointers for reference-counted memory management, attributes, namespaces,
|
||||
callbacks, etc. Users who work at this low-level API can interconnect |ns3|
|
||||
objects with fine granulariy. However, a simulation program written entirely
|
||||
using the low-level API would be quite long and tedious to code. For this
|
||||
reason, a separate so-called "helper API" has been overlaid on the core |ns3|
|
||||
API. If you have read the |ns3| tutorial, you will already be familiar with the
|
||||
helper API, since it is the API that new users are typically introduced to
|
||||
first. In this chapter, we introduce the design philosophy of the helper API
|
||||
and contrast it to the low-level API. If you become a heavy user of |ns3|, you
|
||||
will likely move back and forth between these APIs even in the same program.
|
||||
|
||||
The helper API has a few goals:
|
||||
|
||||
#. the rest of ``src/`` has no dependencies on the helper API; anything that can
|
||||
be done with the helper API can be coded also at the low-level API
|
||||
#. **Containers:** Often simulations will need to do a number of identical
|
||||
actions to groups of objects. The helper API makes heavy use of containers of
|
||||
similar objects to which similar or identical operations can be performed.
|
||||
#. The helper API is not generic; it does not strive to maximize code reuse. So,
|
||||
programming constructs such as polymorphism and templates that achieve code
|
||||
reuse are not as prevalent. For instance, there are separate CsmaNetDevice
|
||||
helpers and PointToPointNetDevice helpers but they do not derive from a
|
||||
common NetDevice base class.
|
||||
#. The helper API typically works with stack-allocated (vs. heap-allocated)
|
||||
objects. For some programs, |ns3| users may not need to worry about any low
|
||||
level Object Create or Ptr handling; they can make do with containers of
|
||||
objects and stack-allocated helpers that operate on them.
|
||||
|
||||
The helper API is really all about making |ns3| programs easier to write and
|
||||
read, without taking away the power of the low-level interface. The rest of this
|
||||
chapter provides some examples of the programming conventions of the helper API.
|
||||
@@ -1,143 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
How to write tests
|
||||
------------------
|
||||
|
||||
A primary goal of the ns-3 project is to help users to improve the
|
||||
validity and credibility of their results. There are many elements
|
||||
to obtaining valid models and simulations, and testing is a major
|
||||
component. If you contribute models or examples to ns-3, you may
|
||||
be asked to contribute test code. Models that you contribute will be
|
||||
used for many years by other people, who probably have no idea upon
|
||||
first glance whether the model is correct. The test code that you
|
||||
write for your model will help to avoid future regressions in
|
||||
the output and will aid future users in understanding the verification
|
||||
and bounds of applicability of your models.
|
||||
|
||||
There are many ways to verify the correctness of a model's implementation.
|
||||
In this section,
|
||||
we hope to cover some common cases that can be used as a guide to
|
||||
writing new tests.
|
||||
|
||||
Sample TestSuite skeleton
|
||||
*************************
|
||||
|
||||
When starting from scratch (i.e. not adding a TestCase to an existing
|
||||
TestSuite), these things need to be decided up front:
|
||||
|
||||
* What the test suite will be called
|
||||
* What type of test it will be (Build Verification Test, Unit Test,
|
||||
System Test, or Performance Test)
|
||||
* Where the test code will live (either in an existing ns-3 module or
|
||||
separately in src/test/ directory). You will have to edit the wscript
|
||||
file in that directory to compile your new code, if it is a new file.
|
||||
|
||||
A program called ``src/create-module.py`` is a good starting point.
|
||||
This program can be invoked such as ``create-module.py router`` for
|
||||
a hypothetical new module called ``router``. Once you do this, you
|
||||
will see a ``router`` directory, and a ``test/router-test-suite.cc``
|
||||
test suite. This file can be a starting point for your initial test.
|
||||
This is a working test suite, although the actual tests performed are
|
||||
trivial. Copy it over to your module's test directory, and do a global
|
||||
substitution of "Router" in that file for something pertaining to
|
||||
the model that you want to test. You can also edit things such as a
|
||||
more descriptive test case name.
|
||||
|
||||
You also need to add a block into your wscript to get this test to
|
||||
compile:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
module_test.source = [
|
||||
'test/router-test-suite.cc',
|
||||
]
|
||||
|
||||
Before you actually start making this do useful things, it may help
|
||||
to try to run the skeleton. Make sure that ns-3 has been configured with
|
||||
the "--enable-tests" option. Let's assume that your new test suite
|
||||
is called "router" such as here:
|
||||
|
||||
::
|
||||
|
||||
RouterTestSuite::RouterTestSuite ()
|
||||
: TestSuite ("router", UNIT)
|
||||
|
||||
Try this command:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./test.py -s router
|
||||
|
||||
Output such as below should be produced:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
PASS: TestSuite router
|
||||
1 of 1 tests passed (1 passed, 0 skipped, 0 failed, 0 crashed, 0 valgrind errors)
|
||||
|
||||
See src/lte/test/test-lte-antenna.cc for a worked example.
|
||||
|
||||
Test macros
|
||||
***********
|
||||
|
||||
There are a number of macros available for checking test program
|
||||
output with expected output. These macros are defined in
|
||||
``src/core/model/test.h``.
|
||||
|
||||
The main set of macros that are used include the following:
|
||||
|
||||
::
|
||||
|
||||
NS_TEST_ASSERT_MSG_EQ(actual, limit, msg)
|
||||
NS_TEST_ASSERT_MSG_NE(actual, limit, msg)
|
||||
NS_TEST_ASSERT_MSG_LT(actual, limit, msg)
|
||||
NS_TEST_ASSERT_MSG_GT(actual, limit, msg)
|
||||
NS_TEST_ASSERT_MSG_EQ_TOL(actual, limit, tol, msg)
|
||||
|
||||
The first argument ``actual`` is the value under test, the second value
|
||||
``limit`` is the expected value (or the value to test against), and the
|
||||
last argument ``msg`` is the error message to print out if the test fails.
|
||||
|
||||
The first four macros above test for equality, inequality, less than, or
|
||||
greater than, respectively. The fifth macro above tests for equality,
|
||||
but within a certain tolerance. This variant is useful when testing
|
||||
floating point numbers for equality against a limit, where you want to
|
||||
avoid a test failure due to rounding errors.
|
||||
|
||||
Finally, there are variants of the above where the keyword ``ASSERT``
|
||||
is replaced by ``EXPECT``. These variants are designed specially for
|
||||
use in methods (especially callbacks) returning void. Reserve their
|
||||
use for callbacks that you use in your test programs; otherwise,
|
||||
use the ``ASSERT`` variants.
|
||||
|
||||
How to add an example program to the test suite
|
||||
***********************************************
|
||||
|
||||
One can "smoke test" that examples compile and run successfully
|
||||
to completion (without memory leaks) using the ``examples-to-run.py``
|
||||
script located in your module's test directory. Briefly, by including
|
||||
an instance of this file in your test directory, you can cause the
|
||||
test runner to execute the examples listed. It is usually best to make
|
||||
sure that you select examples that have reasonably short run times so as
|
||||
to not bog down the tests. See the example in ``src/lte/test/``
|
||||
directory.
|
||||
|
||||
Testing for boolean outcomes
|
||||
****************************
|
||||
|
||||
Testing outcomes when randomness is involved
|
||||
********************************************
|
||||
|
||||
Testing output data against a known distribution
|
||||
************************************************
|
||||
|
||||
Providing non-trivial input vectors of data
|
||||
*******************************************
|
||||
|
||||
Storing and referencing non-trivial output data
|
||||
***********************************************
|
||||
|
||||
Presenting your output test data
|
||||
********************************
|
||||
|
||||
@@ -1,43 +0,0 @@
|
||||
.. only:: html or latex
|
||||
|
||||
ns-3 Manual
|
||||
===========
|
||||
|
||||
This is the *ns-3 Manual*. Primary documentation for the ns-3 project is
|
||||
available in five forms:
|
||||
|
||||
* `ns-3 Doxygen <http://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
|
||||
* Tutorial, Manual *(this document)*, and Model Library for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <http://www.nsnam.org/ns-3-dev/documentation/>`_
|
||||
* `ns-3 wiki <http://www.nsnam.org/wiki/Main_Page>`_
|
||||
|
||||
Contents
|
||||
--------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
organization
|
||||
random-variables
|
||||
hash-functions
|
||||
events
|
||||
callbacks
|
||||
object-model
|
||||
attributes
|
||||
object-names
|
||||
logging
|
||||
tracing
|
||||
data-collection
|
||||
statistics
|
||||
realtime
|
||||
helpers
|
||||
gnuplot
|
||||
python
|
||||
tests
|
||||
support
|
||||
|
||||
Source
|
||||
------
|
||||
|
||||
This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the
|
||||
``doc/manual`` directory of ns-3's source code.
|
||||
|
||||
@@ -1,429 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
.. heading hierarchy:
|
||||
------------- Chapter
|
||||
************* Section (#.#)
|
||||
============= Subsection (#.#.#)
|
||||
############# Paragraph (no number)
|
||||
|
||||
Logging
|
||||
-------
|
||||
|
||||
The |ns3| logging facility can be used to monitor or debug the progress
|
||||
of simulation programs. Logging output can be enabled by program statements
|
||||
in your ``main()`` program or by the use of the ``NS_LOG`` environment variable.
|
||||
|
||||
Logging statements are not compiled into optimized builds of |ns3|. To use
|
||||
logging, one must build the (default) debug build of |ns3|.
|
||||
|
||||
The project makes no guarantee about whether logging output will remain
|
||||
the same over time. Users are cautioned against building simulation output
|
||||
frameworks on top of logging code, as the output and the way the output
|
||||
is enabled may change over time.
|
||||
|
||||
Overview
|
||||
********
|
||||
|
||||
|ns3| logging statements are typically used to log various program
|
||||
execution events, such as the occurrence of simulation events or the
|
||||
use of a particular function.
|
||||
|
||||
For example, this code snippet is from ``Ipv4L3Protocol::IsDestinationAddress()``::
|
||||
|
||||
if (address == iaddr.GetBroadcast ())
|
||||
{
|
||||
NS_LOG_LOGIC ("For me (interface broadcast address)");
|
||||
return true;
|
||||
}
|
||||
|
||||
If logging has been enabled for the ``Ipv4L3Protocol`` component at a severity
|
||||
of ``LOGIC`` or above (see below about log severity), the statement
|
||||
will be printed out; otherwise, it will be suppressed.
|
||||
|
||||
Enabling Output
|
||||
===============
|
||||
|
||||
There are two ways that users typically control log output. The
|
||||
first is by setting the ``NS_LOG`` environment variable; e.g.:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="*" ./waf --run first
|
||||
|
||||
will run the ``first`` tutorial program with all logging output. (The
|
||||
specifics of the ``NS_LOG`` format will be discussed below.)
|
||||
|
||||
This can be made more granular by selecting individual components:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="Ipv4L3Protocol" ./waf --run first
|
||||
|
||||
The output can be further tailored with prefix options.
|
||||
|
||||
The second way to enable logging is to use explicit statements in your
|
||||
program, such as in the ``first`` tutorial program::
|
||||
|
||||
int
|
||||
main (int argc, char *argv[])
|
||||
{
|
||||
LogComponentEnable ("UdpEchoClientApplication", LOG_LEVEL_INFO);
|
||||
LogComponentEnable ("UdpEchoServerApplication", LOG_LEVEL_INFO);
|
||||
...
|
||||
|
||||
(The meaning of ``LOG_LEVEL_INFO``, and other possible values,
|
||||
will be discussed below.)
|
||||
|
||||
``NS_LOG`` Syntax
|
||||
=================
|
||||
|
||||
The ``NS_LOG`` environment variable contains a list of log components
|
||||
and options. Log components are separated by \`:' characters:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="<log-component>:<log-component>..."
|
||||
|
||||
Options for each log component are given as flags after
|
||||
each log component:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="<log-component>=<option>|<option>...:<log-component>..."
|
||||
|
||||
Options control the severity and level for that component,
|
||||
and whether optional information should be included, such as the
|
||||
simulation time, simulation node, function name, and the symbolic severity.
|
||||
|
||||
Log Components
|
||||
==============
|
||||
|
||||
Generally a log component refers to a single source code ``.cc`` file,
|
||||
and encompasses the entire file.
|
||||
|
||||
Some helpers have special methods to enable the logging of all components
|
||||
in a module, spanning different compilation units, but logically grouped
|
||||
together, such as the |ns3| wifi code::
|
||||
|
||||
WifiHelper wifiHelper;
|
||||
wifiHelper.EnableLogComponents ();
|
||||
|
||||
The ``NS_LOG`` log component wildcard \`*' will enable all components.
|
||||
|
||||
To see what log components are defined, any of these will work:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="print-list" ./waf --run ...
|
||||
|
||||
$ NS_LOG="foo" # a token not matching any log-component
|
||||
|
||||
The first form will print the name and enabled flags for all log components
|
||||
which are linked in; try it with ``scratch-simulator``.
|
||||
The second form prints all registered log components,
|
||||
then exit with an error.
|
||||
|
||||
|
||||
Severity and Level Options
|
||||
==========================
|
||||
|
||||
Individual messages belong to a single "severity class," set by the macro
|
||||
creating the message. In the example above,
|
||||
``NS_LOG_LOGIC(..)`` creates the message in the ``LOG_LOGIC`` severity class.
|
||||
|
||||
The following severity classes are defined as ``enum`` constants:
|
||||
|
||||
================ =====================================
|
||||
Severity Class Meaning
|
||||
================ =====================================
|
||||
``LOG_NONE`` The default, no logging
|
||||
``LOG_ERROR`` Serious error messages only
|
||||
``LOG_WARN`` Warning messages
|
||||
``LOG_DEBUG`` For use in debugging
|
||||
``LOG_INFO`` Informational
|
||||
``LOG_FUNCTION`` Function tracing
|
||||
``LOG_LOGIC`` Control flow tracing within functions
|
||||
================ =====================================
|
||||
|
||||
Typically one wants to see messages at a given severity class *and higher*.
|
||||
This is done by defining inclusive logging "levels":
|
||||
|
||||
====================== ===========================================
|
||||
Level Meaning
|
||||
====================== ===========================================
|
||||
``LOG_LEVEL_ERROR`` Only ``LOG_ERROR`` severity class messages.
|
||||
``LOG_LEVEL_WARN`` ``LOG_WARN`` and above.
|
||||
``LOG_LEVEL_DEBUG`` ``LOG_DEBUG`` and above.
|
||||
``LOG_LEVEL_INFO`` ``LOG_INFO`` and above.
|
||||
``LOG_LEVEL_FUNCTION`` ``LOG_FUNCTION`` and above.
|
||||
``LOG_LEVEL_LOGIC`` ``LOG_LOGIC`` and above.
|
||||
``LOG_LEVEL_ALL`` All severity classes.
|
||||
``LOG_ALL`` Synonym for ``LOG_LEVEL_ALL``
|
||||
====================== ===========================================
|
||||
|
||||
The severity class and level options can be given in the ``NS_LOG``
|
||||
environment variable by these tokens:
|
||||
|
||||
============ =================
|
||||
Class Level
|
||||
============ =================
|
||||
``error`` ``level_error``
|
||||
``warn`` ``level_warn``
|
||||
``debug`` ``level_debug``
|
||||
``info`` ``level_info``
|
||||
``function`` ``level_function``
|
||||
``logic`` ``level_logic``
|
||||
.. | ``level_all``
|
||||
| ``all``
|
||||
| ``*``
|
||||
============ =================
|
||||
|
||||
Using a severity class token enables log messages at that severity only.
|
||||
For example, ``NS_LOG="*=warn"`` won't output messages with severity ``error``.
|
||||
``NS_LOG="*=level_debug"`` will output messages at severity levels
|
||||
``debug`` and above.
|
||||
|
||||
Severity classes and levels can be combined with the \`|' operator:
|
||||
``NS_LOG="*=level_warn|logic"`` will output messages at severity levels
|
||||
``error``, ``warn`` and ``logic``.
|
||||
|
||||
The ``NS_LOG`` severity level wildcard \`*' and ``all``
|
||||
are synonyms for ``level_all``.
|
||||
|
||||
For log components merely mentioned in ``NS_LOG``
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="<log-component>:..."
|
||||
|
||||
the default severity is ``LOG_LEVEL_ALL``.
|
||||
|
||||
|
||||
Prefix Options
|
||||
==============
|
||||
|
||||
A number of prefixes can help identify
|
||||
where and when a message originated, and at what severity.
|
||||
|
||||
The available prefix options (as ``enum`` constants) are
|
||||
|
||||
====================== ===========================================
|
||||
Prefix Symbol Meaning
|
||||
====================== ===========================================
|
||||
``LOG_PREFIX_FUNC`` Prefix the name of the calling function.
|
||||
``LOG_PREFIX_TIME`` Prefix the simulation time.
|
||||
``LOG_PREFIX_NODE`` Prefix the node id.
|
||||
``LOG_PREFIX_LEVEL`` Prefix the severity level.
|
||||
``LOG_PREFIX_ALL`` Enable all prefixes.
|
||||
====================== ===========================================
|
||||
|
||||
The prefix options are described briefly below.
|
||||
|
||||
The options can be given in the ``NS_LOG``
|
||||
environment variable by these tokens:
|
||||
|
||||
================ =========
|
||||
Token Alternate
|
||||
================ =========
|
||||
``prefix_func`` ``func``
|
||||
``prefix_time`` ``time``
|
||||
``prefix_node`` ``node``
|
||||
``prefix_level`` ``level``
|
||||
``prefix_all`` | ``all``
|
||||
| ``*``
|
||||
================ =========
|
||||
|
||||
For log components merely mentioned in ``NS_LOG``
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="<log-component>:..."
|
||||
|
||||
the default prefix options are ``LOG_PREFIX_ALL``.
|
||||
|
||||
Severity Prefix
|
||||
###############
|
||||
|
||||
The severity class of a message can be included with the options
|
||||
``prefix_level`` or ``level``. For example, this value of ``NS_LOG``
|
||||
enables logging for all log components (\`*') and all severity
|
||||
classes (``=all``), and prefixes the message with the severity
|
||||
class (``|prefix_level``).
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="*=all|prefix_level" ./waf --run scratch-simulator
|
||||
Scratch Simulator
|
||||
[ERROR] error message
|
||||
[WARN] warn message
|
||||
[DEBUG] debug message
|
||||
[INFO] info message
|
||||
[FUNCT] function message
|
||||
[LOGIC] logic message
|
||||
|
||||
Time Prefix
|
||||
###########
|
||||
|
||||
The simulation time can be included with the options
|
||||
``prefix_time`` or ``time``. This prints the simulation time in seconds.
|
||||
|
||||
Node Prefix
|
||||
###########
|
||||
|
||||
The simulation node id can be included with the options
|
||||
``prefix_node`` or ``node``.
|
||||
|
||||
Function Prefix
|
||||
###############
|
||||
|
||||
The name of the calling function can be included with the options
|
||||
``prefix_func`` or ``func``.
|
||||
|
||||
|
||||
``NS_LOG`` Wildcards
|
||||
####################
|
||||
|
||||
The log component wildcard \`*' will enable all components. To
|
||||
enable all components at a specific severity level
|
||||
use ``*=<severity>``.
|
||||
|
||||
The severity level option wildcard \`*' is a synonym for ``all``.
|
||||
This must occur before any \`|' characters separating options.
|
||||
To enable all severity classes, use ``<log-component>=*``,
|
||||
or ``<log-component>=*|<options>``.
|
||||
|
||||
The option wildcard \`*' or token ``all`` enables all prefix options,
|
||||
but must occur *after* a \`|' character. To enable a specific
|
||||
severity class or level, and all prefixes, use
|
||||
``<log-component>=<severity>|*``.
|
||||
|
||||
The combined option wildcard ``**`` enables all severities and all prefixes;
|
||||
for example, ``<log-component>=**``.
|
||||
|
||||
The uber-wildcard ``***`` enables all severities and all prefixes
|
||||
for all log components. These are all equivalent:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_LOG="***" ... $ NS_LOG="*=all|*" ... $ NS_LOG="*=*|all" ...
|
||||
$ NS_LOG="*=**" ... $ NS_LOG="*=level_all|*" ... $ NS_LOG="*=*|prefix_all" ...
|
||||
$ NS_LOG="*=*|*" ...
|
||||
|
||||
Be advised: even the trivial ``scratch-simulator`` produces over
|
||||
46K lines of output with ``NS_LOG="***"``!
|
||||
|
||||
|
||||
How to add logging to your code
|
||||
*******************************
|
||||
|
||||
Adding logging to your code is very simple:
|
||||
|
||||
1. Invoke the ``NS_LOG_COMPONENT_DEFINE (...);`` macro
|
||||
inside of ``namespace ns3``.
|
||||
|
||||
Create a unique string identifier (usually based on the name of the file
|
||||
and/or class defined within the file) and register it with a macro call
|
||||
such as follows:
|
||||
|
||||
::
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
NS_LOG_COMPONENT_DEFINE ("Ipv4L3Protocol");
|
||||
...
|
||||
|
||||
This registers ``Ipv4L3Protocol`` as a log component.
|
||||
|
||||
(The macro was carefully written to permit inclusion either within or
|
||||
outside of namespace ``ns3``, and usage will vary across the codebase, but
|
||||
the original intent was to register this *outside* of namespace ``ns3``
|
||||
at file global scope.)
|
||||
|
||||
2. Add logging statements (macro calls) to your functions and function bodies.
|
||||
|
||||
Logging Macros
|
||||
==============
|
||||
|
||||
The logging macros and associated severity levels are
|
||||
|
||||
================ ==========================
|
||||
Severity Class Macro
|
||||
================ ==========================
|
||||
``LOG_NONE`` (none needed)
|
||||
``LOG_ERROR`` ``NS_LOG_ERROR (...);``
|
||||
``LOG_WARN`` ``NS_LOG_WARN (...);``
|
||||
``LOG_DEBUG`` ``NS_LOG_DEBUG (...);``
|
||||
``LOG_INFO`` ``NS_LOG_INFO (...);``
|
||||
``LOG_FUNCTION`` ``NS_LOG_FUNCTION (...);``
|
||||
``LOG_LOGIC`` ``NS_LOG_LOGIC (...);``
|
||||
================ ==========================
|
||||
|
||||
The macros function as output streamers, so anything you can send to
|
||||
``std::cout``, joined by ``<<`` operators, is allowed::
|
||||
|
||||
void MyClass::Check (int value, char * item)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << arg << item);
|
||||
if (arg > 10)
|
||||
{
|
||||
NS_LOG_ERROR ("encountered bad value " << value <<
|
||||
" while checking " << name << "!");
|
||||
}
|
||||
...
|
||||
}
|
||||
|
||||
Note that ``NS_LOG_FUNCTION`` automatically inserts a \`\ :literal:`,\ `'
|
||||
(comma-space) separator between each of its arguments.
|
||||
This simplifies logging of function arguments;
|
||||
just concatenate them with ``<<`` as in the example above.
|
||||
|
||||
Unconditional Logging
|
||||
=====================
|
||||
|
||||
As a convenience, the ``NS_LOG_UNCOND (...);`` macro will always log its
|
||||
arguments, even if the associated log-component is not enabled at any
|
||||
severity. This macro does not use any of the prefix options. Note that
|
||||
logging is only enabled in debug builds; this macro won't produce
|
||||
output in optimized builds.
|
||||
|
||||
|
||||
Guidelines
|
||||
==========
|
||||
|
||||
* Start every class method with ``NS_LOG_FUNCTION (this << args...);``
|
||||
This enables easy function call tracing.
|
||||
|
||||
* Except: don't log operators or explicit copy constructors,
|
||||
since these will cause infinite recursion and stack overflow.
|
||||
|
||||
* For methods without arguments use the same form:
|
||||
``NS_LOG_FUNCTION (this);``
|
||||
|
||||
* For static functions:
|
||||
|
||||
* With arguments use ``NS_LOG_FUNCTION (...);`` as normal.
|
||||
* Without arguments use ``NS_LOG_FUNCTION_NOARGS ();``
|
||||
|
||||
* Use ``NS_LOG_ERROR`` for serious error conditions that probably
|
||||
invalidate the simulation execution.
|
||||
|
||||
* Use ``NS_LOG_WARN`` for unusual conditions that may be correctable.
|
||||
Please give some hints as to the nature of the problem and how
|
||||
it might be corrected.
|
||||
|
||||
* ``NS_LOG_DEBUG`` is usually used in an *ad hoc* way to understand
|
||||
the execution of a model.
|
||||
|
||||
* Use ``NS_LOG_INFO`` for additional information about the execution,
|
||||
such as the size of a data structure when adding/removing from it.
|
||||
|
||||
* Use ``NS_LOG_LOGIC`` to trace important logic branches within a function.
|
||||
|
||||
* Test that your logging changes do not break the code.
|
||||
Run some example programs with all log components turned on (e.g.
|
||||
``NS_LOG="***"``).
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,573 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Creating a new |ns3| model
|
||||
--------------------------
|
||||
|
||||
This chapter walks through the design process of an |ns3| model. In many
|
||||
research cases, users will not be satisfied to merely adapt existing models, but
|
||||
may want to extend the core of the simulator in a novel way. We will use the
|
||||
example of adding an ErrorModel to a simple |ns3| link as a motivating example
|
||||
of how one might approach this problem and proceed through a design and
|
||||
implementation.
|
||||
|
||||
.. note:: Documentation
|
||||
|
||||
Here we focus on the process of creating new models
|
||||
and new modules, and some of the design choices involved.
|
||||
For the sake of clarity, we defer discussion of the
|
||||
*mechanics* of documenting models and source code to the
|
||||
:doc:`Documentation <documentation>` chapter.
|
||||
|
||||
|
||||
Design Approach
|
||||
***************
|
||||
|
||||
Consider how you want it to work; what should it do. Think about these things:
|
||||
|
||||
* *functionality:* What functionality should it have? What attributes or
|
||||
configuration is exposed to the user?
|
||||
* *reusability:* How much should others be able to reuse my design? Can I
|
||||
reuse code from |ns2| to get started? How does a user integrate the model
|
||||
with the rest of another simulation?
|
||||
* *dependencies:* How can I reduce the introduction of outside dependencies on
|
||||
my new code as much as possible (to make it more modular)? For instance,
|
||||
should I avoid any dependence on IPv4 if I want it to also be used by IPv6?
|
||||
Should I avoid any dependency on IP at all?
|
||||
|
||||
Do not be hesitant to contact the `ns-3-users` or `ns-developers` list if you have
|
||||
questions. In particular, it is important to think about the public API of your
|
||||
new model and ask for feedback. It also helps to let others know of your work in
|
||||
case you are interested in collaborators.
|
||||
|
||||
Example: `ErrorModel`
|
||||
+++++++++++++++++++++
|
||||
|
||||
An error model exists in |ns2|. It allows packets to be passed to a stateful
|
||||
object that determines, based on a random variable, whether the packet is
|
||||
corrupted. The caller can then decide what to do with the packet (drop it,
|
||||
etc.).
|
||||
|
||||
The main API of the error model is a function to pass a packet to, and the
|
||||
return value of this function is a boolean that tells the caller whether any
|
||||
corruption occurred. Note that depending on the error model, the packet data
|
||||
buffer may or may not be corrupted. Let's call this function "IsCorrupt()".
|
||||
|
||||
So far, in our design, we have::
|
||||
|
||||
class ErrorModel
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \returns true if the Packet is to be considered as errored/corrupted
|
||||
* \param pkt Packet to apply error model to
|
||||
*/
|
||||
bool IsCorrupt (Ptr<Packet> pkt);
|
||||
};
|
||||
|
||||
Note that we do not pass a const pointer, thereby allowing the function to
|
||||
modify the packet if IsCorrupt() returns true. Not all error models will
|
||||
actually modify the packet; whether or not the packet data buffer is corrupted
|
||||
should be documented.
|
||||
|
||||
We may also want specialized versions of this, such as in |ns2|, so although it
|
||||
is not the only design choice for polymorphism, we assume that we will subclass
|
||||
a base class ErrorModel for specialized classes, such as RateErrorModel,
|
||||
ListErrorModel, etc, such as is done in |ns2|.
|
||||
|
||||
You may be thinking at this point, "Why not make IsCorrupt() a virtual method?".
|
||||
That is one approach; the other is to make the public non-virtual function
|
||||
indirect through a private virtual function (this in C++ is known as the non
|
||||
virtual interface idiom and is adopted in the |ns3| ErrorModel class).
|
||||
|
||||
Next, should this device have any dependencies on IP or other protocols? We do
|
||||
not want to create dependencies on Internet protocols (the error model should be
|
||||
applicable to non-Internet protocols too), so we'll keep that in mind later.
|
||||
|
||||
Another consideration is how objects will include this error model. We envision
|
||||
putting an explicit setter in certain NetDevice implementations, for example.::
|
||||
|
||||
/**
|
||||
* Attach a receive ErrorModel to the PointToPointNetDevice.
|
||||
*
|
||||
* The PointToPointNetDevice may optionally include an ErrorModel in
|
||||
* the packet receive chain.
|
||||
*
|
||||
* @see ErrorModel
|
||||
* @param em Ptr to the ErrorModel.
|
||||
*/
|
||||
void PointToPointNetDevice::SetReceiveErrorModel(Ptr<ErrorModel> em);
|
||||
|
||||
Again, this is not the only choice we have (error models could be aggregated to
|
||||
lots of other objects), but it satisfies our primary use case, which is to allow
|
||||
a user to force errors on otherwise successful packet transmissions, at the
|
||||
NetDevice level.
|
||||
|
||||
After some thinking and looking at existing |ns2| code, here is a sample API of
|
||||
a base class and first subclass that could be posted for initial review::
|
||||
|
||||
class ErrorModel
|
||||
{
|
||||
public:
|
||||
ErrorModel ();
|
||||
virtual ~ErrorModel ();
|
||||
bool IsCorrupt (Ptr<Packet> pkt);
|
||||
void Reset (void);
|
||||
void Enable (void);
|
||||
void Disable (void);
|
||||
bool IsEnabled (void) const;
|
||||
private:
|
||||
virtual bool DoCorrupt (Ptr<Packet> pkt) = 0;
|
||||
virtual void DoReset (void) = 0;
|
||||
};
|
||||
|
||||
enum ErrorUnit
|
||||
{
|
||||
EU_BIT,
|
||||
EU_BYTE,
|
||||
EU_PKT
|
||||
};
|
||||
|
||||
// Determine which packets are errored corresponding to an underlying
|
||||
// random variable distribution, an error rate, and unit for the rate.
|
||||
class RateErrorModel : public ErrorModel
|
||||
{
|
||||
public:
|
||||
RateErrorModel ();
|
||||
virtual ~RateErrorModel ();
|
||||
enum ErrorUnit GetUnit (void) const;
|
||||
void SetUnit (enum ErrorUnit error_unit);
|
||||
double GetRate (void) const;
|
||||
void SetRate (double rate);
|
||||
void SetRandomVariable (const RandomVariable &ranvar);
|
||||
private:
|
||||
virtual bool DoCorrupt (Ptr<Packet> pkt);
|
||||
virtual void DoReset (void);
|
||||
};
|
||||
|
||||
|
||||
Scaffolding
|
||||
***********
|
||||
|
||||
Let's say that you are ready to start implementing; you have a fairly clear
|
||||
picture of what you want to build, and you may have solicited some initial
|
||||
review or suggestions from the list. One way to approach the next step
|
||||
(implementation) is to create scaffolding and fill in the details as the design
|
||||
matures.
|
||||
|
||||
This section walks through many of the steps you should consider to define
|
||||
scaffolding, or a non-functional skeleton of what your model will eventually
|
||||
implement. It is usually good practice to not wait to get these details
|
||||
integrated at the end, but instead to plumb a skeleton of your model into the
|
||||
system early and then add functions later once the API and integration seems
|
||||
about right.
|
||||
|
||||
Note that you will want to modify a few things in the below presentation for
|
||||
your model since if you follow the error model verbatim, the code you produce
|
||||
will collide with the existing error model. The below is just an outline of how
|
||||
ErrorModel was built that you can adapt to other models.
|
||||
|
||||
Review the |ns3| Coding Style Document
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
At this point, you may want to pause and read the |ns3| coding style document,
|
||||
especially if you are considering to contribute your code back to the project.
|
||||
The coding style document is linked off the main project page: `ns-3 coding
|
||||
style <http://www.nsnam.org/developers/contributing-code/coding-style/>`_.
|
||||
|
||||
Decide Where in the Source Tree the Model Should Reside
|
||||
+++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
All of the |ns3| model source code is in the directory ``src/``. You will need
|
||||
to choose which subdirectory it resides in. If it is new model code of some
|
||||
sort, it makes sense to put it into the ``src/`` directory somewhere,
|
||||
particularly for ease of integrating with the build system.
|
||||
|
||||
In the case of the error model, it is very related to the packet class, so it
|
||||
makes sense to implement this in the ``src/network/`` module where |ns3|
|
||||
packets are implemented.
|
||||
|
||||
`waf` and `wscript`
|
||||
+++++++++++++++++++
|
||||
|
||||
|ns3| uses the `Waf <http://www.freehackers.org/~tnagy/waf.html>`_ build system.
|
||||
You will want to integrate your new |ns3| uses the Waf build system. You will
|
||||
want to integrate your new source files into this system. This requires that you
|
||||
add your files to the ``wscript`` file found in each directory.
|
||||
|
||||
Let's start with empty files error-model.h and error-model.cc, and add this to
|
||||
``src/network/wscript``. It is really just a matter of adding the .cc file to the
|
||||
rest of the source files, and the .h file to the list of the header files.
|
||||
|
||||
Now, pop up to the top level directory and type "./test.py". You
|
||||
shouldn't have broken anything by this operation.
|
||||
|
||||
Include Guards
|
||||
++++++++++++++
|
||||
|
||||
Next, let's add some `include guards
|
||||
<http://en.wikipedia.org/wiki/Include_guard>`_ in our header file.::
|
||||
|
||||
#ifndef ERROR_MODEL_H
|
||||
#define ERROR_MODEL_H
|
||||
...
|
||||
#endif
|
||||
|
||||
`namespace ns3`
|
||||
+++++++++++++++
|
||||
|
||||
|ns3| uses the |ns3| `namespace
|
||||
<http://en.wikipedia.org/wiki/Namespace_(computer_science)#Use_in_common_languages>`_
|
||||
to isolate its symbols from other namespaces. Typically, a user will next put
|
||||
an |ns3| namespace block in both the cc and h file.::
|
||||
|
||||
namespace ns3 {
|
||||
...
|
||||
}
|
||||
|
||||
At this point, we have some skeletal files in which we can start defining
|
||||
our new classes. The header file looks like this::
|
||||
|
||||
#ifndef ERROR_MODEL_H
|
||||
#define ERROR_MODEL_H
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
} // namespace ns3
|
||||
#endif
|
||||
|
||||
while the ``error-model.cc`` file simply looks like this::
|
||||
|
||||
#include "error-model.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
} // namespace ns3
|
||||
|
||||
These files should compile since they don't really have any contents. We're now
|
||||
ready to start adding classes.
|
||||
|
||||
Initial Implementation
|
||||
**********************
|
||||
|
||||
At this point, we're still working on some scaffolding, but we can begin to
|
||||
define our classes, with the functionality to be added later.
|
||||
|
||||
Inherit from the `Object` Class?
|
||||
++++++++++++++++++++++++++++++++
|
||||
|
||||
This is an important design step; whether to use class :cpp:class:`Object` as a
|
||||
base class for your new classes.
|
||||
|
||||
As described in the chapter on the |ns3| :ref:`Object-model`, classes that
|
||||
inherit from class :cpp:class:`Object` get special properties:
|
||||
|
||||
* the |ns3| type and attribute system (see :ref:`Attributes`)
|
||||
* an object aggregation system
|
||||
* a smart-pointer reference counting system (class Ptr)
|
||||
|
||||
Classes that derive from class :cpp:class:`ObjectBase`} get the first two
|
||||
properties above, but do not get smart pointers. Classes that derive from class
|
||||
:cpp:class:`RefCountBase` get only the smart-pointer reference counting system.
|
||||
|
||||
In practice, class :cpp:class:`Object` is the variant of the three above that
|
||||
the |ns3| developer will most commonly encounter.
|
||||
|
||||
In our case, we want to make use of the attribute system, and we will be passing
|
||||
instances of this object across the |ns3| public API, so class
|
||||
:cpp:class:`Object` is appropriate for us.
|
||||
|
||||
Initial Classes
|
||||
+++++++++++++++
|
||||
|
||||
One way to proceed is to start by defining the bare minimum functions and see if
|
||||
they will compile. Let's review what all is needed to implement when we derive
|
||||
from class Object.::
|
||||
|
||||
#ifndef ERROR_MODEL_H
|
||||
#define ERROR_MODEL_H
|
||||
|
||||
#include "ns3/object.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
class ErrorModel : public Object
|
||||
{
|
||||
public:
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
ErrorModel ();
|
||||
virtual ~ErrorModel ();
|
||||
};
|
||||
|
||||
class RateErrorModel : public ErrorModel
|
||||
{
|
||||
public:
|
||||
static TypeId GetTypeId (void);
|
||||
|
||||
RateErrorModel ();
|
||||
virtual ~RateErrorModel ();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
A few things to note here. We need to include ``object.h``. The convention in
|
||||
|ns3| is that if the header file is co-located in the same directory, it may be
|
||||
included without any path prefix. Therefore, if we were implementing ErrorModel
|
||||
in ``src/core/model`` directory, we could have just said "``#include "object.h"``".
|
||||
But we are in ``src/network/model``, so we must include it as "``#include
|
||||
"ns3/object.h"``". Note also that this goes outside the namespace declaration.
|
||||
|
||||
Second, each class must implement a static public member function called
|
||||
``GetTypeId (void)``.
|
||||
|
||||
Third, it is a good idea to implement constructors and destructors rather than
|
||||
to let the compiler generate them, and to make the destructor virtual. In C++,
|
||||
note also that copy assignment operator and copy constructors are auto-generated
|
||||
if they are not defined, so if you do not want those, you should implement those
|
||||
as private members. This aspect of C++ is discussed in Scott Meyers' Effective
|
||||
C++ book. item 45.
|
||||
|
||||
Let's now look at some corresponding skeletal implementation code in the .cc
|
||||
file.::
|
||||
|
||||
#include "error-model.h"
|
||||
|
||||
namespace ns3 {
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (ErrorModel);
|
||||
|
||||
TypeId ErrorModel::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::ErrorModel")
|
||||
.SetParent<Object> ()
|
||||
.SetGroupName ("Network")
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
ErrorModel::ErrorModel ()
|
||||
{
|
||||
}
|
||||
|
||||
ErrorModel::~ErrorModel ()
|
||||
{
|
||||
}
|
||||
|
||||
NS_OBJECT_ENSURE_REGISTERED (RateErrorModel);
|
||||
|
||||
TypeId RateErrorModel::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::RateErrorModel")
|
||||
.SetParent<ErrorModel> ()
|
||||
.SetGroupName ("Network")
|
||||
.AddConstructor<RateErrorModel> ()
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
RateErrorModel::RateErrorModel ()
|
||||
{
|
||||
}
|
||||
|
||||
RateErrorModel::~RateErrorModel ()
|
||||
{
|
||||
}
|
||||
|
||||
What is the ``GetTypeId (void)`` function? This function does a few things. It
|
||||
registers a unique string into the TypeId system. It establishes the hierarchy
|
||||
of objects in the attribute system (via ``SetParent``). It also declares that
|
||||
certain objects can be created via the object creation framework
|
||||
(``AddConstructor``).
|
||||
|
||||
The macro ``NS_OBJECT_ENSURE_REGISTERED (classname)`` is needed also once for
|
||||
every class that defines a new GetTypeId method, and it does the actual
|
||||
registration of the class into the system. The :ref:`Object-model` chapter
|
||||
discusses this in more detail.
|
||||
|
||||
Including External Files
|
||||
++++++++++++++++++++++++
|
||||
|
||||
Logging Support
|
||||
+++++++++++++++
|
||||
|
||||
*Here, write a bit about adding |ns3| logging macros. Note that
|
||||
LOG_COMPONENT_DEFINE is done outside the namespace ns3*
|
||||
|
||||
Constructor, Empty Function Prototypes
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Key Variables (Default Values, Attributes)
|
||||
++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Test Program 1
|
||||
++++++++++++++
|
||||
|
||||
Object Framework
|
||||
++++++++++++++++
|
||||
|
||||
Adding a Sample Script
|
||||
**********************
|
||||
|
||||
At this point, one may want to try to take the basic scaffolding defined above
|
||||
and add it into the system. Performing this step now allows one to use a simpler
|
||||
model when plumbing into the system and may also reveal whether any design or
|
||||
API modifications need to be made. Once this is done, we will return to building
|
||||
out the functionality of the ErrorModels themselves.
|
||||
|
||||
Add Basic Support in the Class
|
||||
++++++++++++++++++++++++++++++
|
||||
|
||||
::
|
||||
|
||||
/* point-to-point-net-device.h */
|
||||
class ErrorModel;
|
||||
|
||||
/**
|
||||
* Error model for receive packet events
|
||||
*/
|
||||
Ptr<ErrorModel> m_receiveErrorModel;
|
||||
|
||||
Add Accessor
|
||||
++++++++++++
|
||||
|
||||
::
|
||||
|
||||
void
|
||||
PointToPointNetDevice::SetReceiveErrorModel (Ptr<ErrorModel> em)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << em);
|
||||
m_receiveErrorModel = em;
|
||||
}
|
||||
|
||||
.AddAttribute ("ReceiveErrorModel",
|
||||
"The receiver error model used to simulate packet loss",
|
||||
PointerValue (),
|
||||
MakePointerAccessor (&PointToPointNetDevice::m_receiveErrorModel),
|
||||
MakePointerChecker<ErrorModel> ())
|
||||
|
||||
Plumb Into the System
|
||||
+++++++++++++++++++++
|
||||
|
||||
::
|
||||
|
||||
void PointToPointNetDevice::Receive (Ptr<Packet> packet)
|
||||
{
|
||||
NS_LOG_FUNCTION (this << packet);
|
||||
uint16_t protocol = 0;
|
||||
|
||||
if (m_receiveErrorModel && m_receiveErrorModel->IsCorrupt (packet) )
|
||||
{
|
||||
//
|
||||
// If we have an error model and it indicates that it is time to lose a
|
||||
// corrupted packet, don't forward this packet up, let it go.
|
||||
//
|
||||
m_dropTrace (packet);
|
||||
}
|
||||
else
|
||||
{
|
||||
//
|
||||
// Hit the receive trace hook, strip off the point-to-point protocol header
|
||||
// and forward this packet up the protocol stack.
|
||||
//
|
||||
m_rxTrace (packet);
|
||||
ProcessHeader(packet, protocol);
|
||||
m_rxCallback (this, packet, protocol, GetRemote ());
|
||||
if (!m_promiscCallback.IsNull ())
|
||||
{ m_promiscCallback (this, packet, protocol, GetRemote (),
|
||||
GetAddress (), NetDevice::PACKET_HOST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Create Null Functional Script
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
::
|
||||
|
||||
/* simple-error-model.cc */
|
||||
|
||||
// Error model
|
||||
// We want to add an error model to node 3's NetDevice
|
||||
// We can obtain a handle to the NetDevice via the channel and node
|
||||
// pointers
|
||||
Ptr<PointToPointNetDevice> nd3 = PointToPointTopology::GetNetDevice
|
||||
(n3, channel2);
|
||||
Ptr<ErrorModel> em = Create<ErrorModel> ();
|
||||
nd3->SetReceiveErrorModel (em);
|
||||
|
||||
|
||||
bool
|
||||
ErrorModel::DoCorrupt (Packet& p)
|
||||
{
|
||||
NS_LOG_FUNCTION;
|
||||
NS_LOG_UNCOND("Corrupt!");
|
||||
return false;
|
||||
}
|
||||
|
||||
At this point, we can run the program with our trivial ErrorModel plumbed into
|
||||
the receive path of the PointToPointNetDevice. It prints out the string
|
||||
"Corrupt!" for each packet received at node n3. Next, we return to the error
|
||||
model to add in a subclass that performs more interesting error modeling.
|
||||
|
||||
Add a Subclass
|
||||
**************
|
||||
|
||||
The trivial base class ErrorModel does not do anything interesting, but it
|
||||
provides a useful base class interface (Corrupt () and Reset ()), forwarded to
|
||||
virtual functions that can be subclassed. Let's next consider what we call a
|
||||
BasicErrorModel which is based on the |ns2| ErrorModel class (in
|
||||
``ns-2/queue/errmodel.{cc,h}``).
|
||||
|
||||
What properties do we want this to have, from a user interface perspective? We
|
||||
would like for the user to be able to trivially swap out the type of ErrorModel
|
||||
used in the NetDevice. We would also like the capability to set configurable
|
||||
parameters.
|
||||
|
||||
Here are a few simple requirements we will consider:
|
||||
|
||||
* Ability to set the random variable that governs the losses (default is
|
||||
UniformVariable)
|
||||
* Ability to set the unit (bit, byte, packet, time) of granularity over which
|
||||
errors are applied.
|
||||
* Ability to set the rate of errors (e.g. 10^-3) corresponding to the above unit
|
||||
of granularity.
|
||||
* Ability to enable/disable (default is enabled)
|
||||
|
||||
How to Subclass
|
||||
+++++++++++++++
|
||||
|
||||
We declare BasicErrorModel to be a subclass of ErrorModel as follows,::
|
||||
|
||||
class BasicErrorModel : public ErrorModel
|
||||
{
|
||||
public:
|
||||
static TypeId GetTypeId (void);
|
||||
...
|
||||
private:
|
||||
// Implement base class pure virtual functions
|
||||
virtual bool DoCorrupt (Ptr<Packet> p);
|
||||
virtual bool DoReset (void);
|
||||
...
|
||||
}
|
||||
|
||||
and configure the subclass GetTypeId function by setting a unique TypeId string
|
||||
and setting the Parent to ErrorModel::
|
||||
|
||||
TypeId RateErrorModel::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::RateErrorModel")
|
||||
.SetParent<ErrorModel> ()
|
||||
.SetGroupName ("Network")
|
||||
.AddConstructor<RateErrorModel> ()
|
||||
...
|
||||
|
||||
Build Core Functions and Unit Tests
|
||||
***********************************
|
||||
|
||||
Assert Macros
|
||||
+++++++++++++
|
||||
|
||||
Writing Unit Tests
|
||||
++++++++++++++++++
|
||||
|
||||
@@ -1,384 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Adding a New Module to |ns3|
|
||||
----------------------------
|
||||
|
||||
When you have created a group of related classes, examples, and tests,
|
||||
they can be combined together into an |ns3| module so that they can be
|
||||
used with existing |ns3| modules and by other researchers.
|
||||
|
||||
This chapter walks you through the steps necessary to add a new module
|
||||
to |ns3|.
|
||||
|
||||
.. _Step-0:
|
||||
|
||||
Step 0 - Module Layout
|
||||
**********************
|
||||
|
||||
All modules can be found in the ``src`` directory. Each module can be
|
||||
found in a directory that has the same name as the module. For
|
||||
example, the ``spectrum`` module can be found here: ``src/spectrum``.
|
||||
We'll be quoting from the ``spectrum`` module for illustration.
|
||||
|
||||
A prototypical module has the following directory structure and
|
||||
required files:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
src/
|
||||
module-name/
|
||||
bindings/
|
||||
doc/
|
||||
examples/
|
||||
wscript
|
||||
helper/
|
||||
model/
|
||||
test/
|
||||
examples-to-run.py
|
||||
wscript
|
||||
|
||||
Not all directories will be present in each module.
|
||||
|
||||
Step 1 - Create a Module Skeleton
|
||||
*********************************
|
||||
|
||||
A python program is provided in the source directory that
|
||||
will create a skeleton for a new module. For the purposes
|
||||
of this discussion we will assume that your new module
|
||||
is called ``new-module``. From the ``src`` directory, do the following
|
||||
to create the new module:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./create-module.py new-module
|
||||
|
||||
Next, ``cd`` into ``new-module``; you will find this directory layout:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
$ cd new-module
|
||||
$ ls
|
||||
doc examples helper model test wscript
|
||||
|
||||
In more detail, the ``create-module.py`` script will create the
|
||||
directories as well as initial skeleton ``wscript``, ``.h``, ``.cc``
|
||||
and ``.rst`` files. The complete module with skeleton files looks like this:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
src/
|
||||
new-module/
|
||||
doc/
|
||||
new-module.rst
|
||||
examples/
|
||||
new-module-example.cc
|
||||
wscript
|
||||
helper/
|
||||
new-module-helper.cc
|
||||
new-module-helper.h
|
||||
model/
|
||||
new-module.cc
|
||||
new-module.h
|
||||
test/
|
||||
new-module-test-suite.cc
|
||||
wscript
|
||||
|
||||
(If required the ``bindings/`` directory listed in
|
||||
:ref:`Step-0 <Step-0>` will be created automatically during
|
||||
the build.)
|
||||
|
||||
We next walk through how to customize this module. Informing ``waf``
|
||||
about the files which make up your module is done by editing the two
|
||||
``wscript`` files. We will walk through the main steps in this chapter.
|
||||
|
||||
All |ns3| modules depend on the ``core`` module and usually on
|
||||
other modules. This dependency is specified in the ``wscript`` file
|
||||
(at the top level of the module, not the separate ``wscript`` file
|
||||
in the ``examples`` directory!). In the skeleton ``wscript``
|
||||
the call that will declare your new module to ``waf`` will look
|
||||
like this (before editing):
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def build(bld):
|
||||
module = bld.create_ns3_module('new-module', ['core'])
|
||||
|
||||
Let's assume that ``new-module`` depends on the ``internet``,
|
||||
``mobility``, and ``aodv`` modules. After editing it the ``wscript`` file
|
||||
should look like:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def build(bld):
|
||||
module = bld.create_ns3_module('new-module', ['internet', 'mobility', 'aodv'])
|
||||
|
||||
Note that only first level module dependencies should be listed, which
|
||||
is why we removed ``core``; the ``internet`` module in turn depends on
|
||||
``core``.
|
||||
|
||||
Your module will most likely have model source files. Initial skeletons
|
||||
(which will compile successfully) are created in ``model/new-module.cc``
|
||||
and ``model/new-module.h``.
|
||||
|
||||
If your module will have helper source files, then they will go into
|
||||
the ``helper/`` directory; again, initial skeletons are created
|
||||
in that directory.
|
||||
|
||||
Finally, it is good practice to write tests and examples. These will
|
||||
almost certainly be required for new modules to be accepted into
|
||||
the official |ns3| source tree. A skeleton
|
||||
test suite and test case is created in the ``test/`` directory.
|
||||
The skeleton test suite will contain the below constructor,
|
||||
which declares a new unit test named ``new-module``,
|
||||
with a single test case consisting of the class ``NewModuleTestCase1``::
|
||||
|
||||
NewModuleTestSuite::NewModuleTestSuite ()
|
||||
: TestSuite ("new-module", UNIT)
|
||||
{
|
||||
AddTestCase (new NewModuleTestCase1);
|
||||
}
|
||||
|
||||
Step 3 - Declare Source Files
|
||||
*****************************
|
||||
|
||||
The public header and source code files for your new module
|
||||
should be specified in the ``wscript`` file by modifying it with
|
||||
your text editor.
|
||||
|
||||
As an example, after declaring the ``spectrum`` module,
|
||||
the ``src/spectrum/wscript`` specifies the source code files
|
||||
with the following list:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def build(bld):
|
||||
|
||||
module = bld.create_ns3_module('spectrum', ['internet', 'propagation', 'antenna', 'applications'])
|
||||
|
||||
module.source = [
|
||||
'model/spectrum-model.cc',
|
||||
'model/spectrum-value.cc',
|
||||
.
|
||||
.
|
||||
.
|
||||
'model/microwave-oven-spectrum-value-helper.cc',
|
||||
'helper/spectrum-helper.cc',
|
||||
'helper/adhoc-aloha-noack-ideal-phy-helper.cc',
|
||||
'helper/waveform-generator-helper.cc',
|
||||
'helper/spectrum-analyzer-helper.cc',
|
||||
]
|
||||
|
||||
The objects resulting from compiling these sources will be assembled
|
||||
into a link library, which will be linked to any programs relying on this
|
||||
module.
|
||||
|
||||
But how do such programs learn the public API of our new module? Read on!
|
||||
|
||||
Step 4 - Declare Public Header Files
|
||||
************************************
|
||||
|
||||
The header files defining the public API of your model and helpers
|
||||
also should be specified in the ``wscript`` file.
|
||||
|
||||
Continuing with the ``spectrum`` model illustration,
|
||||
the public header files are specified with the following stanza.
|
||||
(Note that the argument to the ``bld`` function tells
|
||||
``waf`` to install this module's headers with the other |ns3| headers):
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
headers = bld(features='ns3header')
|
||||
|
||||
headers.module = 'spectrum'
|
||||
|
||||
headers.source = [
|
||||
'model/spectrum-model.h',
|
||||
'model/spectrum-value.h',
|
||||
.
|
||||
.
|
||||
.
|
||||
'model/microwave-oven-spectrum-value-helper.h',
|
||||
'helper/spectrum-helper.h',
|
||||
'helper/adhoc-aloha-noack-ideal-phy-helper.h',
|
||||
'helper/waveform-generator-helper.h',
|
||||
'helper/spectrum-analyzer-helper.h',
|
||||
]
|
||||
|
||||
Headers made public in this way will be accessible to users of your model
|
||||
with include statements like
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
#include "ns3/spectrum-model.h"
|
||||
|
||||
Headers used strictly internally in your implementation should not
|
||||
be included here. They are still accessible to your implemenation by
|
||||
include statements like
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
#include "my-module-implementation.h"
|
||||
|
||||
|
||||
Step 5 - Declare Tests
|
||||
**********************
|
||||
|
||||
If your new module has tests, then they must be specified in your
|
||||
``wscript`` file by modifying it with your text editor.
|
||||
|
||||
The ``spectrum`` model tests are specified with the following stanza:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
module_test = bld.create_ns3_module_test_library('spectrum')
|
||||
|
||||
module_test.source = [
|
||||
'test/spectrum-interference-test.cc',
|
||||
'test/spectrum-value-test.cc',
|
||||
]
|
||||
|
||||
See :doc:`Tests <tests>` for more information on how to write test cases.
|
||||
|
||||
Step 6 - Declare Examples
|
||||
*************************
|
||||
|
||||
If your new module has examples, then they must be specified in your
|
||||
``examples/wscript`` file. (The skeleton top-level ``wscript`` will
|
||||
recursively include ``examples/wscript`` only if the examples were
|
||||
enabled at configure time.)
|
||||
|
||||
The ``spectrum`` model defines it's first example in
|
||||
``src/spectrum/examples/wscript`` with
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
def build(bld):
|
||||
obj = bld.create_ns3_program('adhoc-aloha-ideal-phy',
|
||||
['spectrum', 'mobility'])
|
||||
obj.source = 'adhoc-aloha-ideal-phy.cc'
|
||||
|
||||
Note that the second argument to the function ``create_ns3_program()``
|
||||
is the list of modules that the program being created depends on; again,
|
||||
don't forget to include ``new-module`` in the list. It's best practice
|
||||
to list only the direct module dependencies, and let ``waf`` deduce
|
||||
the full dependency tree.
|
||||
|
||||
Occasionally, for clarity, you may want to split the implementation
|
||||
for your example among several source files. In this case, just
|
||||
include those files as additional explicit sources of the example:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
obj = bld.create_ns3_program('new-module-example', [new-module])
|
||||
obj.source = ['new-module-example.cc', 'new-module-example-part.cc']
|
||||
|
||||
Python examples are specified using the following
|
||||
function call. Note that the second argument for the function
|
||||
``register_ns3_script()`` is the list of modules that the Python example
|
||||
depends on:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
bld.register_ns3_script('new-module-example.py', ['new-module'])
|
||||
|
||||
Step 7 - Examples Run as Tests
|
||||
******************************
|
||||
|
||||
In addition to running explicit test code, the test framework
|
||||
can also be instrumented to run full example programs to
|
||||
try to catch regressions in the examples. However, not all examples
|
||||
are suitable for regression tests. The file ``test/examples-to-run.py``
|
||||
controls the invocation of the examples when the test framework runs.
|
||||
|
||||
The ``spectrum`` model examples run by ``test.py`` are specified in
|
||||
``src/spectrum/test/examples-to-run.py`` using the following
|
||||
two lists of C++ and Python examples:
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# A list of C++ examples to run in order to ensure that they remain
|
||||
# buildable and runnable over time. Each tuple in the list contains
|
||||
#
|
||||
# (example_name, do_run, do_valgrind_run).
|
||||
#
|
||||
# See test.py for more information.
|
||||
cpp_examples = [
|
||||
("adhoc-aloha-ideal-phy", "True", "True"),
|
||||
("adhoc-aloha-ideal-phy-with-microwave-oven", "True", "True"),
|
||||
("adhoc-aloha-ideal-phy-matrix-propagation-loss-model", "True", "True"),
|
||||
]
|
||||
|
||||
# A list of Python examples to run in order to ensure that they remain
|
||||
# runnable over time. Each tuple in the list contains
|
||||
#
|
||||
# (example_name, do_run).
|
||||
#
|
||||
# See test.py for more information.
|
||||
python_examples = [
|
||||
("sample-simulator.py", "True"),
|
||||
]
|
||||
|
||||
As indicated in the comment, each entry in the C++ list of examples to run
|
||||
contains the tuple ``(example_name, do_run, do_valgrind_run)``, where
|
||||
|
||||
* ``example_name`` is the executable to be run,
|
||||
* ``do_run`` is a condition under which to run the example, and
|
||||
* ``do_valgrind_run`` is a condition under which to run the example
|
||||
under valgrind. (This is needed because NSC causes illegal instruction
|
||||
crashes with some tests when they are run under valgrind.)
|
||||
|
||||
Note that the two conditions are Python statements that
|
||||
can depend on ``waf`` configuration variables. For example,
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
("tcp-nsc-lfn", "NSC_ENABLED == True", "NSC_ENABLED == False"),
|
||||
|
||||
Each entry in the Python list of examples to run contains the tuple
|
||||
``(example_name, do_run)``, where, as for the C++ examples,
|
||||
|
||||
* ``example_name`` is the Python script to be run, and
|
||||
* ``do_run`` is a condition under which to run the example.
|
||||
|
||||
Again, the condition is a Python statement that can
|
||||
depend on ``waf`` configuration variables. For example,
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
("realtime-udp-echo.py", "ENABLE_REAL_TIME == False"),
|
||||
|
||||
|
||||
Step 8 - Configure and Build
|
||||
****************************
|
||||
|
||||
You can now configure, build and test your module as normal.
|
||||
You must reconfigure the project as a first step so that ``waf``
|
||||
caches the new information in your ``wscript`` files,
|
||||
or else your new module will not be included in the build.
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf configure --enable-examples --enable-tests
|
||||
$ ./waf build
|
||||
$ ./test.py
|
||||
|
||||
Look for your new module's test suite (and example programs,
|
||||
if your module has any enabled) in the test output.
|
||||
|
||||
|
||||
Step 9 - Python Bindings
|
||||
************************
|
||||
|
||||
Adding Python bindings to your module is optional, and the step is
|
||||
commented out by default in the ``create-module.py`` script.
|
||||
|
||||
.. sourcecode:: python
|
||||
|
||||
# bld.ns3_python_bindings()
|
||||
|
||||
If you want to include Python bindings (needed only if you want
|
||||
to write Python ns-3 programs instead of C++ ns-3 programs), you
|
||||
should uncomment the above and install the Python API scanning
|
||||
system (covered elsewhere in this manual) and scan your module to
|
||||
generate new bindings.
|
||||
@@ -1,298 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
.. _Object-model:
|
||||
|
||||
Object model
|
||||
------------
|
||||
|
||||
|ns3| is fundamentally a C++ object system. Objects can be declared and
|
||||
instantiated as usual, per C++ rules. |ns3| also adds some features to
|
||||
traditional C++ objects, as described below, to provide greater functionality
|
||||
and features. This manual chapter is intended to introduce the reader to the
|
||||
|ns3| object model.
|
||||
|
||||
This section describes the C++ class design for |ns3| objects. In brief,
|
||||
several design patterns in use include classic object-oriented design
|
||||
(polymorphic interfaces and implementations), separation of interface and
|
||||
implementation, the non-virtual public interface design pattern, an object
|
||||
aggregation facility, and reference counting for memory management. Those
|
||||
familiar with component models such as COM or Bonobo will recognize elements of
|
||||
the design in the |ns3| object aggregation model, although the |ns3| design is
|
||||
not strictly in accordance with either.
|
||||
|
||||
Object-oriented behavior
|
||||
************************
|
||||
|
||||
C++ objects, in general, provide common object-oriented capabilities
|
||||
(abstraction, encapsulation, inheritance, and polymorphism) that are part
|
||||
of classic object-oriented design. |ns3| objects make use of these
|
||||
properties; for instance::
|
||||
|
||||
class Address
|
||||
{
|
||||
public:
|
||||
Address ();
|
||||
Address (uint8_t type, const uint8_t *buffer, uint8_t len);
|
||||
Address (const Address & address);
|
||||
Address &operator = (const Address &address);
|
||||
...
|
||||
private:
|
||||
uint8_t m_type;
|
||||
uint8_t m_len;
|
||||
...
|
||||
};
|
||||
|
||||
Object base classes
|
||||
*******************
|
||||
|
||||
There are three special base classes used in |ns3|. Classes that inherit
|
||||
from these base classes can instantiate objects with special properties.
|
||||
These base classes are:
|
||||
|
||||
* class :cpp:class:`Object`
|
||||
* class :cpp:class:`ObjectBase`
|
||||
* class :cpp:class:`SimpleRefCount`
|
||||
|
||||
It is not required that |ns3| objects inherit from these class, but
|
||||
those that do get special properties. Classes deriving from
|
||||
class :cpp:class:`Object` get the following properties.
|
||||
|
||||
* the |ns3| type and attribute system (see :ref:`Attributes`)
|
||||
* an object aggregation system
|
||||
* a smart-pointer reference counting system (class Ptr)
|
||||
|
||||
Classes that derive from class :cpp:class:`ObjectBase` get the first two
|
||||
properties above, but do not get smart pointers. Classes that derive from class
|
||||
:cpp:class:`SimpleRefCount`: get only the smart-pointer reference counting
|
||||
system.
|
||||
|
||||
In practice, class :cpp:class:`Object` is the variant of the three above that
|
||||
the |ns3| developer will most commonly encounter.
|
||||
|
||||
.. _Memory-management-and-class-Ptr:
|
||||
|
||||
Memory management and class Ptr
|
||||
*******************************
|
||||
|
||||
Memory management in a C++ program is a complex process, and is often done
|
||||
incorrectly or inconsistently. We have settled on a reference counting design
|
||||
described as follows.
|
||||
|
||||
All objects using reference counting maintain an internal reference count to
|
||||
determine when an object can safely delete itself. Each time that a pointer is
|
||||
obtained to an interface, the object's reference count is incremented by calling
|
||||
``Ref()``. It is the obligation of the user of the pointer to explicitly
|
||||
``Unref()`` the pointer when done. When the reference count falls to zero, the
|
||||
object is deleted.
|
||||
|
||||
* When the client code obtains a pointer from the object itself through object
|
||||
creation, or via GetObject, it does not have to increment the reference count.
|
||||
* When client code obtains a pointer from another source (e.g., copying a
|
||||
pointer) it must call ``Ref()`` to increment the reference count.
|
||||
* All users of the object pointer must call ``Unref()`` to release the
|
||||
reference.
|
||||
|
||||
The burden for calling :cpp:func:`Unref()` is somewhat relieved by the use of
|
||||
the reference counting smart pointer class described below.
|
||||
|
||||
Users using a low-level API who wish to explicitly allocate
|
||||
non-reference-counted objects on the heap, using operator new, are responsible
|
||||
for deleting such objects.
|
||||
|
||||
Reference counting smart pointer (Ptr)
|
||||
++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
Calling ``Ref()`` and ``Unref()`` all the time would be cumbersome, so |ns3|
|
||||
provides a smart pointer class :cpp:class:`Ptr` similar to
|
||||
:cpp:class:`Boost::intrusive_ptr`. This smart-pointer class assumes that the
|
||||
underlying type provides a pair of ``Ref`` and ``Unref`` methods that are
|
||||
expected to increment and decrement the internal refcount of the object
|
||||
instance.
|
||||
|
||||
This implementation allows you to manipulate the smart pointer as if it was a
|
||||
normal pointer: you can compare it with zero, compare it against other pointers,
|
||||
assign zero to it, etc.
|
||||
|
||||
It is possible to extract the raw pointer from this smart pointer with the
|
||||
:cpp:func:`GetPointer` and :cpp:func:`PeekPointer` methods.
|
||||
|
||||
If you want to store a newed object into a smart pointer, we recommend you to
|
||||
use the CreateObject template functions to create the object and store it in a
|
||||
smart pointer to avoid memory leaks. These functions are really small
|
||||
convenience functions and their goal is just to save you a small bit of typing.
|
||||
|
||||
CreateObject and Create
|
||||
***********************
|
||||
|
||||
Objects in C++ may be statically, dynamically, or automatically created. This
|
||||
holds true for |ns3| also, but some objects in the system have some additional
|
||||
frameworks available. Specifically, reference counted objects are usually
|
||||
allocated using a templated Create or CreateObject method, as follows.
|
||||
|
||||
For objects deriving from class :cpp:class:`Object`::
|
||||
|
||||
Ptr<WifiNetDevice> device = CreateObject<WifiNetDevice> ();
|
||||
|
||||
Please do not create such objects using ``operator new``; create them using
|
||||
:cpp:func:`CreateObject()` instead.
|
||||
|
||||
For objects deriving from class :cpp:class:`SimpleRefCount`, or other objects
|
||||
that support usage of the smart pointer class, a templated helper function is
|
||||
available and recommended to be used::
|
||||
|
||||
Ptr<B> b = Create<B> ();
|
||||
|
||||
This is simply a wrapper around operator new that correctly handles the
|
||||
reference counting system.
|
||||
|
||||
In summary, use ``Create<B>`` if B is not an object but just uses reference
|
||||
counting (e.g. :cpp:class:`Packet`), and use ``CreateObject<B>`` if B derives
|
||||
from :cpp:class:`ns3::Object`.
|
||||
|
||||
Aggregation
|
||||
***********
|
||||
|
||||
The |ns3| object aggregation system is motivated in strong part by a recognition
|
||||
that a common use case for |ns2| has been the use of inheritance and
|
||||
polymorphism to extend protocol models. For instance, specialized versions of
|
||||
TCP such as RenoTcpAgent derive from (and override functions from) class
|
||||
TcpAgent.
|
||||
|
||||
However, two problems that have arisen in the |ns2| model are downcasts and
|
||||
"weak base class." Downcasting refers to the procedure of using a base class
|
||||
pointer to an object and querying it at run time to find out type information,
|
||||
used to explicitly cast the pointer to a subclass pointer so that the subclass
|
||||
API can be used. Weak base class refers to the problems that arise when a class
|
||||
cannot be effectively reused (derived from) because it lacks necessary
|
||||
functionality, leading the developer to have to modify the base class and
|
||||
causing proliferation of base class API calls, some of which may not be
|
||||
semantically correct for all subclasses.
|
||||
|
||||
|ns3| is using a version of the query interface design pattern to avoid these
|
||||
problems. This design is based on elements of the `Component Object Model
|
||||
<http://en.wikipedia.org/wiki/Component_Object_Model>`_ and `GNOME Bonobo
|
||||
<http://en.wikipedia.org/wiki/Bonobo_(component_model)>`_ although full
|
||||
binary-level compatibility of replaceable components is not supported and we
|
||||
have tried to simplify the syntax and impact on model developers.
|
||||
|
||||
Examples
|
||||
********
|
||||
|
||||
Aggregation example
|
||||
+++++++++++++++++++
|
||||
|
||||
:cpp:class:`Node` is a good example of the use of aggregation in |ns3|. Note
|
||||
that there are not derived classes of Nodes in |ns3| such as class
|
||||
:cpp:class:`InternetNode`. Instead, components (protocols) are aggregated to a
|
||||
node. Let's look at how some Ipv4 protocols are added to a node.::
|
||||
|
||||
static void
|
||||
AddIpv4Stack(Ptr<Node> node)
|
||||
{
|
||||
Ptr<Ipv4L3Protocol> ipv4 = CreateObject<Ipv4L3Protocol> ();
|
||||
ipv4->SetNode (node);
|
||||
node->AggregateObject (ipv4);
|
||||
Ptr<Ipv4Impl> ipv4Impl = CreateObject<Ipv4Impl> ();
|
||||
ipv4Impl->SetIpv4 (ipv4);
|
||||
node->AggregateObject (ipv4Impl);
|
||||
}
|
||||
|
||||
Note that the Ipv4 protocols are created using :cpp:func:`CreateObject()`.
|
||||
Then, they are aggregated to the node. In this manner, the Node base class does
|
||||
not need to be edited to allow users with a base class Node pointer to access
|
||||
the Ipv4 interface; users may ask the node for a pointer to its Ipv4 interface
|
||||
at runtime. How the user asks the node is described in the next subsection.
|
||||
|
||||
Note that it is a programming error to aggregate more than one object of the
|
||||
same type to an :cpp:class:`ns3::Object`. So, for instance, aggregation is not
|
||||
an option for storing all of the active sockets of a node.
|
||||
|
||||
GetObject example
|
||||
+++++++++++++++++
|
||||
|
||||
GetObject is a type-safe way to achieve a safe downcasting and to allow
|
||||
interfaces to be found on an object.
|
||||
|
||||
Consider a node pointer ``m_node`` that points to a Node object that has an
|
||||
implementation of IPv4 previously aggregated to it. The client code wishes to
|
||||
configure a default route. To do so, it must access an object within the node
|
||||
that has an interface to the IP forwarding configuration. It performs the
|
||||
following::
|
||||
|
||||
Ptr<Ipv4> ipv4 = m_node->GetObject<Ipv4> ();
|
||||
|
||||
If the node in fact does not have an Ipv4 object aggregated to it, then the
|
||||
method will return null. Therefore, it is good practice to check the return
|
||||
value from such a function call. If successful, the user can now use the Ptr to
|
||||
the Ipv4 object that was previously aggregated to the node.
|
||||
|
||||
Another example of how one might use aggregation is to add optional models to
|
||||
objects. For instance, an existing Node object may have an "Energy Model" object
|
||||
aggregated to it at run time (without modifying and recompiling the node class).
|
||||
An existing model (such as a wireless net device) can then later "GetObject" for
|
||||
the energy model and act appropriately if the interface has been either built in
|
||||
to the underlying Node object or aggregated to it at run time. However, other
|
||||
nodes need not know anything about energy models.
|
||||
|
||||
We hope that this mode of programming will require much less need for developers
|
||||
to modify the base classes.
|
||||
|
||||
Object factories
|
||||
****************
|
||||
|
||||
A common use case is to create lots of similarly configured objects. One can
|
||||
repeatedly call :cpp:func:`CreateObject` but there is also a factory design
|
||||
pattern in use in the |ns3| system. It is heavily used in the "helper" API.
|
||||
|
||||
Class :cpp:class:`ObjectFactory` can be used to instantiate objects and to
|
||||
configure the attributes on those objects::
|
||||
|
||||
void SetTypeId (TypeId tid);
|
||||
void Set (std::string name, const AttributeValue &value);
|
||||
Ptr<T> Create (void) const;
|
||||
|
||||
The first method allows one to use the |ns3| TypeId system to specify the type
|
||||
of objects created. The second allows one to set attributes on the objects to be
|
||||
created, and the third allows one to create the objects themselves.
|
||||
|
||||
For example: ::
|
||||
|
||||
ObjectFactory factory;
|
||||
// Make this factory create objects of type FriisPropagationLossModel
|
||||
factory.SetTypeId ("ns3::FriisPropagationLossModel")
|
||||
// Make this factory object change a default value of an attribute, for
|
||||
// subsequently created objects
|
||||
factory.Set ("SystemLoss", DoubleValue (2.0));
|
||||
// Create one such object
|
||||
Ptr<Object> object = factory.Create ();
|
||||
factory.Set ("SystemLoss", DoubleValue (3.0));
|
||||
// Create another object with a different SystemLoss
|
||||
Ptr<Object> object = factory.Create ();
|
||||
|
||||
Downcasting
|
||||
***********
|
||||
|
||||
A question that has arisen several times is, "If I have a base class pointer
|
||||
(Ptr) to an object and I want the derived class pointer, should I downcast (via
|
||||
C++ dynamic cast) to get the derived pointer, or should I use the object
|
||||
aggregation system to :cpp:func:`GetObject\<> ()` to find a Ptr to the interface
|
||||
to the subclass API?"
|
||||
|
||||
The answer to this is that in many situations, both techniques will work.
|
||||
|ns3| provides a templated function for making the syntax of Object
|
||||
dynamic casting much more user friendly::
|
||||
|
||||
template <typename T1, typename T2>
|
||||
Ptr<T1>
|
||||
DynamicCast (Ptr<T2> const&p)
|
||||
{
|
||||
return Ptr<T1> (dynamic_cast<T1 *> (PeekPointer (p)));
|
||||
}
|
||||
|
||||
DynamicCast works when the programmer has a base type pointer and is testing
|
||||
against a subclass pointer. GetObject works when looking for different objects
|
||||
aggregated, but also works with subclasses, in the same way as DynamicCast. If
|
||||
unsure, the programmer should use GetObject, as it works in all cases. If the
|
||||
programmer knows the class hierarchy of the object under consideration, it is
|
||||
more direct to just use DynamicCast.
|
||||
@@ -1,9 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
.. _Object-names:
|
||||
|
||||
Object names
|
||||
------------
|
||||
|
||||
*Placeholder chapter*
|
||||
@@ -1,61 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
|
||||
Organization
|
||||
------------
|
||||
|
||||
This chapter describes the overall |ns3| software organization and the
|
||||
corresponding organization of this manual.
|
||||
|
||||
|ns3| is a discrete-event network simulator in which the simulation core
|
||||
and models are implemented in C++. |ns3| is built as a library which may be
|
||||
statically or dynamically linked to a C++ main program that defines the
|
||||
simulation topology and starts the simulator. |ns3| also exports nearly all
|
||||
of its API to Python, allowing Python programs to import an "ns3" module in
|
||||
much the same way as the |ns3| library is linked by executables in C++.
|
||||
|
||||
.. _software-organization:
|
||||
|
||||
.. figure:: figures/software-organization.*
|
||||
|
||||
Software organization of |ns3|
|
||||
|
||||
The source code for |ns3| is mostly organized in the ``src`` directory and
|
||||
can be described by the diagram in :ref:`software-organization`. We will
|
||||
work our way from the bottom up; in general, modules only have dependencies
|
||||
on modules beneath them in the figure.
|
||||
|
||||
We first describe the core of the simulator; those components that are
|
||||
common across all protocol, hardware, and environmental models.
|
||||
The simulation core is implemented in ``src/core``. Packets are
|
||||
fundamental objects in a network simulator
|
||||
and are implemented in ``src/network``. These two simulation modules by
|
||||
themselves are intended to comprise a generic simulation core that can be
|
||||
used by different kinds of networks, not just Internet-based networks. The
|
||||
above modules of |ns3| are independent of specific network and device
|
||||
models, which are covered in subsequent parts of this manual.
|
||||
|
||||
In addition to the above |ns3| core, we introduce, also in the initial
|
||||
portion of the manual, two other modules that supplement the core C++-based
|
||||
API. |ns3| programs may access
|
||||
all of the API directly or may make use of a so-called *helper API* that
|
||||
provides convenient wrappers or encapsulation of low-level API calls. The
|
||||
fact that |ns3| programs can be written to two APIs (or a combination
|
||||
thereof) is a fundamental aspect of the simulator.
|
||||
We also describe how Python is supported in |ns3| before moving onto
|
||||
specific models of relevance to network simulation.
|
||||
|
||||
The remainder of the manual is focused on documenting the models and
|
||||
supporting capabilities. The next part focuses on two fundamental objects in
|
||||
|ns3|: the ``Node`` and ``NetDevice``. Two special NetDevice types are
|
||||
designed to support network emulation use cases, and emulation is described
|
||||
next. The following chapter is devoted to Internet-related models,
|
||||
including the
|
||||
sockets API used by Internet applications. The next chapter covers
|
||||
applications, and the following chapter describes additional support for
|
||||
simulation, such as animators and statistics.
|
||||
|
||||
The project maintains a separate manual devoted to testing and validation
|
||||
of |ns3| code (see the `ns-3 Testing and Validation manual
|
||||
<http://www.nsnam.org/tutorials.html>`_).
|
||||
@@ -1,315 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: python
|
||||
|
||||
Using Python to Run |ns3|
|
||||
-------------------------
|
||||
|
||||
Python bindings allow the C++ code in |ns3| to be called from Python.
|
||||
|
||||
This chapter shows you how to create a Python script that can run |ns3| and also the process of creating Python bindings for a C++ |ns3| module.
|
||||
|
||||
Introduction
|
||||
************
|
||||
|
||||
The goal of Python bindings for |ns3| are two fold:
|
||||
|
||||
#. Allow the programmer to write complete simulation scripts in Python (http://www.python.org);
|
||||
#. Prototype new models (e.g. routing protocols).
|
||||
|
||||
For the time being, the primary focus of the bindings is the first goal, but the second goal will eventually be supported as well.
|
||||
Python bindings for |ns3| are being developed using a new tool called PyBindGen (http://code.google.com/p/pybindgen).
|
||||
|
||||
An Example Python Script that Runs |ns3|
|
||||
****************************************
|
||||
|
||||
Here is some example code that is written in Python and that runs |ns3|, which is written in C++. This Python example can be found in ``examples/tutorial/first.py``:
|
||||
|
||||
::
|
||||
|
||||
import ns.applications
|
||||
import ns.core
|
||||
import ns.internet
|
||||
import ns.network
|
||||
import ns.point_to_point
|
||||
|
||||
ns.core.LogComponentEnable("UdpEchoClientApplication", ns.core.LOG_LEVEL_INFO)
|
||||
ns.core.LogComponentEnable("UdpEchoServerApplication", ns.core.LOG_LEVEL_INFO)
|
||||
|
||||
nodes = ns.network.NodeContainer()
|
||||
nodes.Create(2)
|
||||
|
||||
pointToPoint = ns.point_to_point.PointToPointHelper()
|
||||
pointToPoint.SetDeviceAttribute("DataRate", ns.core.StringValue("5Mbps"))
|
||||
pointToPoint.SetChannelAttribute("Delay", ns.core.StringValue("2ms"))
|
||||
|
||||
devices = pointToPoint.Install(nodes)
|
||||
|
||||
stack = ns.internet.InternetStackHelper()
|
||||
stack.Install(nodes)
|
||||
|
||||
address = ns.internet.Ipv4AddressHelper()
|
||||
address.SetBase(ns.network.Ipv4Address("10.1.1.0"), ns.network.Ipv4Mask("255.255.255.0"))
|
||||
|
||||
interfaces = address.Assign (devices);
|
||||
|
||||
echoServer = ns.applications.UdpEchoServerHelper(9)
|
||||
|
||||
serverApps = echoServer.Install(nodes.Get(1))
|
||||
serverApps.Start(ns.core.Seconds(1.0))
|
||||
serverApps.Stop(ns.core.Seconds(10.0))
|
||||
|
||||
echoClient = ns.applications.UdpEchoClientHelper(interfaces.GetAddress(1), 9)
|
||||
echoClient.SetAttribute("MaxPackets", ns.core.UintegerValue(1))
|
||||
echoClient.SetAttribute("Interval", ns.core.TimeValue(ns.core.Seconds (1.0)))
|
||||
echoClient.SetAttribute("PacketSize", ns.core.UintegerValue(1024))
|
||||
|
||||
clientApps = echoClient.Install(nodes.Get(0))
|
||||
clientApps.Start(ns.core.Seconds(2.0))
|
||||
clientApps.Stop(ns.core.Seconds(10.0))
|
||||
|
||||
ns.core.Simulator.Run()
|
||||
ns.core.Simulator.Destroy()
|
||||
|
||||
Running Python Scripts
|
||||
**********************
|
||||
|
||||
waf contains some options that automatically update the python path to find the ns3 module. To run example programs, there are two ways to use waf to take care of this. One is to run a waf shell; e.g.:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --shell
|
||||
$ python examples/wireless/mixed-wireless.py
|
||||
|
||||
and the other is to use the --pyrun option to waf:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --pyrun examples/wireless/mixed-wireless.py
|
||||
|
||||
To run a python script under the C debugger:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --shell
|
||||
$ gdb --args python examples/wireless/mixed-wireless.py
|
||||
|
||||
To run your own Python script that calls |ns3| and that has this path, ``/path/to/your/example/my-script.py``, do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --shell
|
||||
$ python /path/to/your/example/my-script.py
|
||||
|
||||
Caveats
|
||||
*******
|
||||
|
||||
Python bindings for |ns3| are a work in progress, and some limitations are known by developers. Some of these limitations (not all) are listed here.
|
||||
|
||||
Incomplete Coverage
|
||||
+++++++++++++++++++
|
||||
|
||||
First of all, keep in mind that not 100% of the API is supported in Python. Some of the reasons are:
|
||||
|
||||
#. some of the APIs involve pointers, which require knowledge of what kind of memory passing semantics (who owns what memory). Such knowledge is not part of the function signatures, and is either documented or sometimes not even documented. Annotations are needed to bind those functions;
|
||||
#. Sometimes a unusual fundamental data type or C++ construct is used which is not yet supported by PyBindGen;
|
||||
#. GCC-XML does not report template based classes unless they are instantiated.
|
||||
|
||||
Most of the missing APIs can be wrapped, given enough time, patience, and expertise, and will likely be wrapped if bug reports are submitted. However, don't file a bug report saying "bindings are incomplete", because we do not have manpower to complete 100% of the bindings.
|
||||
|
||||
Conversion Constructors
|
||||
+++++++++++++++++++++++
|
||||
|
||||
`Conversion constructors <http://publib.boulder.ibm.com/infocenter/compbgpl/v9v111/topic/com.ibm.xlcpp9.bg.doc/language_ref/cplr384.htm>`_ are not fully supported yet by PyBindGen, and they always act as explicit constructors when translating an API into Python. For example, in C++ you can do this:
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
Ipv4AddressHelper ipAddrs;
|
||||
ipAddrs.SetBase ("192.168.0.0", "255.255.255.0");
|
||||
ipAddrs.Assign (backboneDevices);
|
||||
|
||||
In Python, for the time being you have to do:
|
||||
|
||||
::
|
||||
|
||||
ipAddrs = ns3.Ipv4AddressHelper()
|
||||
ipAddrs.SetBase(ns3.Ipv4Address("192.168.0.0"), ns3.Ipv4Mask("255.255.255.0"))
|
||||
ipAddrs.Assign(backboneDevices)
|
||||
|
||||
CommandLine
|
||||
+++++++++++
|
||||
|
||||
:cpp:func:`CommandLine::AddValue` works differently in Python than it does in |ns3|. In Python, the first parameter is a string that represents the command-line option name. When the option is set, an attribute with the same name as the option name is set on the :cpp:func:`CommandLine` object. Example:
|
||||
|
||||
::
|
||||
|
||||
NUM_NODES_SIDE_DEFAULT = 3
|
||||
|
||||
cmd = ns3.CommandLine()
|
||||
|
||||
cmd.NumNodesSide = None
|
||||
cmd.AddValue("NumNodesSide", "Grid side number of nodes (total number of nodes will be this number squared)")
|
||||
|
||||
cmd.Parse(argv)
|
||||
|
||||
[...]
|
||||
|
||||
if cmd.NumNodesSide is None:
|
||||
num_nodes_side = NUM_NODES_SIDE_DEFAULT
|
||||
else:
|
||||
num_nodes_side = int(cmd.NumNodesSide)
|
||||
|
||||
Tracing
|
||||
+++++++
|
||||
|
||||
Callback based tracing is not yet properly supported for Python, as new |ns3| API needs to be provided for this to be supported.
|
||||
|
||||
Pcap file writing is supported via the normal API.
|
||||
|
||||
Ascii tracing is supported since |ns3|.4 via the normal C++ API translated to Python. However, ascii tracing requires the creation of an ostream object to pass into the ascii tracing methods. In Python, the C++ std::ofstream has been minimally wrapped to allow this. For example:
|
||||
|
||||
::
|
||||
|
||||
ascii = ns3.ofstream("wifi-ap.tr") # create the file
|
||||
ns3.YansWifiPhyHelper.EnableAsciiAll(ascii)
|
||||
ns3.Simulator.Run()
|
||||
ns3.Simulator.Destroy()
|
||||
ascii.close() # close the file
|
||||
|
||||
There is one caveat: you must not allow the file object to be garbage collected while |ns3| is still using it. That means that the 'ascii' variable above must not be allowed to go out of scope or else the program will crash.
|
||||
|
||||
Cygwin limitation
|
||||
+++++++++++++++++
|
||||
|
||||
Python bindings do not work on Cygwin. This is due to a gccxml bug.
|
||||
|
||||
You might get away with it by re-scanning API definitions from within the
|
||||
cygwin environment (./waf --python-scan). However the most likely solution
|
||||
will probably have to be that we disable python bindings in CygWin.
|
||||
|
||||
If you really care about Python bindings on Windows, try building with mingw and native
|
||||
python instead. Or else, to build without python bindings, disable python bindings in the configuration stage:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf configure --disable-python
|
||||
|
||||
Working with Python Bindings
|
||||
****************************
|
||||
|
||||
There are currently two kinds of Python bindings in |ns3|:
|
||||
|
||||
#. Monolithic bindings contain API definitions for all of the modules and can be found in a single directory, ``bindings/python``.
|
||||
#. Modular bindings contain API definitions for a single module and can be found in each module's ``bindings`` directory.
|
||||
|
||||
Python Bindings Workflow
|
||||
++++++++++++++++++++++++
|
||||
|
||||
The process by which Python bindings are handled is the following:
|
||||
|
||||
#. Periodically a developer uses a GCC-XML (http://www.gccxml.org) based API scanning script, which saves the scanned API definition as ``bindings/python/ns3_module_*.py`` files or as Python files in each modules' ``bindings`` directory. These files are kept under version control in the main |ns3| repository;
|
||||
#. Other developers clone the repository and use the already scanned API definitions;
|
||||
#. When configuring |ns3|, pybindgen will be automatically downloaded if not already installed. Released |ns3| tarballs will ship a copy of pybindgen.
|
||||
|
||||
If something goes wrong with compiling Python bindings and you just want to ignore them and move on with C++, you can disable Python with:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --disable-python
|
||||
|
||||
Instructions for Handling New Files or Changed API's
|
||||
****************************************************
|
||||
|
||||
So you have been changing existing |ns3| APIs and Python bindings no longer compile? Do not despair, you can rescan the bindings to create new bindings that reflect the changes to the |ns3| API.
|
||||
|
||||
Depending on if you are using monolithic or modular bindings, see the discussions below to learn how to rescan your Python bindings.
|
||||
|
||||
Monolithic Python Bindings
|
||||
**************************
|
||||
|
||||
Scanning the Monolithic Python Bindings
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To scan the monolithic Python bindings do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --python-scan
|
||||
|
||||
Organization of the Monolithic Python Bindings
|
||||
++++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
The monolithic Python API definitions are organized as follows. For each |ns3| module <name>, the file ``bindings/python/ns3_module_<name>.py`` describes its API. Each of those files have 3 toplevel functions:
|
||||
|
||||
#. :py:func:`def register_types(module)`: this function takes care of registering new types (e.g. C++ classes, enums) that are defined in tha module;
|
||||
#. :py:func:`def register_methods(module)`: this function calls, for each class <name>, another function register_methods_Ns3<name>(module). These latter functions add method definitions for each class;
|
||||
#. :py:func:`def register_functions(module)`: this function registers |ns3| functions that belong to that module.
|
||||
|
||||
Modular Python Bindings
|
||||
***********************
|
||||
|
||||
Overview
|
||||
++++++++
|
||||
|
||||
Since ns 3.11, the modular bindings are being added, in parallel to the old monolithic bindings.
|
||||
|
||||
The new python bindings are generated into an 'ns' namespace, instead of 'ns3' for the old bindings. Example:
|
||||
|
||||
::
|
||||
|
||||
from ns.network import Node
|
||||
n1 = Node()
|
||||
|
||||
With modular Python bindings:
|
||||
|
||||
#. There is one separate Python extension module for each |ns3| module;
|
||||
#. Scanning API definitions (apidefs) is done on a per ns- module basis;
|
||||
#. Each module's apidefs files are stored in a 'bindings' subdirectory of the module directory;
|
||||
|
||||
Scanning the Modular Python Bindings
|
||||
+++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To scan the modular Python bindings for the core module, for example, do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --apiscan=core
|
||||
|
||||
To scan the modular Python bindings for all of the modules, do the following:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --apiscan=all
|
||||
|
||||
Creating a New Module
|
||||
+++++++++++++++++++++
|
||||
|
||||
If you are adding a new module, Python bindings will continue to compile but will not cover the new module.
|
||||
|
||||
To cover a new module, you have to create a ``bindings/python/ns3_module_<name>.py`` file, similar to the what is described in the previous sections, and register it in the variable :cpp:func:`LOCAL_MODULES` in ``bindings/python/ns3modulegen.py``
|
||||
|
||||
Adding Modular Bindings To A Existing Module
|
||||
++++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
To add support for modular bindings to an existing |ns3| module, simply add the following line to its wscript build() function:
|
||||
|
||||
::
|
||||
|
||||
bld.ns3_python_bindings()
|
||||
|
||||
Organization of the Modular Python Bindings
|
||||
+++++++++++++++++++++++++++++++++++++++++++
|
||||
|
||||
The ``src/<module>/bindings`` directory may contain the following files, some of them optional:
|
||||
|
||||
* ``callbacks_list.py``: this is a scanned file, DO NOT TOUCH. Contains a list of Callback<...> template instances found in the scanned headers;
|
||||
* ``modulegen__gcc_LP64.py``: this is a scanned file, DO NOT TOUCH. Scanned API definitions for the GCC, LP64 architecture (64-bit)
|
||||
* ``modulegen__gcc_ILP32.py``: this is a scanned file, DO NOT TOUCH. Scanned API definitions for the GCC, ILP32 architecture (32-bit)
|
||||
* ``modulegen_customizations.py``: you may optionally add this file in order to customize the pybindgen code generation
|
||||
* ``scan-header.h``: you may optionally add this file to customize what header file is scanned for the module. Basically this file is scanned instead of ns3/<module>-module.h. Typically, the first statement is #include "ns3/<module>-module.h", plus some other stuff to force template instantiations;
|
||||
* ``module_helpers.cc``: you may add additional files, such as this, to be linked to python extension module, but they have to be registered in the wscript. Look at src/core/wscript for an example of how to do so;
|
||||
* ``<module>.py``: if this file exists, it becomes the "frontend" python module for the ns3 module, and the extension module (.so file) becomes _<module>.so instead of <module>.so. The <module>.py file has to import all symbols from the module _<module> (this is more tricky than it sounds, see src/core/bindings/core.py for an example), and then can add some additional pure-python definitions.
|
||||
|
||||
More Information for Developers
|
||||
*******************************
|
||||
|
||||
If you are a developer and need more information on |ns3|'s Python bindings, please see the `Python Bindings wiki page <http://www.nsnam.org/wiki/NS-3_Python_Bindings>`_.
|
||||
@@ -1,382 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
Random Variables
|
||||
----------------
|
||||
|
||||
|ns3| contains a built-in pseudo-random number generator (PRNG). It is important
|
||||
for serious users of the simulator to understand the functionality,
|
||||
configuration, and usage of this PRNG, and to decide whether it is sufficient
|
||||
for his or her research use.
|
||||
|
||||
Quick Overview
|
||||
**************
|
||||
|
||||
|ns3| random numbers are provided via instances of
|
||||
:cpp:class:`ns3::RandomVariableStream`.
|
||||
|
||||
* by default, |ns3| simulations use a fixed seed; if there is any randomness in
|
||||
the simulation, each run of the program will yield identical results unless
|
||||
the seed and/or run number is changed.
|
||||
|
||||
* in *ns-3.3* and earlier, |ns3| simulations used a random seed by default; this
|
||||
marks a change in policy starting with *ns-3.4*.
|
||||
|
||||
* in *ns-3.14* and earlier, |ns3| simulations used a different wrapper class
|
||||
called :cpp:class:`ns3::RandomVariable`. As of *ns-3.15*, this class has been
|
||||
replaced by :cpp:class:`ns3::RandomVariableStream`; the underlying pseudo-random
|
||||
number generator has not changed.
|
||||
|
||||
* to obtain randomness across multiple simulation runs, you must either set the
|
||||
seed differently or set the run number differently. To set a seed, call
|
||||
:cpp:func:`ns3::RngSeedManager::SetSeed` at the beginning of the program; to set
|
||||
a run number with the same seed, call :cpp:func:`ns3::RngSeedManager::SetRun` at
|
||||
the beginning of the program; see :ref:`seeding-and-independent-replications`.
|
||||
|
||||
* each RandomVariableStream used in |ns3| has a virtual random number generator
|
||||
associated with it; all random variables use either a fixed or random seed
|
||||
based on the use of the global seed (previous bullet);
|
||||
|
||||
* if you intend to perform multiple runs of the same scenario, with different
|
||||
random numbers, please be sure to read the section on how to perform
|
||||
independent replications: :ref:`seeding-and-independent-replications`.
|
||||
|
||||
Read further for more explanation about the random number facility for |ns3|.
|
||||
|
||||
Background
|
||||
**********
|
||||
|
||||
Simulations use a lot of random numbers; one study
|
||||
found that most network simulations spend as much as 50%
|
||||
of the CPU generating random numbers. Simulation users need
|
||||
to be concerned with the quality of the (pseudo) random numbers and
|
||||
the independence between different streams of random numbers.
|
||||
|
||||
Users need to be concerned with a few issues, such as:
|
||||
|
||||
* the seeding of the random number generator and whether a
|
||||
simulation outcome is deterministic or not,
|
||||
* how to acquire different streams of random numbers that are
|
||||
independent from one another, and
|
||||
* how long it takes for streams to cycle
|
||||
|
||||
We will introduce a few terms here: a RNG provides a long sequence
|
||||
of (pseudo) random numbers.
|
||||
The length of this sequence is called the *cycle length*
|
||||
or *period*, after which the RNG will repeat itself.
|
||||
This sequence can
|
||||
be partitioned into disjoint *streams*. A stream of a
|
||||
RNG is a contiguous subset or block of the RNG sequence.
|
||||
For instance, if the
|
||||
RNG period is of length N, and two streams are provided from this
|
||||
RNG, then
|
||||
the first stream might use the first N/2 values and the second
|
||||
stream might produce the second N/2 values. An important property
|
||||
here is that the two streams are uncorrelated. Likewise, each
|
||||
stream can be partitioned disjointedly to a number of
|
||||
uncorrelated *substreams*. The underlying RNG hopefully
|
||||
produces a pseudo-random sequence of numbers with a very long
|
||||
cycle length, and partitions this into streams and substreams in an
|
||||
efficient manner.
|
||||
|
||||
|ns3| uses the same underlying random number generator as does |ns2|: the
|
||||
MRG32k3a generator from Pierre L'Ecuyer. A detailed description can be found in
|
||||
http://www.iro.umontreal.ca/~lecuyer/myftp/papers/streams00.pdf. The MRG32k3a
|
||||
generator provides :math:`1.8x10^{19}` independent streams of random numbers,
|
||||
each of which consists of :math:`2.3x10^{15}` substreams. Each substream has a
|
||||
period (*i.e.*, the number of random numbers before overlap) of
|
||||
:math:`7.6x10^{22}`. The period of the entire generator is :math:`3.1x10^{57}`.
|
||||
|
||||
|
||||
Class :cpp:class:`ns3::RandomVariableStream` is the public interface to this
|
||||
underlying random number generator. When users create new random variables
|
||||
(such as :cpp:class:`ns3::UniformRandomVariable`,
|
||||
:cpp:class:`ns3::ExponentialRandomVariable`, etc.), they create an object that uses
|
||||
one of the distinct, independent streams of the random number generator.
|
||||
Therefore, each object of type :cpp:class:`ns3::RandomVariableStream` has,
|
||||
conceptually, its own "virtual" RNG. Furthermore, each
|
||||
:cpp:class:`ns3::RandomVariableStream` can be configured to use one of the set of
|
||||
substreams drawn from the main stream.
|
||||
|
||||
An alternate implementation would be to allow each RandomVariable to have its
|
||||
own (differently seeded) RNG. However, we cannot guarantee as strongly that the
|
||||
different sequences would be uncorrelated in such a case; hence, we prefer to
|
||||
use a single RNG and streams and substreams from it.
|
||||
|
||||
.. _seeding-and-independent-replications:
|
||||
|
||||
Creating random variables
|
||||
*************************
|
||||
|
||||
|ns3| supports a number of random variable objects from the base class
|
||||
:cpp:class:`RandomVariableStream`. These objects derive from
|
||||
:cpp:class:`ns3::Object` and are handled by smart pointers.
|
||||
|
||||
The correct way to create these objects is to use the templated
|
||||
`CreateObject<>` method, such as:
|
||||
|
||||
::
|
||||
|
||||
Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
|
||||
|
||||
then you can access values by calling methods on the object such as:
|
||||
|
||||
::
|
||||
|
||||
myRandomNo = x->GetInteger ();
|
||||
|
||||
|
||||
If you try to instead do something like this:
|
||||
|
||||
::
|
||||
|
||||
myRandomNo = UniformRandomVariable().GetInteger ();
|
||||
|
||||
your program will encounter a segmentation fault, because the implementation
|
||||
relies on some attribute construction that occurs only when `CreateObject`
|
||||
is called.
|
||||
|
||||
Much of the rest of this chapter now discusses the properties of the
|
||||
stream of pseudo-random numbers generated from such objects, and how to
|
||||
control the seeding of such objects.
|
||||
|
||||
Seeding and independent replications
|
||||
************************************
|
||||
|
||||
|ns3| simulations can be configured to produce deterministic or random results.
|
||||
If the |ns3| simulation is configured to use a fixed, deterministic seed with
|
||||
the same run number, it should give the same output each time it is run.
|
||||
|
||||
By default, |ns3| simulations use a fixed seed and run number. These values
|
||||
are stored in two :cpp:class:`ns3::GlobalValue` instances: ``g_rngSeed`` and
|
||||
``g_rngRun``.
|
||||
|
||||
A typical use case is to run a simulation as a sequence of independent trials,
|
||||
so as to compute statistics on a large number of independent runs. The user can
|
||||
either change the global seed and rerun the simulation, or can advance the
|
||||
substream state of the RNG, which is referred to as incrementing the run number.
|
||||
|
||||
A class :cpp:class:`ns3::RngSeedManager` provides an API to control the seeding and
|
||||
run number behavior. This seeding and substream state setting must be called
|
||||
before any random variables are created; e.g::
|
||||
|
||||
RngSeedManager::SetSeed (3); // Changes seed from default of 1 to 3
|
||||
RngSeedManager::SetRun (7); // Changes run number from default of 1 to 7
|
||||
// Now, create random variables
|
||||
Ptr<UniformRandomVariable> x = CreateObject<UniformRandomVariable> ();
|
||||
Ptr<ExponentialRandomVariable> y = CreateObject<ExponentialRandomVarlable> ();
|
||||
...
|
||||
|
||||
Which is better, setting a new seed or advancing the substream state? There is
|
||||
no guarantee that the streams produced by two random seeds will not overlap.
|
||||
The only way to guarantee that two streams do not overlap is to use the
|
||||
substream capability provided by the RNG implementation. *Therefore, use the
|
||||
substream capability to produce multiple independent runs of the same
|
||||
simulation.* In other words, the more statistically rigorous way to configure
|
||||
multiple independent replications is to use a fixed seed and to advance the run
|
||||
number. This implementation allows for a maximum of :math:`2.3x10^{15}`
|
||||
independent replications using the substreams.
|
||||
|
||||
For ease of use, it is not necessary to control the seed and run number from
|
||||
within the program; the user can set the ``NS_GLOBAL_VALUE`` environment
|
||||
variable as follows:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ NS_GLOBAL_VALUE="RngRun=3" ./waf --run program-name
|
||||
|
||||
Another way to control this is by passing a command-line argument; since this is
|
||||
an |ns3| GlobalValue instance, it is equivalently done such as follows:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --command-template="%s --RngRun=3" --run program-name
|
||||
|
||||
or, if you are running programs directly outside of waf:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./build/optimized/scratch/program-name --RngRun=3
|
||||
|
||||
The above command-line variants make it easy to run lots of different
|
||||
runs from a shell script by just passing a different RngRun index.
|
||||
|
||||
Class RandomVariableStream
|
||||
**************************
|
||||
|
||||
All random variables should derive from class :cpp:class:`RandomVariable`. This
|
||||
base class provides a few methods for globally configuring the behavior
|
||||
of the random number generator. Derived classes provide API for drawing random
|
||||
variates from the particular distribution being supported.
|
||||
|
||||
Each RandomVariableStream created in the simulation is given a generator that is a
|
||||
new RNGStream from the underlying PRNG. Used in this manner, the L'Ecuyer
|
||||
implementation allows for a maximum of :math:`1.8x10^19` random variables. Each
|
||||
random variable in a single replication can produce up to :math:`7.6x10^22`
|
||||
random numbers before overlapping.
|
||||
|
||||
Base class public API
|
||||
*********************
|
||||
|
||||
Below are excerpted a few public methods of class :cpp:class:`RandomVariableStream`
|
||||
that access the next value in the substream.
|
||||
|
||||
::
|
||||
|
||||
/**
|
||||
* \brief Returns a random double from the underlying distribution
|
||||
* \return A floating point random value
|
||||
*/
|
||||
double GetValue (void) const;
|
||||
|
||||
/**
|
||||
* \brief Returns a random integer from the underlying distribution
|
||||
* \return Integer cast of ::GetValue()
|
||||
*/
|
||||
uint32_t GetInteger (void) const;
|
||||
|
||||
We have already described the seeding configuration above. Different
|
||||
RandomVariable subclasses may have additional API.
|
||||
|
||||
Types of RandomVariables
|
||||
************************
|
||||
|
||||
The following types of random variables are provided, and are documented in the
|
||||
|ns3| Doxygen or by reading ``src/core/model/random-variable-stream.h``. Users
|
||||
can also create their own custom random variables by deriving from class
|
||||
:cpp:class:`RandomVariableStream`.
|
||||
|
||||
* class :cpp:class:`UniformRandomVariable`
|
||||
* class :cpp:class:`ConstantRandomVariable`
|
||||
* class :cpp:class:`SequentialRandomVariable`
|
||||
* class :cpp:class:`ExponentialRandomVariable`
|
||||
* class :cpp:class:`ParetoRandomVariable`
|
||||
* class :cpp:class:`WeibullRandomVariable`
|
||||
* class :cpp:class:`NormalRandomVariable`
|
||||
* class :cpp:class:`LogNormalRandomVariable`
|
||||
* class :cpp:class:`GammaRandomVariable`
|
||||
* class :cpp:class:`ErlangRandomVariable`
|
||||
* class :cpp:class:`TriangularRandomVariable`
|
||||
* class :cpp:class:`ZipfRandomVariable`
|
||||
* class :cpp:class:`ZetaRandomVariable`
|
||||
* class :cpp:class:`DeterministicRandomVariable`
|
||||
* class :cpp:class:`EmpiricalRandomVariable`
|
||||
|
||||
Semantics of RandomVariableStream objects
|
||||
*****************************************
|
||||
|
||||
RandomVariableStream objects derive from :cpp:class:`ns3::Object` and are
|
||||
handled by smart pointers.
|
||||
|
||||
RandomVariableStream instances can also be used in |ns3| attributes, which means
|
||||
that values can be set for them through the |ns3| attribute system.
|
||||
An example is in the propagation models for WifiNetDevice::
|
||||
|
||||
TypeId
|
||||
RandomPropagationDelayModel::GetTypeId (void)
|
||||
{
|
||||
static TypeId tid = TypeId ("ns3::RandomPropagationDelayModel")
|
||||
.SetParent<PropagationDelayModel> ()
|
||||
.SetGroupName ("Propagation")
|
||||
.AddConstructor<RandomPropagationDelayModel> ()
|
||||
.AddAttribute ("Variable",
|
||||
"The random variable which generates random delays (s).",
|
||||
StringValue ("ns3::UniformRandomVariable"),
|
||||
MakePointerAccessor (&RandomPropagationDelayModel::m_variable),
|
||||
MakePointerChecker<RandomVariableStream> ())
|
||||
;
|
||||
return tid;
|
||||
}
|
||||
|
||||
Here, the |ns3| user can change the default random variable for this
|
||||
delay model (which is a UniformRandomVariable ranging from 0 to 1) through
|
||||
the attribute system.
|
||||
|
||||
Using other PRNG
|
||||
****************
|
||||
|
||||
There is presently no support for substituting a different underlying
|
||||
random number generator (e.g., the GNU Scientific Library or the Akaroa
|
||||
package). Patches are welcome.
|
||||
|
||||
Setting the stream number
|
||||
*************************
|
||||
|
||||
The underlying MRG32k3a generator provides 2^64 independent streams.
|
||||
In ns-3, these are assigned sequentially starting from the first stream as
|
||||
new RandomVariableStream instances make their first call to GetValue().
|
||||
|
||||
As a result of how these RandomVariableStream objects are assigned to
|
||||
underlying streams, the assignment is sensitive to perturbations of
|
||||
the simulation configuration. The consequence is that if any aspect of the
|
||||
simulation configuration is changed, the mapping of RandomVariables to
|
||||
streams may (or may not) change.
|
||||
|
||||
As a concrete example, a user running a comparative study between routing
|
||||
protocols may find that the act of changing one routing protocol for another
|
||||
will notice that the underlying mobility pattern also changed.
|
||||
|
||||
Starting with ns-3.15, some control has been provided to users to allow
|
||||
users to optionally fix the assignment of selected RandomVariableStream
|
||||
objects to underlying streams. This is the ``Stream`` attribute, part
|
||||
of the base class RandomVariableStream.
|
||||
|
||||
By partitioning the existing sequence of streams from before:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
<-------------------------------------------------------------------------->
|
||||
stream 0 stream (2^64 - 1)
|
||||
|
||||
into two equal-sized sets:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
<-------------------------------------------------------------------------->
|
||||
^ ^^ ^
|
||||
| || |
|
||||
stream 0 stream (2^63 - 1) stream 2^63 stream (2^64 - 1)
|
||||
<- automatically assigned -----------><- assigned by user ----------------->
|
||||
|
||||
The first 2^63 streams continue to be automatically assigned, while
|
||||
the last 2^63 are given stream indices starting with zero up to
|
||||
2^63-1.
|
||||
|
||||
The assignment of streams to a fixed stream number is optional; instances
|
||||
of RandomVariableStream that do not have a stream value assigned will
|
||||
be assigned the next one from the pool of automatic streams.
|
||||
|
||||
To fix a RandomVariableStream to a particular underlying stream, assign
|
||||
its ``Stream`` attribute to a non-negative integer (the default value
|
||||
of -1 means that a value will be automatically allocated).
|
||||
|
||||
Publishing your results
|
||||
***********************
|
||||
|
||||
When you publish simulation results, a key piece of configuration
|
||||
information that you should always state is how you used the
|
||||
the random number generator.
|
||||
|
||||
* what seeds you used,
|
||||
* what RNG you used if not the default,
|
||||
* how were independent runs performed,
|
||||
* for large simulations, how did you check that you did not cycle.
|
||||
|
||||
It is incumbent on the researcher publishing results to include enough
|
||||
information to allow others to reproduce his or her results. It is also
|
||||
incumbent on the researcher to convince oneself that the random numbers used
|
||||
were statistically valid, and to state in the paper why such confidence is
|
||||
assumed.
|
||||
|
||||
Summary
|
||||
*******
|
||||
|
||||
Let's review what things you should do when creating a simulation.
|
||||
|
||||
* Decide whether you are running with a fixed seed or random seed; a fixed seed
|
||||
is the default,
|
||||
* Decide how you are going to manage independent replications, if applicable,
|
||||
* Convince yourself that you are not drawing more random values than the cycle
|
||||
length, if you are running a very long simulation, and
|
||||
* When you publish, follow the guidelines above about documenting your use of
|
||||
the random number generator.
|
||||
@@ -1,96 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: cpp
|
||||
|
||||
RealTime
|
||||
--------
|
||||
|
||||
|ns3| has been designed for integration into testbed and virtual machine
|
||||
environments. To integrate with real network stacks and emit/consume packets, a
|
||||
real-time scheduler is needed to try to lock the simulation clock with the
|
||||
hardware clock. We describe here a component of this: the RealTime scheduler.
|
||||
|
||||
The purpose of the realtime scheduler is to cause the progression of the
|
||||
simulation clock to occur synchronously with respect to some external time base.
|
||||
Without the presence of an external time base (wall clock), simulation time
|
||||
jumps instantly from one simulated time to the next.
|
||||
|
||||
Behavior
|
||||
********
|
||||
|
||||
When using a non-realtime scheduler (the default in |ns3|), the simulator
|
||||
advances the simulation time to the next scheduled event. During event
|
||||
execution, simulation time is frozen. With the realtime scheduler, the behavior
|
||||
is similar from the perspective of simulation models (i.e., simulation time is
|
||||
frozen during event execution), but between events, the simulator will attempt
|
||||
to keep the simulation clock aligned with the machine clock.
|
||||
|
||||
When an event is finished executing, and the scheduler moves to the next event,
|
||||
the scheduler compares the next event execution time with the machine clock. If
|
||||
the next event is scheduled for a future time, the simulator sleeps until that
|
||||
realtime is reached and then executes the next event.
|
||||
|
||||
It may happen that, due to the processing inherent in the execution of
|
||||
simulation events, that the simulator cannot keep up with realtime. In such a
|
||||
case, it is up to the user configuration what to do. There are two |ns3|
|
||||
attributes that govern the behavior. The first is
|
||||
``ns3::RealTimeSimulatorImpl::SynchronizationMode``. The two entries possible
|
||||
for this attribute are ``BestEffort`` (the default) or ``HardLimit``. In
|
||||
"BestEffort" mode, the simulator will just try to catch up to realtime by
|
||||
executing events until it reaches a point where the next event is in the
|
||||
(realtime) future, or else the simulation ends. In BestEffort mode, then, it is
|
||||
possible for the simulation to consume more time than the wall clock time. The
|
||||
other option "HardLimit" will cause the simulation to abort if the tolerance
|
||||
threshold is exceeded. This attribute is
|
||||
``ns3::RealTimeSimulatorImpl::HardLimit`` and the default is 0.1 seconds.
|
||||
|
||||
A different mode of operation is one in which simulated time is **not** frozen
|
||||
during an event execution. This mode of realtime simulation was implemented but
|
||||
removed from the |ns3| tree because of questions of whether it would be useful.
|
||||
If users are interested in a realtime simulator for which simulation time does
|
||||
not freeze during event execution (i.e., every call to ``Simulator::Now()``
|
||||
returns the current wall clock time, not the time at which the event started
|
||||
executing), please contact the ns-developers mailing list.
|
||||
|
||||
Usage
|
||||
*****
|
||||
|
||||
The usage of the realtime simulator is straightforward, from a scripting
|
||||
perspective. Users just need to set the attribute
|
||||
``SimulatorImplementationType`` to the Realtime simulator, such as follows: ::
|
||||
|
||||
GlobalValue::Bind ("SimulatorImplementationType",
|
||||
StringValue ("ns3::RealtimeSimulatorImpl"));
|
||||
|
||||
There is a script in ``examples/realtime/realtime-udp-echo.cc`` that
|
||||
has an example of how to configure the realtime behavior. Try:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --run realtime-udp-echo
|
||||
|
||||
Whether the simulator will work in a best effort or hard limit policy fashion is
|
||||
governed by the attributes explained in the previous section.
|
||||
|
||||
Implementation
|
||||
**************
|
||||
|
||||
The implementation is contained in the following files:
|
||||
|
||||
* ``src/core/model/realtime-simulator-impl.{cc,h}``
|
||||
* ``src/core/model/wall-clock-synchronizer.{cc,h}``
|
||||
|
||||
In order to create a realtime scheduler, to a first approximation you just want
|
||||
to cause simulation time jumps to consume real time. We propose doing this using
|
||||
a combination of sleep- and busy- waits. Sleep-waits cause the calling process
|
||||
(thread) to yield the processor for some amount of time. Even though this
|
||||
specified amount of time can be passed to nanosecond resolution, it is actually
|
||||
converted to an OS-specific granularity. In Linux, the granularity is called a
|
||||
Jiffy. Typically this resolution is insufficient for our needs (on the order of
|
||||
a ten milliseconds), so we round down and sleep for some smaller number of
|
||||
Jiffies. The process is then awakened after the specified number of Jiffies has
|
||||
passed. At this time, we have some residual time to wait. This time is generally
|
||||
smaller than the minimum sleep time, so we busy-wait for the remainder of the
|
||||
time. This means that the thread just sits in a for loop consuming cycles until
|
||||
the desired time arrives. After the combination of sleep- and busy-waits, the
|
||||
elapsed realtime (wall) clock should agree with the simulation time of the next
|
||||
event and the simulation proceeds.
|
||||
@@ -1,5 +0,0 @@
|
||||
.. |ns3| replace:: *ns-3*
|
||||
|
||||
.. |ns2| replace:: *ns-2*
|
||||
|
||||
.. |check| replace:: :math:`\checkmark`
|
||||
@@ -1,11 +0,0 @@
|
||||
Support
|
||||
-------
|
||||
|
||||
.. toctree::
|
||||
|
||||
new-models
|
||||
new-modules
|
||||
documentation
|
||||
enable-modules
|
||||
enable-tests
|
||||
troubleshoot
|
||||
@@ -1,219 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
|
||||
Background
|
||||
----------
|
||||
|
||||
**This chapter may be skipped by readers familiar with the basics of
|
||||
software testing.**
|
||||
|
||||
Writing defect-free software is a difficult proposition. There are many
|
||||
dimensions to the problem and there is much confusion regarding what is
|
||||
meant by different terms in different contexts. We have found it worthwhile
|
||||
to spend a little time reviewing the subject and defining some terms.
|
||||
|
||||
Software testing may be loosely defined as the process of executing a program
|
||||
with the intent of finding errors. When one enters a discussion regarding
|
||||
software testing, it quickly becomes apparent that there are many distinct
|
||||
mind-sets with which one can approach the subject.
|
||||
|
||||
For example, one could break the process into broad functional categories
|
||||
like ''correctness testing,'' ''performance testing,'' ''robustness testing''
|
||||
and ''security testing.'' Another way to look at the problem is by life-cycle:
|
||||
''requirements testing,'' ''design testing,'' ''acceptance testing,'' and
|
||||
''maintenance testing.'' Yet another view is by the scope of the tested system.
|
||||
In this case one may speak of ''unit testing,'' ''component testing,''
|
||||
''integration testing,'' and ''system testing.'' These terms are also not
|
||||
standardized in any way, and so ''maintenance testing'' and ''regression
|
||||
testing'' may be heard interchangeably. Additionally, these terms are often
|
||||
misused.
|
||||
|
||||
There are also a number of different philosophical approaches to software
|
||||
testing. For example, some organizations advocate writing test programs
|
||||
before actually implementing the desired software, yielding ''test-driven
|
||||
development.'' Some organizations advocate testing from a customer perspective
|
||||
as soon as possible, following a parallel with the agile development process:
|
||||
''test early and test often.'' This is sometimes called ''agile testing.'' It
|
||||
seems that there is at least one approach to testing for every development
|
||||
methodology.
|
||||
|
||||
The |ns3| project is not in the business of advocating for any one of
|
||||
these processes, but the project as a whole has requirements that help inform
|
||||
the test process.
|
||||
|
||||
Like all major software products, |ns3| has a number of qualities that
|
||||
must be present for the product to succeed. From a testing perspective, some
|
||||
of these qualities that must be addressed are that |ns3| must be
|
||||
''correct,'' ''robust,'' ''performant'' and ''maintainable.'' Ideally there
|
||||
should be metrics for each of these dimensions that are checked by the tests
|
||||
to identify when the product fails to meet its expectations / requirements.
|
||||
|
||||
Correctness
|
||||
***********
|
||||
|
||||
The essential purpose of testing is to determine that a piece of software
|
||||
behaves ''correctly.'' For |ns3| this means that if we simulate
|
||||
something, the simulation should faithfully represent some physical entity or
|
||||
process to a specified accuracy and precision.
|
||||
|
||||
It turns out that there are two perspectives from which one can view
|
||||
correctness. Verifying that a particular model is implemented according
|
||||
to its specification is generically called *verification*. The process of
|
||||
deciding that the model is correct for its intended use is generically called
|
||||
*validation*.
|
||||
|
||||
Validation and Verification
|
||||
***************************
|
||||
|
||||
A computer model is a mathematical or logical representation of something. It
|
||||
can represent a vehicle, an elephant (see
|
||||
`David Harel's talk about modeling an elephant at SIMUTools 2009 <http://simutools.org/2009/>`_, or a networking card. Models can also represent
|
||||
processes such as global warming, freeway traffic flow or a specification of a
|
||||
networking protocol. Models can be completely faithful representations of a
|
||||
logical process specification, but they necessarily can never completely
|
||||
simulate a physical object or process. In most cases, a number of
|
||||
simplifications are made to the model to make simulation computationally
|
||||
tractable.
|
||||
|
||||
Every model has a *target system* that it is attempting to simulate. The
|
||||
first step in creating a simulation model is to identify this target system and
|
||||
the level of detail and accuracy that the simulation is desired to reproduce.
|
||||
In the case of a logical process, the target system may be identified as ''TCP
|
||||
as defined by RFC 793.'' In this case, it will probably be desirable to create
|
||||
a model that completely and faithfully reproduces RFC 793. In the case of a
|
||||
physical process this will not be possible. If, for example, you would like to
|
||||
simulate a wireless networking card, you may determine that you need, ''an
|
||||
accurate MAC-level implementation of the 802.11 specification and [...] a
|
||||
not-so-slow PHY-level model of the 802.11a specification.''
|
||||
|
||||
Once this is done, one can develop an abstract model of the target system. This
|
||||
is typically an exercise in managing the tradeoffs between complexity, resource
|
||||
requirements and accuracy. The process of developing an abstract model has been
|
||||
called *model qualification* in the literature. In the case of a TCP
|
||||
protocol, this process results in a design for a collection of objects,
|
||||
interactions and behaviors that will fully implement RFC 793 in |ns3|.
|
||||
In the case of the wireless card, this process results in a number of tradeoffs
|
||||
to allow the physical layer to be simulated and the design of a network device
|
||||
and channel for ns-3, along with the desired objects, interactions and behaviors.
|
||||
|
||||
This abstract model is then developed into an |ns3| model that
|
||||
implements the abstract model as a computer program. The process of getting the
|
||||
implementation to agree with the abstract model is called *model
|
||||
verification* in the literature.
|
||||
|
||||
The process so far is open loop. What remains is to make a determination that a
|
||||
given ns-3 model has some connection to some reality -- that a model is an
|
||||
accurate representation of a real system, whether a logical process or a physical
|
||||
entity.
|
||||
|
||||
If one is going to use a simulation model to try and predict how some real
|
||||
system is going to behave, there must be some reason to believe your results --
|
||||
i.e., can one trust that an inference made from the model translates into a
|
||||
correct prediction for the real system. The process of getting the ns-3 model
|
||||
behavior to agree with the desired target system behavior as defined by the model
|
||||
qualification process is called *model validation* in the literature. In the
|
||||
case of a TCP implementation, you may want to compare the behavior of your ns-3
|
||||
TCP model to some reference implementation in order to validate your model. In
|
||||
the case of a wireless physical layer simulation, you may want to compare the
|
||||
behavior of your model to that of real hardware in a controlled setting,
|
||||
|
||||
The |ns3| testing environment provides tools to allow for both model
|
||||
validation and testing, and encourages the publication of validation results.
|
||||
|
||||
Robustness
|
||||
**********
|
||||
|
||||
Robustness is the quality of being able to withstand stresses, or changes in
|
||||
environments, inputs or calculations, etc. A system or design is ''robust''
|
||||
if it can deal with such changes with minimal loss of functionality.
|
||||
|
||||
This kind of testing is usually done with a particular focus. For example, the
|
||||
system as a whole can be run on many different system configurations to
|
||||
demonstrate that it can perform correctly in a large number of environments.
|
||||
|
||||
The system can be also be stressed by operating close to or beyond capacity by
|
||||
generating or simulating resource exhaustion of various kinds. This genre of
|
||||
testing is called ''stress testing.''
|
||||
|
||||
The system and its components may be exposed to so-called ''clean tests'' that
|
||||
demonstrate a positive result -- that is that the system operates correctly in
|
||||
response to a large variation of expected configurations.
|
||||
|
||||
The system and its components may also be exposed to ''dirty tests'' which
|
||||
provide inputs outside the expected range. For example, if a module expects a
|
||||
zero-terminated string representation of an integer, a dirty test might provide
|
||||
an unterminated string of random characters to verify that the system does not
|
||||
crash as a result of this unexpected input. Unfortunately, detecting such
|
||||
''dirty'' input and taking preventive measures to ensure the system does not
|
||||
fail catastrophically can require a huge amount of development overhead. In
|
||||
order to reduce development time, a decision was taken early on in the project
|
||||
to minimize the amount of parameter validation and error handling in the
|
||||
|ns3| codebase. For this reason, we do not spend much time on dirty
|
||||
testing -- it would just uncover the results of the design decision we know
|
||||
we took.
|
||||
|
||||
We do want to demonstrate that |ns3| software does work across some set
|
||||
of conditions. We borrow a couple of definitions to narrow this down a bit.
|
||||
The *domain of applicability* is a set of prescribed conditions for which
|
||||
the model has been tested, compared against reality to the extent possible, and
|
||||
judged suitable for use. The *range of accuracy* is an agreement between
|
||||
the computerized model and reality within a domain of applicability.
|
||||
|
||||
The |ns3| testing environment provides tools to allow for setting up
|
||||
and running test environments over multiple systems (buildbot) and provides
|
||||
classes to encourage clean tests to verify the operation of the system over the
|
||||
expected ''domain of applicability'' and ''range of accuracy.''
|
||||
|
||||
Performant
|
||||
**********
|
||||
|
||||
Okay, ''performant'' isn't a real English word. It is, however, a very concise
|
||||
neologism that is quite often used to describe what we want |ns3| to
|
||||
be: powerful and fast enough to get the job done.
|
||||
|
||||
This is really about the broad subject of software performance testing. One of
|
||||
the key things that is done is to compare two systems to find which performs
|
||||
better (cf benchmarks). This is used to demonstrate that, for example,
|
||||
|ns3| can perform a basic kind of simulation at least as fast as a
|
||||
competing tool, or can be used to identify parts of the system that perform
|
||||
badly.
|
||||
|
||||
In the |ns3| test framework, we provide support for timing various kinds
|
||||
of tests.
|
||||
|
||||
Maintainability
|
||||
***************
|
||||
|
||||
A software product must be maintainable. This is, again, a very broad
|
||||
statement, but a testing framework can help with the task. Once a model has
|
||||
been developed, validated and verified, we can repeatedly execute the suite
|
||||
of tests for the entire system to ensure that it remains valid and verified
|
||||
over its lifetime.
|
||||
|
||||
When a feature stops functioning as intended after some kind of change to the
|
||||
system is integrated, it is called generically a *regression*.
|
||||
Originally the
|
||||
term regression referred to a change that caused a previously fixed bug to
|
||||
reappear, but the term has evolved to describe any kind of change that breaks
|
||||
existing functionality. There are many kinds of regressions that may occur
|
||||
in practice.
|
||||
|
||||
A *local regression* is one in which a change affects the changed component
|
||||
directly. For example, if a component is modified to allocate and free memory
|
||||
but stale pointers are used, the component itself fails.
|
||||
|
||||
A *remote regression* is one in which a change to one component breaks
|
||||
functionality in another component. This reflects violation of an implied but
|
||||
possibly unrecognized contract between components.
|
||||
|
||||
An *unmasked regression* is one that creates a situation where a previously
|
||||
existing bug that had no affect is suddenly exposed in the system. This may
|
||||
be as simple as exercising a code path for the first time.
|
||||
|
||||
A *performance regression* is one that causes the performance requirements
|
||||
of the system to be violated. For example, doing some work in a low level
|
||||
function that may be repeated large numbers of times may suddenly render the
|
||||
system unusable from certain perspectives.
|
||||
|
||||
The |ns3| testing framework provides tools for automating the process
|
||||
used to validate and verify the code in nightly test suites to help quickly
|
||||
identify possible regressions.
|
||||
@@ -1,814 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: bash
|
||||
|
||||
Testing framework
|
||||
-----------------
|
||||
|
||||
|ns3| consists of a simulation core engine, a set of models, example programs,
|
||||
and tests. Over time, new contributors contribute models, tests, and
|
||||
examples. A Python test program ``test.py`` serves as the test
|
||||
execution manager; ``test.py`` can run test code and examples to
|
||||
look for regressions, can output the results into a number of forms, and
|
||||
can manage code coverage analysis tools. On top of this, we layer
|
||||
*buildslaves* that are automated build robots that perform
|
||||
robustness testing by running the test framework on different systems
|
||||
and with different configuration options.
|
||||
|
||||
Buildslaves
|
||||
***********
|
||||
|
||||
At the highest level of |ns3| testing are the buildslaves (build robots).
|
||||
If you are unfamiliar with
|
||||
this system look at `<https://ns-buildmaster.ee.washington.edu:8010/>`_.
|
||||
This is an open-source automated system that allows |ns3| to be rebuilt
|
||||
and tested daily. By running the buildbots on a number
|
||||
of different systems we can ensure that |ns3| builds and executes
|
||||
properly on all of its supported systems.
|
||||
|
||||
Users (and developers) typically will not interact with the buildslave system other
|
||||
than to read its messages regarding test results. If a failure is detected in
|
||||
one of the automated build and test jobs, the buildbot will send an email to the
|
||||
*ns-commits* mailing list. This email will look something like
|
||||
|
||||
.. sourcecode: text
|
||||
|
||||
[Ns-commits] Build failed in Jenkins: daily-ubuntu-without-valgrind » Ubuntu-64-15.04 #926
|
||||
|
||||
...
|
||||
281 of 285 tests passed (281 passed, 3 skipped, 1 failed, 0 crashed, 0 valgrind errors)
|
||||
List of SKIPped tests:
|
||||
ns3-tcp-cwnd
|
||||
ns3-tcp-interoperability
|
||||
nsc-tcp-loss
|
||||
List of FAILed tests:
|
||||
random-variable-stream-generators
|
||||
+ exit 1
|
||||
Build step 'Execute shell' marked build as failure
|
||||
|
||||
In the full details URL shown in the email, one can find links to the detailed test output.
|
||||
|
||||
The buildslave system will do its job quietly if there are no errors, and the
|
||||
system will undergo build and test cycles every day to verify that all is well.
|
||||
|
||||
Test.py
|
||||
*******
|
||||
The buildbots use a Python program, ``test.py``, that is responsible for
|
||||
running all of the tests and collecting the resulting reports into a human-
|
||||
readable form. This program is also available for use by users and developers
|
||||
as well.
|
||||
|
||||
``test.py`` is very flexible in allowing the user to specify the number
|
||||
and kind of tests to run; and also the amount and kind of output to generate.
|
||||
|
||||
Before running ``test.py``, make sure that ns3's examples and tests
|
||||
have been built by doing the following
|
||||
|
||||
::
|
||||
|
||||
$ ./waf configure --enable-examples --enable-tests
|
||||
$ ./waf build
|
||||
|
||||
By default, ``test.py`` will run all available tests and report status
|
||||
back in a very concise form. Running the command
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py
|
||||
|
||||
will result in a number of ``PASS``, ``FAIL``, ``CRASH`` or ``SKIP``
|
||||
indications followed by the kind of test that was run and its display name.
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
'build' finished successfully (0.939s)
|
||||
FAIL: TestSuite propagation-loss-model
|
||||
PASS: TestSuite object-name-service
|
||||
PASS: TestSuite pcap-file-object
|
||||
PASS: TestSuite ns3-tcp-cwnd
|
||||
...
|
||||
PASS: TestSuite ns3-tcp-interoperability
|
||||
PASS: Example csma-broadcast
|
||||
PASS: Example csma-multicast
|
||||
|
||||
This mode is intended to be used by users who are interested in determining if
|
||||
their distribution is working correctly, and by developers who are interested
|
||||
in determining if changes they have made have caused any regressions.
|
||||
|
||||
There are a number of options available to control the behavior of ``test.py``.
|
||||
if you run ``test.py --help`` you should see a command summary like:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Usage: test.py [options]
|
||||
|
||||
Options:
|
||||
-h, --help show this help message and exit
|
||||
-b BUILDPATH, --buildpath=BUILDPATH
|
||||
specify the path where ns-3 was built (defaults to the
|
||||
build directory for the current variant)
|
||||
-c KIND, --constrain=KIND
|
||||
constrain the test-runner by kind of test
|
||||
-e EXAMPLE, --example=EXAMPLE
|
||||
specify a single example to run (no relative path is
|
||||
needed)
|
||||
-d, --duration print the duration of each test suite and example
|
||||
-e EXAMPLE, --example=EXAMPLE
|
||||
specify a single example to run (no relative path is
|
||||
needed)
|
||||
-u, --update-data If examples use reference data files, get them to re-
|
||||
generate them
|
||||
-f FULLNESS, --fullness=FULLNESS
|
||||
choose the duration of tests to run: QUICK, EXTENSIVE,
|
||||
or TAKES_FOREVER, where EXTENSIVE includes QUICK and
|
||||
TAKES_FOREVER includes QUICK and EXTENSIVE (only QUICK
|
||||
tests are run by default)
|
||||
-g, --grind run the test suites and examples using valgrind
|
||||
-k, --kinds print the kinds of tests available
|
||||
-l, --list print the list of known tests
|
||||
-m, --multiple report multiple failures from test suites and test
|
||||
cases
|
||||
-n, --nowaf do not run waf before starting testing
|
||||
-p PYEXAMPLE, --pyexample=PYEXAMPLE
|
||||
specify a single python example to run (with relative
|
||||
path)
|
||||
-r, --retain retain all temporary files (which are normally
|
||||
deleted)
|
||||
-s TEST-SUITE, --suite=TEST-SUITE
|
||||
specify a single test suite to run
|
||||
-t TEXT-FILE, --text=TEXT-FILE
|
||||
write detailed test results into TEXT-FILE.txt
|
||||
-v, --verbose print progress and informational messages
|
||||
-w HTML-FILE, --web=HTML-FILE, --html=HTML-FILE
|
||||
write detailed test results into HTML-FILE.html
|
||||
-x XML-FILE, --xml=XML-FILE
|
||||
write detailed test results into XML-FILE.xml
|
||||
|
||||
If one specifies an optional output style, one can generate detailed descriptions
|
||||
of the tests and status. Available styles are ``text`` and ``HTML``.
|
||||
The buildbots will select the HTML option to generate HTML test reports for the
|
||||
nightly builds using
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --html=nightly.html
|
||||
|
||||
In this case, an HTML file named ''nightly.html'' would be created with a pretty
|
||||
summary of the testing done. A ''human readable'' format is available for users
|
||||
interested in the details.
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --text=results.txt
|
||||
|
||||
In the example above, the test suite checking the |ns3| wireless
|
||||
device propagation loss models failed. By default no further information is
|
||||
provided.
|
||||
|
||||
To further explore the failure, ``test.py`` allows a single test suite
|
||||
to be specified. Running the command
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --suite=propagation-loss-model
|
||||
|
||||
or equivalently
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py -s propagation-loss-model
|
||||
|
||||
results in that single test suite being run.
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
FAIL: TestSuite propagation-loss-model
|
||||
|
||||
To find detailed information regarding the failure, one must specify the kind
|
||||
of output desired. For example, most people will probably be interested in
|
||||
a text file::
|
||||
|
||||
$ ./test.py --suite=propagation-loss-model --text=results.txt
|
||||
|
||||
This will result in that single test suite being run with the test status written to
|
||||
the file ''results.txt''.
|
||||
|
||||
You should find something similar to the following in that file
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
FAIL: Test Suite ''propagation-loss-model'' (real 0.02 user 0.01 system 0.00)
|
||||
PASS: Test Case "Check ... Friis ... model ..." (real 0.01 user 0.00 system 0.00)
|
||||
FAIL: Test Case "Check ... Log Distance ... model" (real 0.01 user 0.01 system 0.00)
|
||||
Details:
|
||||
Message: Got unexpected SNR value
|
||||
Condition: [long description of what actually failed]
|
||||
Actual: 176.395
|
||||
Limit: 176.407 +- 0.0005
|
||||
File: ../src/test/ns3wifi/propagation-loss-models-test-suite.cc
|
||||
Line: 360
|
||||
|
||||
Notice that the Test Suite is composed of two Test Cases. The first test case
|
||||
checked the Friis propagation loss model and passed. The second test case
|
||||
failed checking the Log Distance propagation model. In this case, an SNR of
|
||||
176.395 was found, and the test expected a value of 176.407 correct to three
|
||||
decimal places. The file which implemented the failing test is listed as well
|
||||
as the line of code which triggered the failure.
|
||||
|
||||
If you desire, you could just as easily have written an HTML file using the
|
||||
``--html`` option as described above.
|
||||
|
||||
Typically a user will run all tests at least once after downloading
|
||||
|ns3| to ensure that his or her environment has been built correctly
|
||||
and is generating correct results according to the test suites. Developers
|
||||
will typically run the test suites before and after making a change to ensure
|
||||
that they have not introduced a regression with their changes. In this case,
|
||||
developers may not want to run all tests, but only a subset. For example,
|
||||
the developer might only want to run the unit tests periodically while making
|
||||
changes to a repository. In this case, ``test.py`` can be told to constrain
|
||||
the types of tests being run to a particular class of tests. The following
|
||||
command will result in only the unit tests being run::
|
||||
|
||||
$ ./test.py --constrain=unit
|
||||
|
||||
Similarly, the following command will result in only the example smoke tests
|
||||
being run::
|
||||
|
||||
$ ./test.py --constrain=unit
|
||||
|
||||
To see a quick list of the legal kinds of constraints, you can ask for them
|
||||
to be listed. The following command
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --kinds
|
||||
|
||||
will result in the following list being displayed:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
'build' finished successfully (0.939s)Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
bvt: Build Verification Tests (to see if build completed successfully)
|
||||
core: Run all TestSuite-based tests (exclude examples)
|
||||
example: Examples (to see if example programs run successfully)
|
||||
performance: Performance Tests (check to see if the system is as fast as expected)
|
||||
system: System Tests (spans modules to check integration of modules)
|
||||
unit: Unit Tests (within modules to check basic functionality)
|
||||
|
||||
Any of these kinds of test can be provided as a constraint using the ``--constraint``
|
||||
option.
|
||||
|
||||
To see a quick list of all of the test suites available, you can ask for them
|
||||
to be listed. The following command,
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --list
|
||||
|
||||
will result in a list of the test suite being displayed, similar to
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Waf: Entering directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
Waf: Leaving directory `/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build'
|
||||
'build' finished successfully (0.939s)
|
||||
|
||||
Test Type Test Name
|
||||
--------- ---------
|
||||
performance many-uniform-random-variables-one-get-value-call
|
||||
performance one-uniform-random-variable-many-get-value-calls
|
||||
performance type-id-perf
|
||||
system buildings-pathloss-test
|
||||
system buildings-shadowing-test
|
||||
system devices-mesh-dot11s-regression
|
||||
system devices-mesh-flame-regression
|
||||
system epc-gtpu
|
||||
...
|
||||
unit wimax-phy-layer
|
||||
unit wimax-service-flow
|
||||
unit wimax-ss-mac-layer
|
||||
unit wimax-tlv
|
||||
example adhoc-aloha-ideal-phy
|
||||
example adhoc-aloha-ideal-phy-matrix-propagation-loss-model
|
||||
example adhoc-aloha-ideal-phy-with-microwave-oven
|
||||
example aodv
|
||||
...
|
||||
|
||||
Any of these listed suites can be selected to be run by itself using the
|
||||
``--suite`` option as shown above. Examples are handled differently.
|
||||
|
||||
Similarly to test suites, one can run a single C++ example program
|
||||
using the ``--example`` option. Note that the relative path for the
|
||||
example does not need to be included and that the executables built
|
||||
for C++ examples do not have extensions. Furthermore, the example
|
||||
must be registered as an example to the test framework; it is not
|
||||
sufficient to create an example and run it through test.py; it must
|
||||
be added to the relevant ``examples-to-run.py`` file, explained below.
|
||||
Entering
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --example=udp-echo
|
||||
|
||||
results in that single example being run.
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
PASS: Example examples/udp/udp-echo
|
||||
|
||||
You can specify the directory where |ns3| was built using the
|
||||
``--buildpath`` option as follows.
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --buildpath=/home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/debug --example=wifi-simple-adhoc
|
||||
|
||||
One can run a single Python example program using the ``--pyexample``
|
||||
option. Note that the relative path for the example must be included
|
||||
and that Python examples do need their extensions. Entering
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --pyexample=examples/tutorial/first.py
|
||||
|
||||
results in that single example being run.
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
PASS: Example examples/tutorial/first.py
|
||||
|
||||
Because Python examples are not built, you do not need to specify the
|
||||
directory where |ns3| was built to run them.
|
||||
|
||||
Normally when example programs are executed, they write a large amount of trace
|
||||
file data. This is normally saved to the base directory of the distribution
|
||||
(e.g., /home/user/ns-3-dev). When ``test.py`` runs an example, it really
|
||||
is completely unconcerned with the trace files. It just wants to to determine
|
||||
if the example can be built and run without error. Since this is the case, the
|
||||
trace files are written into a ``/tmp/unchecked-traces`` directory. If you
|
||||
run the above example, you should be able to find the associated
|
||||
``udp-echo.tr`` and ``udp-echo-n-1.pcap`` files there.
|
||||
|
||||
The list of available examples is defined by the contents of the ''examples''
|
||||
directory in the distribution. If you select an example for execution using
|
||||
the ``--example`` option, ``test.py`` will not make any attempt to decide
|
||||
if the example has been configured or not, it will just try to run it and
|
||||
report the result of the attempt.
|
||||
|
||||
When ``test.py`` runs, by default it will first ensure that the system has
|
||||
been completely built. This can be defeated by selecting the ``--nowaf``
|
||||
option.
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --list --nowaf
|
||||
|
||||
will result in a list of the currently built test suites being displayed, similar to:
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
propagation-loss-model
|
||||
ns3-tcp-cwnd
|
||||
ns3-tcp-interoperability
|
||||
pcap-file
|
||||
object-name-service
|
||||
random-variable-stream-generators
|
||||
|
||||
Note the absence of the ``Waf`` build messages.
|
||||
|
||||
``test.py`` also supports running the test suites and examples under valgrind.
|
||||
Valgrind is a flexible program for debugging and profiling Linux executables. By
|
||||
default, valgrind runs a tool called memcheck, which performs a range of memory-
|
||||
checking functions, including detecting accesses to uninitialised memory, misuse
|
||||
of allocated memory (double frees, access after free, etc.) and detecting memory
|
||||
leaks. This can be selected by using the ``--grind`` option.
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --grind
|
||||
|
||||
As it runs, ``test.py`` and the programs that it runs indirectly, generate large
|
||||
numbers of temporary files. Usually, the content of these files is not interesting,
|
||||
however in some cases it can be useful (for debugging purposes) to view these files.
|
||||
``test.py`` provides a ``--retain`` option which will cause these temporary
|
||||
files to be kept after the run is completed. The files are saved in a directory
|
||||
named ``testpy-output`` under a subdirectory named according to the current Coordinated
|
||||
Universal Time (also known as Greenwich Mean Time).
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py --retain
|
||||
|
||||
Finally, ``test.py`` provides a ``--verbose`` option which will print
|
||||
large amounts of information about its progress. It is not expected that this
|
||||
will be terribly useful unless there is an error. In this case, you can get
|
||||
access to the standard output and standard error reported by running test suites
|
||||
and examples. Select verbose in the following way::
|
||||
|
||||
$ ./test.py --verbose
|
||||
|
||||
All of these options can be mixed and matched. For example, to run all of the
|
||||
|ns3| core test suites under valgrind, in verbose mode, while generating an HTML
|
||||
output file, one would do::
|
||||
|
||||
$ ./test.py --verbose --grind --constrain=core --html=results.html
|
||||
|
||||
TestTaxonomy
|
||||
************
|
||||
|
||||
As mentioned above, tests are grouped into a number of broadly defined
|
||||
classifications to allow users to selectively run tests to address the different
|
||||
kinds of testing that need to be done.
|
||||
|
||||
* Build Verification Tests
|
||||
* Unit Tests
|
||||
* System Tests
|
||||
* Examples
|
||||
* Performance Tests
|
||||
|
||||
Moreover, each test is further classified according to the expected time needed to
|
||||
run it. Tests are classified as:
|
||||
|
||||
* QUICK
|
||||
* EXTENSIVE
|
||||
* TAKES_FOREVER
|
||||
|
||||
Note that specifying EXTENSIVE fullness will also run tests in QUICK category.
|
||||
Specifying TAKES_FOREVER will run tests in EXTENSIVE and QUICK categories.
|
||||
By default, only QUICK tests are ran.
|
||||
|
||||
As a rule of thumb, tests that must be run to ensure |ns3| coherence should be
|
||||
QUICK (i.e., take a few seconds). Tests that could be skipped, but are nice to do
|
||||
can be EXTENSIVE; these are tests that typically need minutes. TAKES_FOREVER is
|
||||
left for tests that take a really long time, in the order of several minutes.
|
||||
The main classification goal is to be able to run the buildbots in a reasonable
|
||||
time, and still be able to perform more extensive tests when needed.
|
||||
|
||||
BuildVerificationTests
|
||||
++++++++++++++++++++++
|
||||
|
||||
These are relatively simple tests that are built along with the distribution
|
||||
and are used to make sure that the build is pretty much working. Our
|
||||
current unit tests live in the source files of the code they test and are
|
||||
built into the |ns3| modules; and so fit the description of BVTs. BVTs live
|
||||
in the same source code that is built into the |ns3| code. Our current tests
|
||||
are examples of this kind of test.
|
||||
|
||||
Unit Tests
|
||||
++++++++++
|
||||
|
||||
Unit tests are more involved tests that go into detail to make sure that a
|
||||
piece of code works as advertised in isolation. There is really no reason
|
||||
for this kind of test to be built into an |ns3| module. It turns out, for
|
||||
example, that the unit tests for the object name service are about the same
|
||||
size as the object name service code itself. Unit tests are tests that
|
||||
check a single bit of functionality that are not built into the |ns3| code,
|
||||
but live in the same directory as the code it tests. It is possible that
|
||||
these tests check integration of multiple implementation files in a module
|
||||
as well. The file src/core/test/names-test-suite.cc is an example of this kind
|
||||
of test. The file src/network/test/pcap-file-test-suite.cc is another example
|
||||
that uses a known good pcap file as a test vector file. This file is stored
|
||||
locally in the src/network directory.
|
||||
|
||||
System Tests
|
||||
++++++++++++
|
||||
|
||||
System tests are those that involve more than one module in the system. We
|
||||
have lots of this kind of test running in our current regression framework,
|
||||
but they are typically overloaded examples. We provide a new place
|
||||
for this kind of test in the directory ``src/test``. The file
|
||||
src/test/ns3tcp/ns3-interop-test-suite.cc is an example of this kind of
|
||||
test. It uses NSC TCP to test the |ns3| TCP implementation. Often there
|
||||
will be test vectors required for this kind of test, and they are stored in
|
||||
the directory where the test lives. For example,
|
||||
ns3tcp-interop-response-vectors.pcap is a file consisting of a number of TCP
|
||||
headers that are used as the expected responses of the |ns3| TCP under test
|
||||
to a stimulus generated by the NSC TCP which is used as a ''known good''
|
||||
implementation.
|
||||
|
||||
Examples
|
||||
++++++++
|
||||
|
||||
The examples are tested by the framework to make sure they built and will
|
||||
run. Nothing is checked, and currently the pcap files are just written off
|
||||
into /tmp to be discarded. If the examples run (don't crash) they pass this
|
||||
smoke test.
|
||||
|
||||
Performance Tests
|
||||
+++++++++++++++++
|
||||
|
||||
Performance tests are those which exercise a particular part of the system
|
||||
and determine if the tests have executed to completion in a reasonable time.
|
||||
|
||||
Running Tests
|
||||
*************
|
||||
|
||||
Tests are typically run using the high level ``test.py`` program. To get a list of the available command-line options, run ``test.py --help``
|
||||
|
||||
The test program ``test.py`` will run both tests and those examples that
|
||||
have been added to the list to check. The difference between tests
|
||||
and examples is as follows. Tests generally check that specific simulation
|
||||
output or events conforms to expected behavior. In contrast, the output
|
||||
of examples is not checked, and the test program merely checks the exit
|
||||
status of the example program to make sure that it runs without error.
|
||||
|
||||
Briefly, to run all tests, first one must configure tests during configuration
|
||||
stage, and also (optionally) examples if examples are to be checked:
|
||||
|
||||
::
|
||||
|
||||
$ ./waf --configure --enable-examples --enable-tests
|
||||
|
||||
Then, build |ns3|, and after it is built, just run ``test.py``. ``test.py -h``
|
||||
will show a number of configuration options that modify the behavior
|
||||
of test.py.
|
||||
|
||||
The program ``test.py`` invokes, for C++ tests and examples, a lower-level
|
||||
C++ program called ``test-runner`` to actually run the tests. As discussed
|
||||
below, this ``test-runner`` can be a helpful way to debug tests.
|
||||
|
||||
Debugging Tests
|
||||
***************
|
||||
|
||||
The debugging of the test programs is best performed running the low-level
|
||||
test-runner program. The test-runner is the bridge from generic Python
|
||||
code to |ns3| code. It is written in C++ and uses the automatic test
|
||||
discovery process in the |ns3| code to find and allow execution of all
|
||||
of the various tests.
|
||||
|
||||
The main reason why ``test.py`` is not suitable for debugging is that it is
|
||||
not allowed for logging to be turned on using the ``NS_LOG`` environmental
|
||||
variable when test.py runs. This limitation does not apply to the test-runner
|
||||
executable. Hence, if you want to see logging output from your tests, you
|
||||
have to run them using the test-runner directly.
|
||||
|
||||
In order to execute the test-runner, you run it like any other |ns3| executable
|
||||
-- using ``waf``. To get a list of available options, you can type::
|
||||
|
||||
$ ./waf --run "test-runner --help"
|
||||
|
||||
You should see something like the following
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
Usage: /home/craigdo/repos/ns-3-allinone-test/ns-3-dev/build/utils/ns3-dev-test-runner-debug [OPTIONS]
|
||||
|
||||
Options:
|
||||
--help : print these options
|
||||
--print-test-name-list : print the list of names of tests available
|
||||
--list : an alias for --print-test-name-list
|
||||
--print-test-types : print the type of tests along with their names
|
||||
--print-test-type-list : print the list of types of tests available
|
||||
--print-temp-dir : print name of temporary directory before running
|
||||
the tests
|
||||
--test-type=TYPE : process only tests of type TYPE
|
||||
--test-name=NAME : process only test whose name matches NAME
|
||||
--suite=NAME : an alias (here for compatibility reasons only)
|
||||
for --test-name=NAME
|
||||
--assert-on-failure : when a test fails, crash immediately (useful
|
||||
when running under a debugger
|
||||
--stop-on-failure : when a test fails, stop immediately
|
||||
--fullness=FULLNESS : choose the duration of tests to run: QUICK,
|
||||
EXTENSIVE, or TAKES_FOREVER, where EXTENSIVE
|
||||
includes QUICK and TAKES_FOREVER includes
|
||||
QUICK and EXTENSIVE (only QUICK tests are
|
||||
run by default)
|
||||
--verbose : print details of test execution
|
||||
--xml : format test run output as xml
|
||||
--tempdir=DIR : set temp dir for tests to store output files
|
||||
--datadir=DIR : set data dir for tests to read reference files
|
||||
--out=FILE : send test result to FILE instead of standard output
|
||||
--append=FILE : append test result to FILE instead of standard output
|
||||
|
||||
|
||||
There are a number of things available to you which will be familiar to you if
|
||||
you have looked at ``test.py``. This should be expected since the test-
|
||||
runner is just an interface between ``test.py`` and |ns3|. You
|
||||
may notice that example-related commands are missing here. That is because
|
||||
the examples are really not |ns3| tests. ``test.py`` runs them
|
||||
as if they were to present a unified testing environment, but they are really
|
||||
completely different and not to be found here.
|
||||
|
||||
The first new option that appears here, but not in test.py is the ``--assert-on-failure``
|
||||
option. This option is useful when debugging a test case when running under a
|
||||
debugger like ``gdb``. When selected, this option tells the underlying
|
||||
test case to cause a segmentation violation if an error is detected. This has
|
||||
the nice side-effect of causing program execution to stop (break into the
|
||||
debugger) when an error is detected. If you are using gdb, you could use this
|
||||
option something like,
|
||||
|
||||
::
|
||||
|
||||
$ ./waf shell
|
||||
$ cd build/utils
|
||||
$ gdb ns3-dev-test-runner-debug
|
||||
$ run --suite=global-value --assert-on-failure
|
||||
|
||||
If an error is then found in the global-value test suite, a segfault would be
|
||||
generated and the (source level) debugger would stop at the ``NS_TEST_ASSERT_MSG``
|
||||
that detected the error.
|
||||
|
||||
To run one of the tests directly from the test-runner
|
||||
using ``waf``, you will need to specify the test suite to run.
|
||||
So you could use the shell and do::
|
||||
|
||||
$ ./waf --run "test-runner --suite=pcap-file"
|
||||
|
||||
|ns3| logging is available when you run it this way, such as:
|
||||
|
||||
$ NS_LOG="Packet" ./waf --run "test-runner --suite=pcap-file"
|
||||
|
||||
Test output
|
||||
+++++++++++
|
||||
|
||||
Many test suites need to write temporary files (such as pcap files)
|
||||
in the process of running the tests. The tests then need a temporary directory
|
||||
to write to. The Python test utility (test.py) will provide a temporary file
|
||||
automatically, but if run stand-alone this temporary directory must be provided.
|
||||
It can be annoying to continually have to provide
|
||||
a ``--tempdir``, so the test runner will figure one out for you if you don't
|
||||
provide one. It first looks for environment variables named ``TMP`` and
|
||||
``TEMP`` and uses those. If neither ``TMP`` nor ``TEMP`` are defined
|
||||
it picks ``/tmp``. The code then tacks on an identifier indicating what
|
||||
created the directory (ns-3) then the time (hh.mm.ss) followed by a large random
|
||||
number. The test runner creates a directory of that name to be used as the
|
||||
temporary directory. Temporary files then go into a directory that will be
|
||||
named something like
|
||||
|
||||
::
|
||||
|
||||
/tmp/ns-3.10.25.37.61537845
|
||||
|
||||
The time is provided as a hint so that you can relatively easily reconstruct
|
||||
what directory was used if you need to go back and look at the files that were
|
||||
placed in that directory.
|
||||
|
||||
Another class of output is test output like pcap traces that are generated
|
||||
to compare to reference output. The test program will typically delete
|
||||
these after the test suites all run. To disable the deletion of test
|
||||
output, run ``test.py`` with the "retain" option:
|
||||
|
||||
::
|
||||
|
||||
$ ./test.py -r
|
||||
|
||||
and test output can be found in the ``testpy-output/`` directory.
|
||||
|
||||
Reporting of test failures
|
||||
++++++++++++++++++++++++++
|
||||
|
||||
When you run a test suite using the test-runner it will run the test
|
||||
and report PASS or FAIL.
|
||||
To run more quietly, you need to specify an output file to which the tests will write their status using the ``--out`` option.
|
||||
Try,
|
||||
|
||||
::
|
||||
|
||||
$ ./waf --run "test-runner --suite=pcap-file --out=myfile.txt"
|
||||
|
||||
|
||||
Debugging test suite failures
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
To debug test crashes, such as
|
||||
|
||||
.. sourcecode:: text
|
||||
|
||||
CRASH: TestSuite ns3-wifi-interference
|
||||
|
||||
You can access the underlying test-runner program via gdb as follows, and
|
||||
then pass the "--basedir=`pwd`" argument to run (you can also pass other
|
||||
arguments as needed, but basedir is the minimum needed)::
|
||||
|
||||
$ ./waf --command-template="gdb %s" --run "test-runner"
|
||||
Waf: Entering directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
|
||||
Waf: Leaving directory `/home/tomh/hg/sep09/ns-3-allinone/ns-3-dev-678/build'
|
||||
'build' finished successfully (0.380s)
|
||||
GNU gdb 6.8-debian
|
||||
Copyright (C) 2008 Free Software Foundation, Inc.
|
||||
L cense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
|
||||
This is free software: you are free to change and redistribute it.
|
||||
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
|
||||
and "show warranty" for details.
|
||||
This GDB was configured as "x86_64-linux-gnu"...
|
||||
(gdb) r --suite=
|
||||
Starting program: <..>/build/utils/ns3-dev-test-runner-debug --suite=ns3-wifi-interference
|
||||
[Thread debugging using libthread_db enabled]
|
||||
assert failed. file=../src/core/model/type-id.cc, line=138, cond="uid <= m_information.size () && uid != 0"
|
||||
...
|
||||
|
||||
Here is another example of how to use valgrind to debug a memory problem
|
||||
such as::
|
||||
|
||||
VALGR: TestSuite devices-mesh-dot11s-regression
|
||||
|
||||
$ ./waf --command-template="valgrind %s --suite=devices-mesh-dot11s-regression" --run test-runner
|
||||
|
||||
Class TestRunner
|
||||
****************
|
||||
|
||||
The executables that run dedicated test programs use a TestRunner class. This
|
||||
class provides for automatic test registration and listing, as well as a way to
|
||||
execute the individual tests. Individual test suites use C++ global
|
||||
constructors
|
||||
to add themselves to a collection of test suites managed by the test runner.
|
||||
The test runner is used to list all of the available tests and to select a test
|
||||
to be run. This is a quite simple class that provides three static methods to
|
||||
provide or Adding and Getting test suites to a collection of tests. See the
|
||||
doxygen for class ``ns3::TestRunner`` for details.
|
||||
|
||||
Test Suite
|
||||
**********
|
||||
|
||||
All |ns3| tests are classified into Test Suites and Test Cases. A
|
||||
test suite is a collection of test cases that completely exercise a given kind
|
||||
of functionality. As described above, test suites can be classified as,
|
||||
|
||||
* Build Verification Tests
|
||||
* Unit Tests
|
||||
* System Tests
|
||||
* Examples
|
||||
* Performance Tests
|
||||
|
||||
This classification is exported from the TestSuite class. This class is quite
|
||||
simple, existing only as a place to export this type and to accumulate test
|
||||
cases. From a user perspective, in order to create a new TestSuite in the
|
||||
system one only has to define a new class that inherits from class ``TestSuite``
|
||||
and perform these two duties.
|
||||
|
||||
The following code will define a new class that can be run by ``test.py``
|
||||
as a ''unit'' test with the display name, ``my-test-suite-name``.
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
class MySuite : public TestSuite
|
||||
{
|
||||
public:
|
||||
MyTestSuite ();
|
||||
};
|
||||
|
||||
MyTestSuite::MyTestSuite ()
|
||||
: TestSuite ("my-test-suite-name", UNIT)
|
||||
{
|
||||
AddTestCase (new MyTestCase, TestCase::QUICK);
|
||||
}
|
||||
|
||||
static MyTestSuite myTestSuite;
|
||||
|
||||
The base class takes care of all of the registration and reporting required to
|
||||
be a good citizen in the test framework.
|
||||
|
||||
Avoid putting initialization logic into the test suite or test case
|
||||
constructors. This is
|
||||
because an instance of the test suite is created at run time
|
||||
(due to the static variable above) regardless of whether the test is being
|
||||
run or not. Instead, the TestCase provides a virtual ``DoSetup`` method
|
||||
that can be specialized to perform setup before ``DoRun`` is called.
|
||||
|
||||
Test Case
|
||||
*********
|
||||
|
||||
Individual tests are created using a TestCase class. Common models for the use
|
||||
of a test case include "one test case per feature", and "one test case per method."
|
||||
Mixtures of these models may be used.
|
||||
|
||||
In order to create a new test case in the system, all one has to do is to inherit
|
||||
from the ``TestCase`` base class, override the constructor to give the test
|
||||
case a name and override the ``DoRun`` method to run the test. Optionally,
|
||||
override also the ``DoSetup`` method.
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
class MyTestCase : public TestCase
|
||||
{
|
||||
MyTestCase ();
|
||||
virtual void DoSetup (void);
|
||||
virtual void DoRun (void);
|
||||
};
|
||||
|
||||
MyTestCase::MyTestCase ()
|
||||
: TestCase ("Check some bit of functionality")
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
MyTestCase::DoRun (void)
|
||||
{
|
||||
NS_TEST_ASSERT_MSG_EQ (true, true, "Some failure message");
|
||||
}
|
||||
|
||||
Utilities
|
||||
*********
|
||||
|
||||
There are a number of utilities of various kinds that are also part of the
|
||||
testing framework. Examples include a generalized pcap file useful for
|
||||
storing test vectors; a generic container useful for transient storage of
|
||||
test vectors during test execution; and tools for generating presentations
|
||||
based on validation and verification testing results.
|
||||
|
||||
These utilities are not documented here, but for example, please see
|
||||
how the TCP tests found in ``src/test/ns3tcp/`` use pcap files and reference
|
||||
output.
|
||||
@@ -1,13 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
|
||||
Overview
|
||||
--------
|
||||
|
||||
This chapter is concerned with the testing and validation of |ns3| software.
|
||||
|
||||
This chapter provides
|
||||
|
||||
* background about terminology and software testing
|
||||
* a description of the ns-3 testing framework
|
||||
* a guide to model developers or new model contributors for how to write tests
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
Tests
|
||||
-----
|
||||
|
||||
.. toctree::
|
||||
|
||||
test-overview
|
||||
test-background
|
||||
test-framework
|
||||
how-to-write-tests
|
||||
@@ -1,87 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
.. highlight:: bash
|
||||
|
||||
Troubleshooting
|
||||
---------------
|
||||
|
||||
This chapter posts some information about possibly common errors in building
|
||||
or running |ns3| programs.
|
||||
|
||||
Please note that the wiki
|
||||
(`<http://www.nsnam.org/wiki/Troubleshooting>`_) may have contributed
|
||||
items.
|
||||
|
||||
Build errors
|
||||
************
|
||||
|
||||
Run-time errors
|
||||
***************
|
||||
|
||||
Sometimes, errors can occur with a program after a successful build. These are
|
||||
run-time errors, and can commonly occur when memory is corrupted or pointer
|
||||
values are unexpectedly null.
|
||||
|
||||
Here is an example of what might occur::
|
||||
|
||||
$ ./waf --run tcp-point-to-point
|
||||
Entering directory '/home/tomh/ns-3-nsc/build'
|
||||
Compilation finished successfully
|
||||
Command ['/home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point'] exited with code -11
|
||||
|
||||
The error message says that the program terminated unsuccessfully, but it is
|
||||
not clear from this information what might be wrong. To examine more
|
||||
closely, try running it under the `gdb debugger
|
||||
<http://sources.redhat.com/gdb/>`_:
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ ./waf --run tcp-point-to-point --command-template="gdb %s"
|
||||
Entering directory '/home/tomh/ns-3-nsc/build'
|
||||
Compilation finished successfully
|
||||
GNU gdb Red Hat Linux (6.3.0.0-1.134.fc5rh)
|
||||
Copyright 2004 Free Software Foundation, Inc.
|
||||
GDB is free software, covered by the GNU General Public License, and you are
|
||||
welcome to change it and/or distribute copies of it under certain conditions.
|
||||
Type "show copying" to see the conditions.
|
||||
There is absolutely no warranty for GDB. Type "show warranty" for details.
|
||||
This GDB was configured as "i386-redhat-linux-gnu"...Using host libthread_db
|
||||
library "/lib/libthread_db.so.1".
|
||||
|
||||
(gdb) run
|
||||
Starting program: /home/tomh/ns-3-nsc/build/debug/examples/tcp-point-to-point
|
||||
Reading symbols from shared object read from target memory...done.
|
||||
Loaded system supplied DSO at 0xf5c000
|
||||
|
||||
Program received signal SIGSEGV, Segmentation fault.
|
||||
0x0804aa12 in main (argc=1, argv=0xbfdfefa4)
|
||||
at ../examples/tcp-point-to-point.cc:136
|
||||
136 Ptr<Socket> localSocket = socketFactory->CreateSocket ();
|
||||
(gdb) p localSocket
|
||||
$1 = {m_ptr = 0x3c5d65}
|
||||
(gdb) p socketFactory
|
||||
$2 = {m_ptr = 0x0}
|
||||
(gdb) quit
|
||||
The program is running. Exit anyway? (y or n) y
|
||||
|
||||
Note first the way the program was invoked-- pass the command to run as an
|
||||
argument to the command template "gdb %s".
|
||||
|
||||
This tells us that there was an attempt to dereference a null pointer
|
||||
socketFactory.
|
||||
|
||||
Let's look around line 136 of tcp-point-to-point, as gdb suggests:
|
||||
|
||||
.. sourcecode:: cpp
|
||||
|
||||
Ptr<SocketFactory> socketFactory = n2->GetObject<SocketFactory> (Tcp::iid);
|
||||
Ptr<Socket> localSocket = socketFactory->CreateSocket ();
|
||||
localSocket->Bind ();
|
||||
|
||||
The culprit here is that the return value of GetObject is not being checked and
|
||||
may be null.
|
||||
|
||||
Sometimes you may need to use the `valgrind memory checker
|
||||
<http://valgrind.org>`_ for more subtle errors. Again, you invoke the use of
|
||||
valgrind similarly::
|
||||
|
||||
$ ./waf --run tcp-point-to-point --command-template="valgrind %s"
|
||||
@@ -1,571 +0,0 @@
|
||||
EPSTOPDF = epstopdf
|
||||
DIA = dia
|
||||
CONVERT = convert
|
||||
|
||||
SRC = ../../src
|
||||
# Temporary source directory, for build
|
||||
SOURCETEMP = source-temp
|
||||
FIGURES = $(SOURCETEMP)/figures
|
||||
|
||||
# list all model library .rst files that need to be copied to $SOURCETEMP
|
||||
SOURCES = \
|
||||
source/conf.py \
|
||||
source/_static \
|
||||
source/index.rst \
|
||||
source/replace.txt \
|
||||
source/organization.rst \
|
||||
source/internet-models.rst \
|
||||
source/network.rst \
|
||||
source/emulation-overview.rst \
|
||||
$(SRC)/antenna/doc/source/antenna.rst \
|
||||
$(SRC)/antenna/doc/source/antenna-design.rst \
|
||||
$(SRC)/antenna/doc/source/antenna-user.rst \
|
||||
$(SRC)/antenna/doc/source/antenna-testing.rst \
|
||||
$(SRC)/aodv/doc/aodv.rst \
|
||||
$(SRC)/applications/doc/applications.rst \
|
||||
$(SRC)/bridge/doc/bridge.rst \
|
||||
$(SRC)/brite/doc/brite.rst \
|
||||
$(SRC)/buildings/doc/source/buildings.rst \
|
||||
$(SRC)/buildings/doc/source/buildings-design.rst \
|
||||
$(SRC)/buildings/doc/source/buildings-user.rst \
|
||||
$(SRC)/buildings/doc/source/buildings-testing.rst \
|
||||
$(SRC)/buildings/doc/source/buildings-references.rst \
|
||||
$(SRC)/click/doc/click.rst \
|
||||
$(SRC)/csma/doc/csma.rst \
|
||||
$(SRC)/dsdv/doc/dsdv.rst \
|
||||
$(SRC)/dsr/doc/dsr.rst \
|
||||
$(SRC)/mpi/doc/distributed.rst \
|
||||
$(SRC)/energy/doc/energy.rst \
|
||||
$(SRC)/fd-net-device/doc/fd-net-device.rst \
|
||||
$(SRC)/tap-bridge/doc/tap.rst \
|
||||
$(SRC)/mesh/doc/source/mesh.rst \
|
||||
$(SRC)/lte/doc/source/lte.rst \
|
||||
$(SRC)/lte/doc/source/lte-user.rst \
|
||||
$(SRC)/lte/doc/source/lte-design.rst \
|
||||
$(SRC)/lte/doc/source/lte-testing.rst \
|
||||
$(SRC)/lte/doc/source/lte-profiling.rst \
|
||||
$(SRC)/lte/doc/source/lte-references.rst \
|
||||
$(SRC)/propagation/doc/propagation.rst \
|
||||
$(SRC)/network/doc/network-overview.rst \
|
||||
$(SRC)/network/doc/packets.rst \
|
||||
$(SRC)/network/doc/error-model.rst \
|
||||
$(SRC)/network/doc/sockets-api.rst \
|
||||
$(SRC)/network/doc/simple.rst \
|
||||
$(SRC)/network/doc/queue.rst \
|
||||
$(SRC)/internet/doc/internet-stack.rst \
|
||||
$(SRC)/internet/doc/ipv4.rst \
|
||||
$(SRC)/internet/doc/ipv6.rst \
|
||||
$(SRC)/internet/doc/routing-overview.rst \
|
||||
$(SRC)/internet/doc/tcp.rst \
|
||||
$(SRC)/internet/doc/codel.rst \
|
||||
$(SRC)/mobility/doc/mobility.rst \
|
||||
$(SRC)/olsr/doc/olsr.rst \
|
||||
$(SRC)/openflow/doc/openflow-switch.rst \
|
||||
$(SRC)/point-to-point/doc/point-to-point.rst \
|
||||
$(SRC)/wifi/doc/source/wifi.rst \
|
||||
$(SRC)/wifi/doc/source/wifi-design.rst \
|
||||
$(SRC)/wifi/doc/source/wifi-user.rst \
|
||||
$(SRC)/wifi/doc/source/wifi-testing.rst \
|
||||
$(SRC)/wifi/doc/source/wifi-references.rst \
|
||||
$(SRC)/wimax/doc/wimax.rst \
|
||||
$(SRC)/uan/doc/uan.rst \
|
||||
$(SRC)/topology-read/doc/topology.rst \
|
||||
$(SRC)/spectrum/doc/spectrum.rst \
|
||||
$(SRC)/stats/doc/adaptor.rst \
|
||||
$(SRC)/stats/doc/aggregator.rst \
|
||||
$(SRC)/stats/doc/collector.rst \
|
||||
$(SRC)/stats/doc/data-collection-helpers.rst \
|
||||
$(SRC)/stats/doc/data-collection-overview.rst \
|
||||
$(SRC)/stats/doc/data-collection.rst \
|
||||
$(SRC)/stats/doc/probe.rst \
|
||||
$(SRC)/stats/doc/scope-and-limitations.rst \
|
||||
$(SRC)/netanim/doc/animation.rst \
|
||||
$(SRC)/flow-monitor/doc/flow-monitor.rst \
|
||||
$(SRC)/wave/doc/wave.rst \
|
||||
$(SRC)/sixlowpan/doc/sixlowpan.rst \
|
||||
$(SRC)/lr-wpan/doc/lr-wpan.rst \
|
||||
|
||||
# list all model library figure files that need to be copied to
|
||||
# $SOURCETEMP/figures. For each figure to be included in all
|
||||
# documentation formats (html, latex...) the following formats are supported:
|
||||
# 1) a single .dia file (preferred option, because it can be edited)
|
||||
# 2) a single .eps file
|
||||
# 3) both a .pdf and .png file
|
||||
SOURCEFIGS = \
|
||||
figures/testbed.dia \
|
||||
figures/emulated-channel.dia \
|
||||
$(SRC)/antenna/doc/source/figures/antenna-coordinate-system.dia \
|
||||
$(SRC)/network/doc/packet.dia \
|
||||
$(SRC)/network/doc/node.dia \
|
||||
$(SRC)/network/doc/buffer.dia \
|
||||
$(SRC)/network/doc/sockets-overview.dia \
|
||||
$(SRC)/internet/doc/internet-node-send.dia \
|
||||
$(SRC)/internet/doc/internet-node-recv.dia \
|
||||
$(SRC)/internet/doc/routing.dia \
|
||||
$(SRC)/internet/doc/routing-specialization.dia \
|
||||
$(SRC)/wifi/doc/source/figures/WifiArchitecture.dia \
|
||||
$(SRC)/wifi/doc/source/figures/snir.dia \
|
||||
$(SRC)/wimax/doc/WimaxArchitecture.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-ctrl-arch.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-data-flow-dl.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-data-flow-ul.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-profiling-scenario.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-topology.dia \
|
||||
$(SRC)/lte/doc/source/figures/epc-topology-x2-enhanced.dia \
|
||||
$(SRC)/lte/doc/source/figures/eutran-profiling-scenario.dia \
|
||||
$(SRC)/lte/doc/source/figures/ff-example.dia \
|
||||
$(SRC)/lte/doc/source/figures/ff-mac-saps.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-arch-enb-data.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-arch-enb-ctrl.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-arch-ue-data.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-arch-ue-ctrl.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-enb-phy.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-ue-phy.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-epc-x2-handover-seq-diagram.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-epc-e2e-data-protocol-stack.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-interference-test-scenario.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-subframe-structure.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-epc-x2-interface.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-harq-architecture.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-harq-processes-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/ue-meas-consumer.dia \
|
||||
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-motion.dia \
|
||||
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-a1.dia \
|
||||
$(SRC)/lte/doc/source/figures/ue-meas-piecewise-a1-hys.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-cell-selection-timeline.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-cell-selection-scenario.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-handover-target-scenario.dia \
|
||||
$(SRC)/lte/doc/source/figures/lena-dual-stripe.eps \
|
||||
$(SRC)/lte/doc/source/figures/lte-mcs-index.eps \
|
||||
$(SRC)/lte/doc/source/figures/lenaThrTestCase1.eps \
|
||||
$(SRC)/lte/doc/source/figures/lenaThrTestCase2.eps \
|
||||
$(SRC)/lte/doc/source/figures/runningTime10s.eps \
|
||||
$(SRC)/lte/doc/source/figures/epcRunningTime.eps \
|
||||
$(SRC)/lte/doc/source/figures/propagationModel.eps \
|
||||
$(SRC)/lte/doc/source/figures/simulationTime.eps \
|
||||
$(SRC)/lte/doc/source/figures/epcSimulationTime.eps \
|
||||
$(SRC)/lte/doc/source/figures/epcEutranRunningTime.eps \
|
||||
$(SRC)/lte/doc/source/figures/profiling-memory.eps \
|
||||
$(SRC)/lte/doc/source/figures/lte-rlc-implementation-model.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-rlc-data-txon-dl.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-rlc-data-retx-dl.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-rlc-data-txon-ul.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-rlc-data-retx-ul.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-epc-x2-entity-saps.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-strongest-cell-handover-algorithm.eps \
|
||||
$(SRC)/lte/doc/source/figures/lte-phy-interference.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-phy-interference.png \
|
||||
$(SRC)/lte/doc/source/figures/helpers.pdf \
|
||||
$(SRC)/lte/doc/source/figures/helpers.png \
|
||||
$(SRC)/lte/doc/source/figures/mac-random-access-contention.pdf \
|
||||
$(SRC)/lte/doc/source/figures/mac-random-access-contention.png \
|
||||
$(SRC)/lte/doc/source/figures/mac-random-access-noncontention.pdf \
|
||||
$(SRC)/lte/doc/source/figures/mac-random-access-noncontention.png \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-establishment.pdf \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-establishment.png \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration.pdf \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration.png \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration-handover.pdf \
|
||||
$(SRC)/lte/doc/source/figures/rrc-connection-reconfiguration-handover.png \
|
||||
$(SRC)/lte/doc/source/figures/nas-attach.pdf \
|
||||
$(SRC)/lte/doc/source/figures/nas-attach.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-enb-rrc-states.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-enb-rrc-states.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-ue-rrc-states.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-ue-rrc-states.png \
|
||||
$(SRC)/lte/doc/source/figures/fading_pedestrian.pdf \
|
||||
$(SRC)/lte/doc/source/figures/fading_pedestrian.png \
|
||||
$(SRC)/lte/doc/source/figures/fading_vehicular.pdf \
|
||||
$(SRC)/lte/doc/source/figures/fading_vehicular.png \
|
||||
$(SRC)/lte/doc/source/figures/fading_urban_3kmph.pdf \
|
||||
$(SRC)/lte/doc/source/figures/fading_urban_3kmph.png \
|
||||
$(SRC)/lte/doc/source/figures/fr-enhanced-fractional-frequency-reuse-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-full-frequency-reuse-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-hard-frequency-reuse-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-soft-fractional-frequency-reuse-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-soft-frequency-reuse-scheme-v1.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-soft-frequency-reuse-scheme-v2.dia \
|
||||
$(SRC)/lte/doc/source/figures/fr-strict-frequency-reuse-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/ffr-distributed-scheme.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-soft-1-rem.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-soft-1-rem.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-ffr-soft-2-spectrum-trace.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-ffr-soft-2-spectrum-trace.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-1-rem.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-1-rem.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-2-rem.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-2-rem.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-3-rem.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-fr-hard-3-rem.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_1_4.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_1_4.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_5_8.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_5_8.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_9_12.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_9_12.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_13_16.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_13_16.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_17_20.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_17_20.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_21_24.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_21_24.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_25_28.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_25_28.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_29_29.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_29_29.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_2_test.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_2_test.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_12_test.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_12_test.pdf \
|
||||
$(SRC)/lte/doc/source/figures/MCS_16_test.png \
|
||||
$(SRC)/lte/doc/source/figures/MCS_16_test.pdf \
|
||||
$(SRC)/lte/doc/source/figures/miesm_scheme.pdf \
|
||||
$(SRC)/lte/doc/source/figures/miesm_scheme.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-dl-power-control.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-ffr-scheduling.dia \
|
||||
$(SRC)/lte/doc/source/figures/lte-handover-campaign-rem.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-handover-campaign-rem.png \
|
||||
$(SRC)/lte/doc/source/figures/lte-legacy-handover-algorithm.pdf \
|
||||
$(SRC)/lte/doc/source/figures/lte-legacy-handover-algorithm.png \
|
||||
$(SRC)/uan/doc/auvmobility-classes.dia \
|
||||
$(SRC)/stats/doc/Stat-framework-arch.png \
|
||||
$(SRC)/stats/doc/Wifi-default.png \
|
||||
$(SRC)/stats/doc/dcf-overview.dia \
|
||||
$(SRC)/stats/doc/dcf-overview-with-aggregation.dia \
|
||||
$(SRC)/stats/doc/file-example.png \
|
||||
$(SRC)/stats/doc/gnuplot-aggregator.png \
|
||||
$(SRC)/stats/doc/gnuplot-example.png \
|
||||
$(SRC)/stats/doc/gnuplot-helper-example.png \
|
||||
$(SRC)/stats/doc/seventh-packet-byte-count.png \
|
||||
$(SRC)/netanim/doc/figures/PacketStatistics.png \
|
||||
$(SRC)/netanim/doc/figures/PacketStatistics.pdf \
|
||||
$(SRC)/netanim/doc/figures/NetAnim_3_105.png \
|
||||
$(SRC)/netanim/doc/figures/NetAnim_3_105.pdf \
|
||||
$(SRC)/netanim/doc/figures/Trajectory.png \
|
||||
$(SRC)/netanim/doc/figures/Trajectory.pdf \
|
||||
$(SRC)/netanim/doc/figures/NodeCountersChart.png \
|
||||
$(SRC)/netanim/doc/figures/NodeCountersChart.pdf \
|
||||
$(SRC)/netanim/doc/figures/NodeCountersTable.png \
|
||||
$(SRC)/netanim/doc/figures/NodeCountersTable.pdf \
|
||||
$(SRC)/netanim/doc/figures/RoutingTables.png \
|
||||
$(SRC)/netanim/doc/figures/RoutingTables.pdf \
|
||||
$(SRC)/netanim/doc/figures/PacketTimeline.png \
|
||||
$(SRC)/netanim/doc/figures/PacketTimeline.pdf \
|
||||
$(SRC)/spectrum/doc/spectrum-channel-phy-interface.png \
|
||||
$(SRC)/spectrum/doc/spectrum-channel-phy-interface.pdf \
|
||||
$(SRC)/spectrum/doc/spectrum-analyzer-example.eps \
|
||||
$(SRC)/spectrum/doc/spectrum-tv-rand-geo-points.eps \
|
||||
$(SRC)/spectrum/doc/spectrum-tv-8vsb.png \
|
||||
$(SRC)/spectrum/doc/spectrum-tv-cofdm.png \
|
||||
$(SRC)/lr-wpan/doc/lr-wpan-arch.dia \
|
||||
$(SRC)/lr-wpan/doc/lr-wpan-data-example.dia \
|
||||
$(SRC)/lr-wpan/doc/lr-wpan-primitives.dia \
|
||||
$(SRC)/lr-wpan/doc/802-15-4-ber.eps \
|
||||
$(SRC)/lr-wpan/doc/802-15-4-per-sens.eps \
|
||||
$(SRC)/lr-wpan/doc/802-15-4-psr-distance.eps
|
||||
|
||||
# specify figures from which .png and .pdf figures need to be
|
||||
# generated (all dia and eps figures)
|
||||
IMAGES_EPS = \
|
||||
$(FIGURES)/testbed.eps \
|
||||
$(FIGURES)/emulated-channel.eps \
|
||||
$(FIGURES)/antenna-coordinate-system.eps \
|
||||
$(FIGURES)/packet.eps \
|
||||
$(FIGURES)/node.eps \
|
||||
$(FIGURES)/buffer.eps \
|
||||
$(FIGURES)/sockets-overview.eps \
|
||||
$(FIGURES)/internet-node-send.eps \
|
||||
$(FIGURES)/internet-node-recv.eps \
|
||||
$(FIGURES)/routing.eps \
|
||||
$(FIGURES)/routing-specialization.eps \
|
||||
$(FIGURES)/WifiArchitecture.eps \
|
||||
$(FIGURES)/snir.eps \
|
||||
$(FIGURES)/WimaxArchitecture.eps \
|
||||
$(FIGURES)/dcf-overview.eps \
|
||||
$(FIGURES)/dcf-overview-with-aggregation.eps \
|
||||
$(FIGURES)/epc-ctrl-arch.eps \
|
||||
$(FIGURES)/epc-data-flow-dl.eps \
|
||||
$(FIGURES)/epc-data-flow-ul.eps \
|
||||
$(FIGURES)/epc-profiling-scenario.eps \
|
||||
$(FIGURES)/epc-topology.eps \
|
||||
$(FIGURES)/epc-topology-x2-enhanced.eps \
|
||||
$(FIGURES)/eutran-profiling-scenario.eps \
|
||||
$(FIGURES)/ff-example.eps \
|
||||
$(FIGURES)/ff-mac-saps.eps \
|
||||
$(FIGURES)/lte-dl-power-control.eps \
|
||||
$(FIGURES)/lte-ffr-scheduling.eps \
|
||||
$(FIGURES)/fr-enhanced-fractional-frequency-reuse-scheme.eps \
|
||||
$(FIGURES)/fr-full-frequency-reuse-scheme.eps \
|
||||
$(FIGURES)/fr-hard-frequency-reuse-scheme.eps \
|
||||
$(FIGURES)/fr-soft-fractional-frequency-reuse-scheme.eps \
|
||||
$(FIGURES)/fr-soft-frequency-reuse-scheme-v1.eps \
|
||||
$(FIGURES)/fr-soft-frequency-reuse-scheme-v2.eps \
|
||||
$(FIGURES)/fr-strict-frequency-reuse-scheme.eps \
|
||||
$(FIGURES)/ffr-distributed-scheme.eps \
|
||||
$(FIGURES)/lte-arch-enb-data.eps \
|
||||
$(FIGURES)/lte-arch-enb-ctrl.eps \
|
||||
$(FIGURES)/lte-arch-ue-data.eps \
|
||||
$(FIGURES)/lte-arch-ue-ctrl.eps \
|
||||
$(FIGURES)/lte-enb-phy.eps \
|
||||
$(FIGURES)/lte-ue-phy.eps \
|
||||
$(FIGURES)/lte-epc-e2e-data-protocol-stack.eps \
|
||||
$(FIGURES)/lte-interference-test-scenario.eps \
|
||||
$(FIGURES)/lte-subframe-structure.eps \
|
||||
$(FIGURES)/lte-epc-x2-interface.eps \
|
||||
$(FIGURES)/lte-harq-architecture.eps \
|
||||
$(FIGURES)/lte-harq-processes-scheme.eps \
|
||||
$(FIGURES)/ue-meas-consumer.eps \
|
||||
$(FIGURES)/ue-meas-piecewise-motion.eps \
|
||||
$(FIGURES)/ue-meas-piecewise-a1.eps \
|
||||
$(FIGURES)/ue-meas-piecewise-a1-hys.eps \
|
||||
$(FIGURES)/lte-cell-selection-timeline.eps \
|
||||
$(FIGURES)/lte-cell-selection-scenario.eps \
|
||||
$(FIGURES)/lte-handover-target-scenario.eps \
|
||||
$(FIGURES)/lena-dual-stripe.eps \
|
||||
$(FIGURES)/lte-mcs-index.eps \
|
||||
$(FIGURES)/lenaThrTestCase1.eps \
|
||||
$(FIGURES)/lenaThrTestCase2.eps \
|
||||
$(FIGURES)/runningTime10s.eps \
|
||||
$(FIGURES)/epcRunningTime.eps \
|
||||
$(FIGURES)/propagationModel.eps \
|
||||
$(FIGURES)/simulationTime.eps \
|
||||
$(FIGURES)/epcSimulationTime.eps \
|
||||
$(FIGURES)/epcEutranRunningTime.eps \
|
||||
$(FIGURES)/profiling-memory.eps \
|
||||
$(FIGURES)/lte-rlc-implementation-model.eps \
|
||||
$(FIGURES)/lte-rlc-data-txon-dl.eps \
|
||||
$(FIGURES)/lte-rlc-data-retx-dl.eps \
|
||||
$(FIGURES)/lte-rlc-data-txon-ul.eps \
|
||||
$(FIGURES)/lte-rlc-data-retx-ul.eps \
|
||||
$(FIGURES)/lte-epc-x2-handover-seq-diagram.eps \
|
||||
$(FIGURES)/lte-epc-x2-entity-saps.eps \
|
||||
$(FIGURES)/lte-strongest-cell-handover-algorithm.eps \
|
||||
$(FIGURES)/auvmobility-classes.eps \
|
||||
$(FIGURES)/spectrum-analyzer-example.eps \
|
||||
$(FIGURES)/spectrum-tv-rand-geo-points.eps \
|
||||
$(FIGURES)/lr-wpan-primitives.eps \
|
||||
$(FIGURES)/lr-wpan-data-example.eps \
|
||||
$(FIGURES)/lr-wpan-arch.eps \
|
||||
$(FIGURES)/802-15-4-ber.eps \
|
||||
$(FIGURES)/802-15-4-per-sens.eps \
|
||||
$(FIGURES)/802-15-4-psr-distance.eps
|
||||
|
||||
# rescale pdf figures as necessary
|
||||
$(FIGURES)/testbed.pdf_width = 5in
|
||||
$(FIGURES)/emulated-channel.pdf_width = 6in
|
||||
$(FIGURES)/antenna-coordinate-system.pdf_width = 7cm
|
||||
$(FIGURES)/node.pdf_width = 5in
|
||||
$(FIGURES)/packet.pdf_width = 4in
|
||||
$(FIGURES)/buffer.pdf_width = 15cm
|
||||
$(FIGURES)/sockets-overview.pdf_width = 10cm
|
||||
$(FIGURES)/internet-node-send.pdf_width = 5in
|
||||
$(FIGURES)/internet-node-recv.pdf_width = 5in
|
||||
$(FIGURES)/routing.pdf_width = 6in
|
||||
$(FIGURES)/routing-specialization.pdf_width = 5in
|
||||
$(FIGURES)/snir.pdf_width = 3in
|
||||
$(FIGURES)/lte-interference-test-scenario.pdf_width = 3in
|
||||
$(FIGURES)/epc-ctrl-arch.pdf_width = 8cm
|
||||
$(FIGURES)/epc-topology.pdf_width = 4in
|
||||
$(FIGURES)/epc-topology-x2-enhanced.pdf_width = 14cm
|
||||
$(FIGURES)/lte-arch-data-rrc-pdcp-rlc.pdf_width = 3in
|
||||
$(FIGURES)/lte-epc-e2e-data-protocol-stack.pdf_width = 15cm
|
||||
$(FIGURES)/ff-mac-saps.pdf_width = 5in
|
||||
$(FIGURES)/ff-example.pdf_width = 5in
|
||||
$(FIGURES)/lte-dl-power-control.pdf_width = 8cm
|
||||
$(FIGURES)/lte-ffr-scheduling.pdf_width = 8cm
|
||||
$(FIGURES)/fr-enhanced-fractional-frequency-reuse-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/fr-full-frequency-reuse-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/fr-hard-frequency-reuse-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/fr-soft-fractional-frequency-reuse-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/fr-soft-frequency-reuse-scheme-v1.pdf_width = 8cm
|
||||
$(FIGURES)/fr-soft-frequency-reuse-scheme-v2.pdf_width = 8cm
|
||||
$(FIGURES)/fr-strict-frequency-reuse-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/ffr-distributed-scheme.pdf_width = 8cm
|
||||
$(FIGURES)/lte-arch-enb-data.pdf_width = 6cm
|
||||
$(FIGURES)/lte-arch-enb-ctrl.pdf_width = 10cm
|
||||
$(FIGURES)/lte-arch-ue-data.pdf_width = 6cm
|
||||
$(FIGURES)/lte-arch-ue-ctrl.pdf_width = 10cm
|
||||
$(FIGURES)/lte-rlc-implementation-model.pdf_width = 20in
|
||||
$(FIGURES)/lte-rlc-data-txon-dl.pdf_width = 10cm
|
||||
$(FIGURES)/lte-rlc-data-txon-ul.pdf_width = 10cm
|
||||
$(FIGURES)/lte-rlc-data-retx-dl.pdf_width = 10cm
|
||||
$(FIGURES)/lte-rlc-data-retx-ul.pdf_width = 10cm
|
||||
$(FIGURES)/lte-epc-x2-entity-saps.pdf_width = 12cm
|
||||
$(FIGURES)/lte-phy-interference.pdf_width = 12cm
|
||||
$(FIGURES)/lte-subframe-structure.pdf_width = 2in
|
||||
$(FIGURES)/mac-random-access-contention.pdf_width = 10cm
|
||||
$(FIGURES)/mac-random-access-noncontention.pdf_width = 15cm
|
||||
$(FIGURES)/helpers.pdf_width = 8cm
|
||||
$(FIGURES)/auvmobility-classes.pdf_width = 10cm
|
||||
$(FIGURES)/spectrum-analyzer-example.pdf_width = 15cm
|
||||
$(FIGURES)/spectrum-tv-rand-geo-points.pdf_width = 8cm
|
||||
$(FIGURES)/lr-wpan-primitives.pdf_width = 3in
|
||||
$(FIGURES)/lr-wpan-arch.pdf_width = 2in
|
||||
|
||||
IMAGES_PNG = ${IMAGES_EPS:.eps=.png}
|
||||
IMAGES_PDF = ${IMAGES_EPS:.eps=.pdf}
|
||||
|
||||
IMAGES = $(IMAGES_EPS) $(IMAGES_PNG) $(IMAGES_PDF)
|
||||
|
||||
RESCALE = ../../utils/rescale-pdf.sh
|
||||
|
||||
%.eps : %.dia
|
||||
@echo dia $(notdir $<)
|
||||
@$(DIA) -t eps $< -e $@ >/dev/null
|
||||
|
||||
%.png : %.dia
|
||||
@echo dia $(notdir $<)
|
||||
@$(DIA) -t png $< -e $@ >/dev/null
|
||||
|
||||
%.png : %.eps
|
||||
@echo convert $(notdir $<)
|
||||
@$(CONVERT) $< $@ >/dev/null
|
||||
|
||||
%.pdf : %.eps
|
||||
@echo epstopdf $(notdir $<)
|
||||
@$(EPSTOPDF) $< -o=$@ >/dev/null
|
||||
@if test x$($@_width) != x; then $(RESCALE) $($@_width) $@ ; fi
|
||||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS =
|
||||
SPHINXBUILD = sphinx-build
|
||||
PAPER =
|
||||
BUILDDIR = build
|
||||
|
||||
# Internal variables.
|
||||
PAPEROPT_a4 = -D latex_paper_size=a4
|
||||
PAPEROPT_letter = -D latex_paper_size=letter
|
||||
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) $(SOURCETEMP)
|
||||
|
||||
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest
|
||||
|
||||
.NOTPARALLEL:
|
||||
|
||||
help:
|
||||
@echo "Please use \`make <target>' where <target> is one of"
|
||||
@echo " html to make standalone HTML files"
|
||||
@echo " dirhtml to make HTML files named index.html in directories"
|
||||
@echo " singlehtml to make a single large HTML file"
|
||||
@echo " pickle to make pickle files"
|
||||
@echo " json to make JSON files"
|
||||
@echo " htmlhelp to make HTML files and a HTML help project"
|
||||
@echo " qthelp to make HTML files and a qthelp project"
|
||||
@echo " devhelp to make HTML files and a Devhelp project"
|
||||
@echo " epub to make an epub"
|
||||
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
|
||||
@echo " latexpdf to make LaTeX files and run them through pdflatex"
|
||||
@echo " text to make text files"
|
||||
@echo " man to make manual pages"
|
||||
@echo " changes to make an overview of all changed/added/deprecated items"
|
||||
@echo " linkcheck to check all external links for integrity"
|
||||
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
|
||||
|
||||
copy-sources: $(SOURCES)
|
||||
@mkdir -p $(SOURCETEMP)
|
||||
@mkdir -p $(FIGURES)
|
||||
@cp -r -p $(SOURCES) $(SOURCETEMP)
|
||||
@cp -r -p $(SOURCEFIGS) $(FIGURES)
|
||||
|
||||
clean:
|
||||
-rm -rf $(BUILDDIR)/*
|
||||
-rm -rf $(SOURCETEMP)
|
||||
|
||||
frag: pickle
|
||||
@if test ! -d $(BUILDDIR)/frag; then mkdir $(BUILDDIR)/frag; fi
|
||||
pushd $(BUILDDIR)/frag && ../../pickle-to-xml.py ../pickle/index.fpickle > navigation.xml && popd
|
||||
cp -r $(BUILDDIR)/pickle/_images $(BUILDDIR)/frag
|
||||
|
||||
html: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
|
||||
|
||||
dirhtml: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
|
||||
|
||||
singlehtml: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
|
||||
@echo
|
||||
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
|
||||
|
||||
pickle: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
|
||||
@echo
|
||||
@echo "Build finished; now you can process the pickle files."
|
||||
|
||||
json: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
|
||||
@echo
|
||||
@echo "Build finished; now you can process the JSON files."
|
||||
|
||||
htmlhelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run HTML Help Workshop with the" \
|
||||
".hhp project file in $(BUILDDIR)/htmlhelp."
|
||||
|
||||
qthelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
|
||||
@echo
|
||||
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
|
||||
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
|
||||
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/ns-3.qhcp"
|
||||
@echo "To view the help file:"
|
||||
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/ns-3.qhc"
|
||||
|
||||
devhelp: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
|
||||
@echo
|
||||
@echo "Build finished."
|
||||
@echo "To view the help file:"
|
||||
@echo "# mkdir -p $$HOME/.local/share/devhelp/ns-3"
|
||||
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/ns-3"
|
||||
@echo "# devhelp"
|
||||
|
||||
epub: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
|
||||
@echo
|
||||
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
|
||||
|
||||
latex: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo
|
||||
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
|
||||
@echo "Run \`make' in that directory to run these through (pdf)latex" \
|
||||
"(use \`make latexpdf' here to do that automatically)."
|
||||
|
||||
latexpdf: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
|
||||
@echo "Running LaTeX files through pdflatex..."
|
||||
make -C $(BUILDDIR)/latex all-pdf
|
||||
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
|
||||
|
||||
text: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
|
||||
@echo
|
||||
@echo "Build finished. The text files are in $(BUILDDIR)/text."
|
||||
|
||||
man: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
|
||||
@echo
|
||||
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
|
||||
|
||||
changes: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
|
||||
@echo
|
||||
@echo "The overview file is in $(BUILDDIR)/changes."
|
||||
|
||||
linkcheck: copy-sources $(IMAGEs)
|
||||
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
|
||||
@echo
|
||||
@echo "Link check complete; look for any errors in the above output " \
|
||||
"or in $(BUILDDIR)/linkcheck/output.txt."
|
||||
|
||||
doctest: copy-sources $(IMAGES)
|
||||
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
|
||||
@echo "Testing of doctests in the sources finished, look at the " \
|
||||
"results in $(BUILDDIR)/doctest/output.txt."
|
||||
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
This directory stores .rst files that are used to build the model library
|
||||
documentation but that are not stored with a particular module.
|
||||
@@ -1,216 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# ns-3 documentation build configuration file, created by
|
||||
# sphinx-quickstart on Tue Dec 14 09:00:39 2010.
|
||||
#
|
||||
# This file is execfile()d with the current directory set to its containing dir.
|
||||
#
|
||||
# Note that not all possible configuration values are present in this
|
||||
# autogenerated file.
|
||||
#
|
||||
# All configuration values have a default; values that are commented out
|
||||
# serve to show the default.
|
||||
|
||||
import sys, os
|
||||
|
||||
# If extensions (or modules to document with autodoc) are in another directory,
|
||||
# add these directories to sys.path here. If the directory is relative to the
|
||||
# documentation root, use os.path.abspath to make it absolute, like shown here.
|
||||
#sys.path.insert(0, os.path.abspath('.'))
|
||||
|
||||
# -- General configuration -----------------------------------------------------
|
||||
|
||||
# If your documentation needs a minimal Sphinx version, state it here.
|
||||
#needs_sphinx = '1.0'
|
||||
|
||||
# Add any Sphinx extension module names here, as strings. They can be extensions
|
||||
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
|
||||
extensions = ['sphinx.ext.pngmath']
|
||||
|
||||
# Add any paths that contain templates here, relative to this directory.
|
||||
templates_path = ['_templates']
|
||||
|
||||
# The suffix of source filenames.
|
||||
source_suffix = '.rst'
|
||||
|
||||
# The encoding of source files.
|
||||
#source_encoding = 'utf-8-sig'
|
||||
|
||||
# The master toctree document.
|
||||
master_doc = 'index'
|
||||
|
||||
# General information about the project.
|
||||
project = u'ns-3'
|
||||
copyright = u'2011, ns-3 project'
|
||||
|
||||
# The version info for the project you're documenting, acts as replacement for
|
||||
# |version| and |release|, also used in various other places throughout the
|
||||
# built documents.
|
||||
#
|
||||
# The short X.Y version.
|
||||
version = 'ns-3-dev'
|
||||
# The full version, including alpha/beta/rc tags.
|
||||
release = 'ns-3-dev'
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
#language = None
|
||||
|
||||
# There are two options for replacing |today|: either, you set today to some
|
||||
# non-false value, then it is used:
|
||||
#today = ''
|
||||
# Else, today_fmt is used as the format for a strftime call.
|
||||
#today_fmt = '%B %d, %Y'
|
||||
|
||||
# List of patterns, relative to source directory, that match files and
|
||||
# directories to ignore when looking for source files.
|
||||
exclude_patterns = []
|
||||
|
||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||
#default_role = None
|
||||
|
||||
# If true, '()' will be appended to :func: etc. cross-reference text.
|
||||
#add_function_parentheses = True
|
||||
|
||||
# If true, the current module name will be prepended to all description
|
||||
# unit titles (such as .. function::).
|
||||
#add_module_names = True
|
||||
|
||||
# If true, sectionauthor and moduleauthor directives will be shown in the
|
||||
# output. They are ignored by default.
|
||||
#show_authors = False
|
||||
|
||||
# The name of the Pygments (syntax highlighting) style to use.
|
||||
pygments_style = 'sphinx'
|
||||
|
||||
# A list of ignored prefixes for module index sorting.
|
||||
#modindex_common_prefix = []
|
||||
|
||||
|
||||
# -- Options for HTML output ---------------------------------------------------
|
||||
|
||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||
# a list of builtin themes.
|
||||
html_theme = 'ns3_html_theme'
|
||||
|
||||
# Theme options are theme-specific and customize the look and feel of a theme
|
||||
# further. For a list of options available for each theme, see the
|
||||
# documentation.
|
||||
#html_theme_options = {}
|
||||
|
||||
# Add any paths that contain custom themes here, relative to this directory.
|
||||
html_theme_path = ['../..']
|
||||
|
||||
# The name for this set of Sphinx documents. If None, it defaults to
|
||||
# "<project> v<release> documentation".
|
||||
html_title = 'Model Library'
|
||||
|
||||
# A shorter title for the navigation bar. Default is the same as html_title.
|
||||
html_short_title = 'Models'
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top
|
||||
# of the sidebar.
|
||||
#html_logo = None
|
||||
|
||||
# The name of an image file (within the static path) to use as favicon of the
|
||||
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
|
||||
# pixels large.
|
||||
#html_favicon = None
|
||||
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static']
|
||||
|
||||
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
|
||||
# using the given strftime format.
|
||||
html_last_updated_fmt = '%b %d, %Y %H:%M'
|
||||
|
||||
# If true, SmartyPants will be used to convert quotes and dashes to
|
||||
# typographically correct entities.
|
||||
#html_use_smartypants = True
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
#html_sidebars = {}
|
||||
|
||||
# Additional templates that should be rendered to pages, maps page names to
|
||||
# template names.
|
||||
#html_additional_pages = {}
|
||||
|
||||
# If false, no module index is generated.
|
||||
#html_domain_indices = True
|
||||
|
||||
# If false, no index is generated.
|
||||
#html_use_index = True
|
||||
|
||||
# If true, the index is split into individual pages for each letter.
|
||||
#html_split_index = False
|
||||
|
||||
# If true, links to the reST sources are added to the pages.
|
||||
#html_show_sourcelink = True
|
||||
|
||||
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
|
||||
#html_show_sphinx = True
|
||||
|
||||
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
|
||||
#html_show_copyright = True
|
||||
|
||||
# If true, an OpenSearch description file will be output, and all pages will
|
||||
# contain a <link> tag referring to it. The value of this option must be the
|
||||
# base URL from which the finished HTML is served.
|
||||
#html_use_opensearch = ''
|
||||
|
||||
# This is the file name suffix for HTML files (e.g. ".xhtml").
|
||||
#html_file_suffix = None
|
||||
|
||||
# Output file base name for HTML help builder.
|
||||
htmlhelp_basename = 'ns-3doc'
|
||||
|
||||
|
||||
# -- Options for LaTeX output --------------------------------------------------
|
||||
|
||||
# The paper size ('letter' or 'a4').
|
||||
#latex_paper_size = 'letter'
|
||||
|
||||
# The font size ('10pt', '11pt' or '12pt').
|
||||
#latex_font_size = '10pt'
|
||||
|
||||
# Grouping the document tree into LaTeX files. List of tuples
|
||||
# (source start file, target name, title, author, documentclass [howto/manual]).
|
||||
latex_documents = [
|
||||
('index', 'ns-3-model-library.tex', u'ns-3 Model Library',
|
||||
u'ns-3 project', 'manual'),
|
||||
]
|
||||
|
||||
# The name of an image file (relative to this directory) to place at the top of
|
||||
# the title page.
|
||||
latex_logo = '../../ns3_html_theme/static/ns-3.png'
|
||||
|
||||
# For "manual" documents, if this is true, then toplevel headings are parts,
|
||||
# not chapters.
|
||||
#latex_use_parts = False
|
||||
|
||||
# If true, show page references after internal links.
|
||||
#latex_show_pagerefs = False
|
||||
|
||||
# If true, show URL addresses after external links.
|
||||
#latex_show_urls = False
|
||||
|
||||
# Additional stuff for the LaTeX preamble.
|
||||
#latex_preamble = ''
|
||||
|
||||
# Documents to append as an appendix to all manuals.
|
||||
#latex_appendices = []
|
||||
|
||||
# If false, no module index is generated.
|
||||
#latex_domain_indices = True
|
||||
|
||||
|
||||
# -- Options for manual page output --------------------------------------------
|
||||
|
||||
# One entry per manual page. List of tuples
|
||||
# (source start file, name, description, authors, manual section).
|
||||
man_pages = [
|
||||
('index', 'ns-3-model-library', u'ns-3 Model Library',
|
||||
[u'ns-3 project'], 1)
|
||||
]
|
||||
@@ -1,86 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
|
||||
Emulation Overview
|
||||
------------------
|
||||
|
||||
|ns3| has been designed for integration into testbed and virtual machine
|
||||
environments. We have addressed this need by providing two kinds of net devices.
|
||||
The first kind of device is a file descriptor net device (``FdNetDevice``),
|
||||
which is a generic device type that can read and write from a file descriptor.
|
||||
By associating this file descriptor with different things on the host
|
||||
system, different capabilities can be provided. For instance, the
|
||||
FdNetDevice can be associated with an underlying packet socket to provide
|
||||
emulation capabilities. This allows |ns3| simulations
|
||||
to send data on a "real" network. The second kind, called a ``Tap``
|
||||
``NetDevice`` allows a "real" host to participate in an |ns3| simulation as if
|
||||
it were one of the simulated nodes. An |ns3| simulation may be constructed with
|
||||
any combination of simulated or emulated devices.
|
||||
|
||||
**Note:** Prior to ns-3.17, the emulation capability was provided by a
|
||||
special device called an ``Emu`` NetDevice; the ``Emu`` NetDevice has
|
||||
been replaced by the ``FdNetDevice``.
|
||||
|
||||
One of the use-cases we want to support is that of a testbed. A concrete example
|
||||
of an environment of this kind is the ORBIT testbed. ORBIT is a laboratory
|
||||
emulator/field trial network arranged as a two dimensional grid of 400 802.11
|
||||
radio nodes. We integrate with ORBIT by using their "imaging" process to load
|
||||
and run |ns3| simulations on the ORBIT array. We can use our
|
||||
``EmuFdNetDevice``
|
||||
to drive the hardware in the testbed and we can accumulate results either using
|
||||
the |ns3| tracing and logging functions, or the native ORBIT data gathering
|
||||
techniques. See `<http://www.orbit-lab.org/>`_ for details on the ORBIT
|
||||
testbed.
|
||||
|
||||
A simulation of this kind is shown in the following figure:
|
||||
|
||||
.. _testbed:
|
||||
|
||||
.. figure:: figures/testbed.*
|
||||
|
||||
Example Implementation of Testbed Emulation.
|
||||
|
||||
You can see that there are separate hosts, each running a subset of a "global"
|
||||
simulation. Instead of an |ns3| channel connecting the hosts, we use real
|
||||
hardware provided by the testbed. This allows |ns3| applications and protocol
|
||||
stacks attached to a simulation node to communicate over real hardware.
|
||||
|
||||
We expect the primary use for this configuration will be to generate repeatable
|
||||
experimental results in a real-world network environment that includes all of
|
||||
the |ns3| tracing, logging, visualization and statistics gathering tools.
|
||||
|
||||
In what can be viewed as essentially an inverse configuration, we allow "real"
|
||||
machines running native applications and protocol stacks to integrate with an
|
||||
|ns3| simulation. This allows for the simulation of large networks connected to
|
||||
a real machine, and also enables virtualization. A simulation of this kind is
|
||||
shown in the following figure:
|
||||
|
||||
.. _emulated-channel:
|
||||
|
||||
.. figure:: figures/emulated-channel.*
|
||||
|
||||
Implementation overview of emulated channel.
|
||||
|
||||
Here, you will see that there is a single host with a number of virtual machines
|
||||
running on it. An |ns3| simulation is shown running in the virtual machine shown
|
||||
in the center of the figure. This simulation has a number of nodes with
|
||||
associated |ns3| applications and protocol stacks that are talking to an |ns3|
|
||||
channel through native simulated |ns3| net devices.
|
||||
|
||||
There are also two virtual machines shown at the far left and far right of the
|
||||
figure. These VMs are running native (Linux) applications and protocol stacks.
|
||||
The VM is connected into the simulation by a Linux Tap net device. The user-mode
|
||||
handler for the Tap device is instantiated in the simulation and attached to a
|
||||
proxy node that represents the native VM in the simulation. These handlers allow
|
||||
the Tap devices on the native VMs to behave as if they were |ns3| net devices in
|
||||
the simulation VM. This, in turn, allows the native software and protocol suites
|
||||
in the native VMs to believe that they are connected to the simulated |ns3|
|
||||
channel.
|
||||
|
||||
We expect the typical use case for this environment will be to analyze the
|
||||
behavior of native applications and protocol suites in the presence of large
|
||||
simulated |ns3| networks.
|
||||
|
||||
.. toctree::
|
||||
|
||||
fd-net-device
|
||||
tap
|
||||
@@ -1,52 +0,0 @@
|
||||
.. only:: html or latex
|
||||
|
||||
ns-3 Model Library
|
||||
==================
|
||||
|
||||
This is the *ns-3 Model Library* documentation. Primary documentation for the ns-3 project is
|
||||
available in five forms:
|
||||
|
||||
* `ns-3 Doxygen <http://www.nsnam.org/doxygen/index.html>`_: Documentation of the public APIs of the simulator
|
||||
* Tutorial, Manual, and Model Library *(this document)* for the `latest release <http://www.nsnam.org/documentation/latest/>`_ and `development tree <http://www.nsnam.org/ns-3-dev/documentation/>`_
|
||||
* `ns-3 wiki <http://www.nsnam.org/wiki>`_
|
||||
|
||||
This document is written in `reStructuredText <http://docutils.sourceforge.net/rst.html>`_ for `Sphinx <http://sphinx.pocoo.org/>`_ and is maintained in the
|
||||
``doc/models`` directory of ns-3's source code.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
organization
|
||||
animation
|
||||
antenna
|
||||
aodv
|
||||
applications
|
||||
bridge
|
||||
brite
|
||||
buildings
|
||||
click
|
||||
csma
|
||||
data-collection
|
||||
dsdv
|
||||
dsr
|
||||
emulation-overview
|
||||
energy
|
||||
flow-monitor
|
||||
internet-models
|
||||
lr-wpan
|
||||
lte
|
||||
mesh
|
||||
distributed
|
||||
mobility
|
||||
network
|
||||
olsr
|
||||
openflow-switch
|
||||
point-to-point
|
||||
propagation
|
||||
spectrum
|
||||
sixlowpan
|
||||
topology
|
||||
uan
|
||||
wave
|
||||
wifi
|
||||
wimax
|
||||
@@ -1,11 +0,0 @@
|
||||
Internet Models
|
||||
---------------
|
||||
|
||||
.. toctree::
|
||||
|
||||
internet-stack
|
||||
ipv4
|
||||
ipv6
|
||||
routing-overview
|
||||
tcp
|
||||
codel
|
||||
@@ -1,11 +0,0 @@
|
||||
Network Module
|
||||
--------------
|
||||
|
||||
.. toctree::
|
||||
|
||||
packets
|
||||
error-model
|
||||
network-overview
|
||||
sockets-api
|
||||
simple
|
||||
queue
|
||||
@@ -1,53 +0,0 @@
|
||||
.. include:: replace.txt
|
||||
|
||||
Organization
|
||||
------------
|
||||
|
||||
This manual compiles documentation for |ns3| models and supporting
|
||||
software that enable users to construct network simulations.
|
||||
It is important to distinguish between **modules** and **models**:
|
||||
|
||||
* |ns3| software is organized into separate *modules* that are each
|
||||
built as a separate software library. Individual ns-3 programs can link
|
||||
the modules (libraries) they need to conduct their simulation.
|
||||
|
||||
* |ns3| *models* are abstract representations of real-world objects,
|
||||
protocols, devices, etc.
|
||||
|
||||
An |ns3| module may consist of more than one model (for instance, the
|
||||
:mod:`internet` module contains models for both TCP and UDP). In general,
|
||||
ns-3 models do not span multiple software modules, however.
|
||||
|
||||
This manual provides documentation about the models of |ns3|. It
|
||||
complements two other sources of documentation concerning models:
|
||||
|
||||
* the model APIs are documented, from a programming perspective, using
|
||||
`Doxygen <http://www.doxygen.org>`_. Doxygen for ns-3 models is available
|
||||
`on the project web server <http://www.nsnam.org/docs/doxygen/index.html>`_.
|
||||
|
||||
* the |ns3| core is documented in the developer's manual. |ns3| models make
|
||||
use of the facilities of the core, such as attributes, default values,
|
||||
random numbers, test frameworks, etc. Consult the
|
||||
`main web site <http://www.nsnam.org>`_ to find copies of the manual.
|
||||
|
||||
Finally, additional documentation about various aspects of |ns3| may
|
||||
exist on the `project wiki <http://www.nsnam.org/wiki>`_.
|
||||
|
||||
A sample outline of how to write model library documentation can be
|
||||
found by executing the ``create-module.py`` program and looking at the
|
||||
template created in the file ``new-module/doc/new-module.rst``.
|
||||
|
||||
.. sourcecode:: bash
|
||||
|
||||
$ cd src
|
||||
$ ./create-module.py new-module
|
||||
|
||||
The remainder of this document is organized alphabetically by module name.
|
||||
|
||||
If you are new to |ns3|, you might first want to read below about the network
|
||||
module, which contains some fundamental models for the simulator.
|
||||
The packet model, models for different address formats, and abstract
|
||||
base classes for objects such as nodes, net devices, channels, sockets, and
|
||||
applications are discussed there.
|
||||
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
.. |ns3| replace:: *ns-3*
|
||||
|
||||
.. |ns2| replace:: *ns-2*
|
||||
@@ -1,42 +0,0 @@
|
||||
/**
|
||||
* @anchor modules_anchor
|
||||
*
|
||||
* @defgroup constants Constants
|
||||
* @brief Constants you can change
|
||||
*
|
||||
* @defgroup utils Utils
|
||||
* @brief The utils directory is for various programs and scripts related
|
||||
* to code coverage, test suites, style checking, and benchmarking.
|
||||
*/
|
||||
/**
|
||||
* @defgroup core Core
|
||||
* \brief The "core" module contains:
|
||||
* - a time management class to hold a time and convert between various
|
||||
* time units: ns3::Time
|
||||
* - a scheduler base class used to implement new simulation event
|
||||
* schedulers:
|
||||
* ns3::Scheduler and ns3::SchedulerFactory
|
||||
* - a simulator class used to create, schedule and cancel events:
|
||||
* ns3::Simulator
|
||||
* - a Functor class: ns3::Callback
|
||||
* - an os-independent interface to get access to the elapsed wall clock
|
||||
* time: ns3::SystemWallClockMs
|
||||
* - a class to register regression tests with the test manager: ns3::Test
|
||||
* and ns3::TestManager
|
||||
* - debugging facilities: \ref debugging
|
||||
* - \ref randomvariable
|
||||
* - a base class for objects which need to support per-instance
|
||||
* "attributes" and trace sources: ns3::ObjectBase
|
||||
* - a base class for objects which need to support reference counting
|
||||
* and dynamic object aggregation: ns3::Object
|
||||
* - a smart-pointer class ns3::Ptr designed to work together with
|
||||
* ns3::Object
|
||||
* - a configuration class used to set and control all attributes and
|
||||
* trace sources in a simulation: ns3::Config.
|
||||
*/
|
||||
/**
|
||||
* @ingroup core
|
||||
* @defgroup debugging Debugging tools
|
||||
*
|
||||
* @brief Assertions, breakpoints, logging, and abnormal program termination
|
||||
*/
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,85 +0,0 @@
|
||||
.. ns3-theme documentation master file, created by
|
||||
sphinx-quickstart on Fri Jun 15 12:53:37 2012.
|
||||
You can adapt this file completely to your liking, but it should at least
|
||||
contain the root `toctree` directive.
|
||||
|
||||
|
||||
The files in this directory customize the Doxygen and Sphinx
|
||||
layout and rendering to give a common look.
|
||||
|
||||
|
||||
Design
|
||||
------
|
||||
|
||||
The basic color scheme is taken from the ns3 homepage, http://www.nsnam.org/
|
||||
|
||||
Since Sphinx provides more flexible layout and configuration control,
|
||||
the Doxygen layout and images are used as the basis.
|
||||
|
||||
The top bar design is derived from Doxygen's default when a logo,
|
||||
project name and description are given in the Doxygen configuration file.
|
||||
The files ``layout.html`` and ``ns3_doxy_header.html`` should kept in sync.
|
||||
|
||||
|
||||
Sphinx Files
|
||||
------------
|
||||
|
||||
``layout.html`` (:ref:`static filename <sphinx-static-filenames>`)
|
||||
Augments the default page layout, including the top bar.
|
||||
|
||||
``theme.conf`` (:ref:`static filename <sphinx-static-filenames>`)
|
||||
Configuration parameters for ``layout.html`` and ``default.css_t``.
|
||||
|
||||
``static/bc_s.png``
|
||||
Angle bracket in Sphinx relbar, taken from the Doxygen html output.
|
||||
|
||||
``static/default.css_t`` (:ref:`static filename <sphinx-static-filenames>`)
|
||||
Default Sphinx CSS template, copied from the default theme.
|
||||
|
||||
``static/nav_f.png``
|
||||
Background image for headings, taken from the Doxygen html output.
|
||||
|
||||
``static/sidebar.js`` (:ref:`static filename <sphinx-static-filenames>`)
|
||||
Sidebar script, copied from the default theme.
|
||||
|
||||
``static/tab_b.png``
|
||||
Background image for the Sphinx relbar, taken from the Doxygen html output.
|
||||
|
||||
.. _sphinx-static-filenames:
|
||||
.. note:: **Static Filenames**
|
||||
|
||||
Sphinx uses hard-coded file names (and the ``static`` directory name).
|
||||
The files referenced above can't be renamed.
|
||||
|
||||
|
||||
Doxygen Files
|
||||
-------------
|
||||
|
||||
``ns3_doxy_footer.html``
|
||||
Defines the page footer.
|
||||
|
||||
``ns3_doxy_header.html``
|
||||
Defines the page header, including the top bar.
|
||||
|
||||
``static/doxygen.css``
|
||||
Default Doxygen CSS file, obtained by the ``doxygen -w html ...``
|
||||
command.
|
||||
|
||||
Shared Files
|
||||
------------
|
||||
|
||||
``README.rst``
|
||||
This file, not used by Doxygen or Sphinx.
|
||||
|
||||
``static/bar-top.png``
|
||||
Background image for the top bar, resized from the ns-3 homepage.
|
||||
|
||||
``static/favicon.ico``
|
||||
Browser location bar favicon, referenced in ``layout.html`` and
|
||||
``ns3_doxy_header.html``, taken from the ns-3 homepage.
|
||||
|
||||
``static/ns-3-inverted-notext-small.png``
|
||||
Logo used in the top bar, taken from the ns-3 media kit.
|
||||
|
||||
``static/ns3_stylesheet.css``
|
||||
Style customizations for both Doxygen and Sphinx.
|
||||
@@ -1,216 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Get the current repo name and version
|
||||
# to format urls appropriately via Javascript
|
||||
# variables for inclusion in html files.
|
||||
|
||||
# Use cases:
|
||||
# 1. Hosted on nsnam.org, tagged release.
|
||||
# 2. Hosted on nsnam.org, ns-3-dev.
|
||||
# 3. User repo, modified from a tagged release (or ns-3-dev).
|
||||
# 4. User repo, at a release tag.
|
||||
# 5. User repo, at a private tag.
|
||||
# 6. Private web host, at a tag (or ns-3-dev, or local mod).
|
||||
#
|
||||
# For case 1 and 2, we want the links to point to the nsnam.org
|
||||
# publicly hosted pages. For all other cases, we want to point
|
||||
# to the built pages in the repo itself.
|
||||
#
|
||||
# For robustness, we attempt to identify cases 1 & 2
|
||||
# automatically. (There is also an explicit switch '-p'
|
||||
# to force us into the public case.)
|
||||
#
|
||||
# The approach to identify cases 1 & 2 is to test:
|
||||
# a. We're on nsnam.org (actually, nsnam-www.coe-hosted.gatech.edu), and
|
||||
# b. We're in the tmp build directory, /tmp/daily-nsnam/
|
||||
# (This is the directory used by the update-* scripts
|
||||
# run by cron jobs.)
|
||||
#
|
||||
# If both a and b are true, we're building for public urls.
|
||||
# (The newer update-docs script (through waf) sets
|
||||
# NS3_WWW_URLS=public explicitly.)
|
||||
#
|
||||
# The repo version is either a tag name or a commit (short) id.
|
||||
#
|
||||
# If we're building for nsnam.org, and at a tag, we use just
|
||||
# the tag as the repo name/version string, e.g. 'ns-3.14'.
|
||||
# Otherwise, we're building for ns-3-dev, and we use, e.g,
|
||||
# 'ns-3-dev @ fd0f27a10eff'.
|
||||
#
|
||||
# If we're *not* building for nsnam.org, we use the repo
|
||||
# directory name as the repo name. (This will typically be
|
||||
# a name meaningful to the user doing the build, perhaps a
|
||||
# shorthand for the feature they are working on.) For
|
||||
# example, this script was developed in a repo named
|
||||
# 'docs'. We always use the repo version, resulting
|
||||
# in document version strings like 'docs @ ns-3.15' or
|
||||
# 'docs @ fd0f27a10eff'
|
||||
#
|
||||
|
||||
me=`basename $0`
|
||||
function say
|
||||
{
|
||||
echo "$me: $*"
|
||||
}
|
||||
|
||||
function usage
|
||||
{
|
||||
cat <<-EOF
|
||||
Usage: $me [-p] normal versioning
|
||||
$me [-n] [-d] [-t] test options
|
||||
-p build public urls, NS3_WWW_URLS=public is an alternative
|
||||
|
||||
Testing options:
|
||||
-n pretend we are on nsnam.org
|
||||
-d pretend we are in the automated build directory
|
||||
-t pretend we are at a repo tag
|
||||
|
||||
EOF
|
||||
exit 1
|
||||
}
|
||||
|
||||
# script arguments
|
||||
say
|
||||
say using doxygen: $(which doxygen) $(doxygen --version)
|
||||
public=0
|
||||
nsnam=0
|
||||
daily=0
|
||||
tag=0
|
||||
|
||||
while getopts :pndth option ; do
|
||||
case $option in
|
||||
(p) public=1 ;;
|
||||
(n) nsnam=1 ;;
|
||||
|
||||
(d) daily=1 ;;
|
||||
|
||||
(t) tag=1 ;;
|
||||
|
||||
(h) usage ;;
|
||||
(:) say "Missing argument to -$OPTARG" ; usage ;;
|
||||
(\?) say "Invalid option: -$OPTARG" ; usage ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Hostname, fully qualified, e.g. nsnam-www.coe-hosted.gatech.edu
|
||||
HOST=`hostname`
|
||||
NSNAM="nsnam-www.coe-hosted.gatech.edu"
|
||||
|
||||
# Build directory
|
||||
DAILY="^/tmp/daily-nsnam/"
|
||||
|
||||
if [ $nsnam -eq 1 ]; then
|
||||
HOST=$NSNAM
|
||||
say "-n forcing HOST = $HOST"
|
||||
fi
|
||||
|
||||
if [ $daily -eq 1 ] ; then
|
||||
OLDPWD=$PWD
|
||||
PWD=/tmp/daily-nsnam/foo
|
||||
say "-d forcing PWD = $PWD"
|
||||
fi
|
||||
|
||||
if [ $tag -eq 1 ]; then
|
||||
version="ns-3.14"
|
||||
say "-t forcing tagged version = $version"
|
||||
fi
|
||||
|
||||
if ((nsnam + daily + tag > 0)) ; then
|
||||
say
|
||||
fi
|
||||
|
||||
if [[ ($public == 1) || \
|
||||
("${NS3_WWW_URLS:-}" == "public") || \
|
||||
( ($HOST == $NSNAM) && ($PWD =~ $DAILY) ) ]] ; then
|
||||
PUBLIC=1
|
||||
say "building public docs for nsnam.org"
|
||||
else
|
||||
PUBLIC=0
|
||||
say "building private docs"
|
||||
fi
|
||||
|
||||
if [ $daily -eq 1 ]; then
|
||||
PWD=$OLDPWD
|
||||
fi
|
||||
|
||||
# Destination javascript file
|
||||
outf="doc/ns3_html_theme/static/ns3_version.js"
|
||||
|
||||
# Distance from last tag
|
||||
# Zero distance means we're at the tag
|
||||
distance=`hg log -r tip --template '{latesttagdistance}'`
|
||||
|
||||
if [ $distance -eq 1 ]; then
|
||||
version=`hg log -r tip --template '{latesttag}'`
|
||||
say "at tag $version"
|
||||
|
||||
elif [ $tag -eq 1 ]; then
|
||||
distance=1
|
||||
# version previously set
|
||||
vers_href=
|
||||
|
||||
else
|
||||
version=`hg log -r tip --template '{node|short}'`
|
||||
# Check for uncommitted changes
|
||||
hg summary | grep 'commit:' | grep -q '(clean)'
|
||||
if [ ! $? ] ; then
|
||||
say "beyond latest tag, last commit: $version, dirty"
|
||||
dirty="(+)"
|
||||
else
|
||||
say "beyond latest tag, last commit: $version, clean"
|
||||
dirty=
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $PUBLIC -eq 1 ]; then
|
||||
echo "// ns3_version.js: automatically generated" > $outf
|
||||
echo "// public urls" >> $outf
|
||||
# Generate URL relative to server root
|
||||
echo "var ns3_host = \"/\";" >> $outf
|
||||
|
||||
if [ $distance -eq 1 ]; then
|
||||
# Like "http://www.nsnam.org/ns-3-14"
|
||||
vers_href="http://www.nsnam.org/ns-3-${version#ns-3.}"
|
||||
vers_href="<a href=\\\"$vers_href\\\">$version$dirty</a>"
|
||||
|
||||
echo "var ns3_version = \"Release $vers_href\";" >> $outf
|
||||
echo "var ns3_release = \"docs/release/${version#ns-}/\";" >> $outf
|
||||
else
|
||||
# Like "http://code.nsnam.org/ns-3-dev/rev/<hash>"
|
||||
vers_href="http://code.nsnam.org/ns-3-dev/rev/$version"
|
||||
version="<a href=\\\"$vers_href\\\">$version$dirty</a>"
|
||||
|
||||
echo "var ns3_version = \"ns-3-dev @ $version\";" >> $outf
|
||||
echo "var ns3_release = \"docs/\";" >> $outf
|
||||
fi
|
||||
echo "var ns3_local = \"\";" >> $outf
|
||||
echo "var ns3_doxy = \"doxygen/\";" >> $outf
|
||||
|
||||
else
|
||||
repo=`basename $PWD`
|
||||
echo "// ns3_version.js: automatically generated" > $outf
|
||||
echo "// private urls" >> $outf
|
||||
echo "var ns3_host = \"file://$PWD/\";" > $outf
|
||||
echo "var ns3_version = \"$repo @ $version$dirty\";" >> $outf
|
||||
echo "var ns3_release = \"doc/\";" >> $outf
|
||||
echo "var ns3_local = \"build/\";" >> $outf
|
||||
echo "var ns3_doxy = \"html/\";" >> $outf
|
||||
fi
|
||||
|
||||
# Copy to html directories
|
||||
# This seems not always done automatically
|
||||
# by Sphinx when rebuilding
|
||||
cd doc 2>&1 >/dev/null
|
||||
for d in {manual,models,tutorial{,-pt-br}}/build/{single,}html/_static/ ; do
|
||||
if [ ! -d $d ]; then
|
||||
mkdir -p $d
|
||||
fi
|
||||
cp ns3_html_theme/static/ns3_version.js $d
|
||||
done
|
||||
cd - 2>&1 >/dev/null
|
||||
|
||||
# Show what was done
|
||||
say
|
||||
say "outf = $outf:"
|
||||
cat -n $outf
|
||||
say Done.
|
||||
@@ -1,121 +0,0 @@
|
||||
{#
|
||||
ns-3/layout.html
|
||||
~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Sphinx layout template for ns-3.
|
||||
|
||||
:copyright: Copyright 2012 by ns-3, see AUTHORS.
|
||||
:license: GPL, see LICENSE for details.
|
||||
#}
|
||||
{% extends "basic/layout.html" %}
|
||||
|
||||
{% set reldelim1 = '<span class="navelem"> </span>' %}
|
||||
{# set reldelim1 = ' @' #}
|
||||
|
||||
{%- block extrahead %}
|
||||
{%- if theme_customstylesheet %}
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="{{ pathto('_static/'+theme_customstylesheet, 1) }}" />
|
||||
{%- endif %}
|
||||
|
||||
{%- if theme_favicon %}
|
||||
<link rel="icon" type="image/ico"
|
||||
href="{{ pathto('_static/'+theme_favicon, 1) }}" />
|
||||
{%- endif %}
|
||||
|
||||
<script type="text/javascript" src="_static/drop-down-menu.js"></script>
|
||||
<script type="text/javascript" src="_static/ns3_version.js"></script>
|
||||
<script type="text/javascript">var ns3_builder="{{builder}}";</script>
|
||||
<script type="text/javascript" src="_static/ns3_links.js"></script>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block header %}
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<td id="projectlogo">
|
||||
<a id="ns3_home1"
|
||||
href="http://www.nsnam.org/">
|
||||
<img alt="ns-3 Logo"
|
||||
src="{{ pathto('_static/' + theme_logo, 1)}}"/>
|
||||
</a>
|
||||
</td>
|
||||
<td id="projecttext">
|
||||
<div id="projectbrief">A Discrete-Event Network Simulator</div>
|
||||
<span id="projectnumber"><script type="text/javascript">document.write(ns3_version)</script></span>
|
||||
</td>
|
||||
|
||||
<td id="ns3-menu">
|
||||
<div class="menu">
|
||||
<ul >
|
||||
<li><a id="ns3_home2"
|
||||
href="http://www.nsnam.org/"
|
||||
> Home</a>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mTuts')"
|
||||
onmouseout="mclosetime()"
|
||||
>Tutorials ▼</span>
|
||||
<div id="mTuts"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_tut"
|
||||
href="/docs/tutorial/html/index.html"
|
||||
>English</a><br/>
|
||||
<a id="ns3_ptbr"
|
||||
href="/docs/tutorial-pt-br/html/index.html"
|
||||
>Portuguese</a><br/>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mDocs')"
|
||||
onmouseout="mclosetime()"
|
||||
>Docs ▼</span>
|
||||
<div id="mDocs"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_wiki"
|
||||
href="http://www.nsnam.org/wiki"
|
||||
>Wiki</a><br/>
|
||||
<a id="ns3_man"
|
||||
href="/docs/manual/html/index.html"
|
||||
>Manual</a><br/>
|
||||
<a id="ns3_mod"
|
||||
href="/docs/models/html/index.html"
|
||||
>Models</a><br/>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mDev')"
|
||||
onmouseout="mclosetime()"
|
||||
>Develop ▼</span>
|
||||
<div id="mDev"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_api"
|
||||
href="/docs/doxygen/html/index.html"
|
||||
>API</a><br/>
|
||||
<a id="ns3_bugs"
|
||||
href="http://www.nsnam.org/bugzilla/">Bugs</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
<td id="projectsection">
|
||||
<span style="margin-right:10px">{{ shorttitle }}</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<script type="text/javascript">ns3_write_links()</script>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block rootrellink %}
|
||||
<li class="navelem"><a href="{{ theme_homepage }}">{{ theme_projectname }}</a><span class="navelem"> </span></li>
|
||||
{{ super() }}
|
||||
{% endblock %}
|
||||
|
||||
{% if theme_collapsiblesidebar|tobool %}
|
||||
{% set script_files = script_files + ['_static/sidebar.js'] %}
|
||||
{% endif %}
|
||||
@@ -1,24 +0,0 @@
|
||||
<!-- ns-3 doxygen header, based on
|
||||
HTML header for doxygen 1.8.3.1
|
||||
Verified compatible with 1.8.6
|
||||
-->
|
||||
<!-- start footer part -->
|
||||
<!--BEGIN GENERATE_TREEVIEW-->
|
||||
<div id="nav-path" class="navpath"><!-- id is needed for treeview function! -->
|
||||
<ul>
|
||||
$navpath
|
||||
<li class="footer">$generatedby
|
||||
<a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/></a> $doxygenversion </li>
|
||||
</ul>
|
||||
</div>
|
||||
<!--END GENERATE_TREEVIEW-->
|
||||
<!--BEGIN !GENERATE_TREEVIEW-->
|
||||
<hr class="footer"/><address class="footer"><small>
|
||||
$generatedby  <a href="http://www.doxygen.org/index.html">
|
||||
<img class="footer" src="$relpath$doxygen.png" alt="doxygen"/>
|
||||
</a> $doxygenversion
|
||||
</small></address>
|
||||
<!--END !GENERATE_TREEVIEW-->
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,110 +0,0 @@
|
||||
<!-- ns-3 doxygen header, based on
|
||||
HTML header for doxygen 1.8.3.1
|
||||
Verified compatible with 1.8.6
|
||||
-->
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/xhtml;charset=UTF-8"/>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=9"/>
|
||||
<meta name="generator" content="Doxygen $doxygenversion"/>
|
||||
<!--BEGIN PROJECT_NAME--><title>$projectname: $title</title><!--END PROJECT_NAME-->
|
||||
<!--BEGIN !PROJECT_NAME--><title>$title</title><!--END !PROJECT_NAME-->
|
||||
<link href="$relpath^tabs.css" rel="stylesheet" type="text/css"/>
|
||||
<script type="text/javascript" src="$relpath^jquery.js"></script>
|
||||
<script type="text/javascript" src="$relpath^dynsections.js"></script>
|
||||
$treeview
|
||||
$search
|
||||
$mathjax
|
||||
<link href="$relpath^$stylesheet" rel="stylesheet" type="text/css" />
|
||||
$extrastylesheet
|
||||
<link href="$relpath$favicon.ico" rel="shortcut icon" type="image/ico"></link>
|
||||
<script type="text/javascript" src="$relpath$drop-down-menu.js"></script>
|
||||
<script type="text/javascript" src="$relpath$ns3_version.js"></script>
|
||||
<script type="text/javascript" src="$relpath$ns3_links.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="top"><!-- do not remove this div, it is closed by doxygen! -->
|
||||
|
||||
<!--BEGIN TITLEAREA-->
|
||||
<div id="titlearea">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr style="height: 56px;">
|
||||
<td id="projectlogo">
|
||||
<a id="ns3_home1"
|
||||
href="http://www.nsnam.org/">
|
||||
<img alt="ns-3 Logo"
|
||||
src="$relpath$$projectlogo"/>
|
||||
</a>
|
||||
</td>
|
||||
<td id="projecttext">
|
||||
<div id="projectbrief">A Discrete-Event Network Simulator</div>
|
||||
<span id="projectnumber"><script type="text/javascript">document.write(ns3_version)</script></span>
|
||||
|
||||
</td>
|
||||
<td id="ns3-menu">
|
||||
<div class="menu">
|
||||
<ul >
|
||||
<li><a id="ns3_home2"
|
||||
href="http://www.nsnam.org/"
|
||||
> Home</a>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mTuts')"
|
||||
onmouseout="mclosetime()"
|
||||
>Tutorials ▼</span>
|
||||
<div id="mTuts"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_tut"
|
||||
href="/docs/tutorial/html/index.html"
|
||||
>English</a><br/>
|
||||
<a id="ns3_ptbr"
|
||||
href="/docs/tutorial-pt-br/html/index.html"
|
||||
>Portuguese</a><br/>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mDocs')"
|
||||
onmouseout="mclosetime()"
|
||||
>Docs ▼</span>
|
||||
<div id="mDocs"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_wiki"
|
||||
href="http://www.nsnam.org/wiki"
|
||||
>Wiki</a><br/>
|
||||
<a id="ns3_man"
|
||||
href="/docs/manual/html/index.html"
|
||||
>Manual</a><br/>
|
||||
<a id="ns3_mod"
|
||||
href="/docs/models/html/index.html"
|
||||
>Models</a><br/>
|
||||
</li>
|
||||
<li><span
|
||||
onmouseover="mopen('mDev')"
|
||||
onmouseout="mclosetime()"
|
||||
>Develop ▼</span>
|
||||
<div id="mDev"
|
||||
onmouseover="mcancelclosetime()"
|
||||
onmouseout="mclosetime()">
|
||||
<a id="ns3_api"
|
||||
href="/docs/doxygen/index.html"
|
||||
>API</a><br/>
|
||||
<a id="ns3_bugs"
|
||||
href="http://www.nsnam.org/bugzilla/">Bugs</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td id="projectsection">
|
||||
<span style="margin-right:10px">API</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<script type="text/javascript">ns3_write_links();</script>
|
||||
</div>
|
||||
<!--END TITLEAREA-->
|
||||
<!-- end header part -->
|
||||
|
Before Width: | Height: | Size: 362 B |
|
Before Width: | Height: | Size: 635 B |
@@ -1,310 +0,0 @@
|
||||
/*
|
||||
* default.css_t
|
||||
* ~~~~~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- default theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
@import url("basic.css");
|
||||
|
||||
/* -- page layout ----------------------------------------------------------- */
|
||||
|
||||
body {
|
||||
font-family: {{ theme_bodyfont }};
|
||||
font-size: 100%;
|
||||
background-color: {{ theme_footerbgcolor }};
|
||||
color: #000;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.document {
|
||||
background-color: {{ theme_sidebarbgcolor }};
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
float: left;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.bodywrapper {
|
||||
margin: 0 0 0 {{ theme_sidebarwidth|toint }}px;
|
||||
}
|
||||
|
||||
div.body {
|
||||
background-color: {{ theme_bgcolor }};
|
||||
color: {{ theme_textcolor }};
|
||||
padding: 0 20px 30px 20px;
|
||||
}
|
||||
|
||||
{%- if theme_rightsidebar|tobool %}
|
||||
div.bodywrapper {
|
||||
margin: 0 {{ theme_sidebarwidth|toint }}px 0 0;
|
||||
}
|
||||
{%- endif %}
|
||||
|
||||
div.footer {
|
||||
color: {{ theme_footertextcolor }};
|
||||
width: 100%;
|
||||
padding: 9px 0 9px 0;
|
||||
text-align: center;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
div.footer a {
|
||||
color: {{ theme_footertextcolor }};
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div.related {
|
||||
background-color: {{ theme_relbarbgcolor }};
|
||||
line-height: 30px;
|
||||
color: {{ theme_relbartextcolor }};
|
||||
}
|
||||
|
||||
div.related a {
|
||||
color: {{ theme_relbarlinkcolor }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
{%- if theme_stickysidebar|tobool %}
|
||||
top: 30px;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
overflow: auto;
|
||||
height: auto;
|
||||
{%- endif %}
|
||||
{%- if theme_rightsidebar|tobool %}
|
||||
float: right;
|
||||
{%- if theme_stickysidebar|tobool %}
|
||||
right: 0;
|
||||
{%- endif %}
|
||||
{%- endif %}
|
||||
}
|
||||
|
||||
{%- if theme_stickysidebar|tobool %}
|
||||
/* this is nice, but it it leads to hidden headings when jumping
|
||||
to an anchor */
|
||||
/*
|
||||
div.related {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
div.documentwrapper {
|
||||
margin-top: 30px;
|
||||
}
|
||||
*/
|
||||
{%- endif %}
|
||||
|
||||
div.sphinxsidebar h3 {
|
||||
font-family: {{ theme_headfont }};
|
||||
color: {{ theme_sidebartextcolor }};
|
||||
font-size: 1.4em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar h3 a {
|
||||
color: {{ theme_sidebartextcolor }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar h4 {
|
||||
font-family: {{ theme_headfont }};
|
||||
color: {{ theme_sidebartextcolor }};
|
||||
font-size: 1.3em;
|
||||
font-weight: normal;
|
||||
margin: 5px 0 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar p {
|
||||
color: {{ theme_sidebartextcolor }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar p.topless {
|
||||
margin: 5px 10px 10px 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
color: {{ theme_sidebartextcolor }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar a {
|
||||
color: {{ theme_sidebarlinkcolor }};
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid {{ theme_sidebarlinkcolor }};
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
{% if theme_collapsiblesidebar|tobool %}
|
||||
/* for collapsible sidebar */
|
||||
div#sidebarbutton {
|
||||
background-color: {{ theme_sidebarbtncolor }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
/* -- hyperlink styles ------------------------------------------------------ */
|
||||
|
||||
a {
|
||||
color: {{ theme_linkcolor }};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:visited {
|
||||
color: {{ theme_visitedlinkcolor }};
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
{% if theme_externalrefs|tobool %}
|
||||
a.external {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed {{ theme_linkcolor }};
|
||||
}
|
||||
|
||||
a.external:hover {
|
||||
text-decoration: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
a.external:visited {
|
||||
text-decoration: none;
|
||||
border-bottom: 1px dashed {{ theme_visitedlinkcolor }};
|
||||
}
|
||||
{% endif %}
|
||||
|
||||
/* -- body styles ----------------------------------------------------------- */
|
||||
|
||||
div.body h1,
|
||||
div.body h2,
|
||||
div.body h3,
|
||||
div.body h4,
|
||||
div.body h5,
|
||||
div.body h6 {
|
||||
font-family: {{ theme_headfont }};
|
||||
background-color: {{ theme_headbgcolor }};
|
||||
font-weight: normal;
|
||||
color: {{ theme_headtextcolor }};
|
||||
border-bottom: 1px solid #ccc;
|
||||
margin: 20px -20px 10px -20px;
|
||||
padding: 3px 0 3px 10px;
|
||||
}
|
||||
|
||||
div.body h1 { margin-top: 0; font-size: 200%; }
|
||||
div.body h2 { font-size: 160%; }
|
||||
div.body h3 { font-size: 140%; }
|
||||
div.body h4 { font-size: 120%; }
|
||||
div.body h5 { font-size: 110%; }
|
||||
div.body h6 { font-size: 100%; }
|
||||
|
||||
a.headerlink {
|
||||
color: {{ theme_headlinkcolor }};
|
||||
font-size: 0.8em;
|
||||
padding: 0 4px 0 4px;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a.headerlink:hover {
|
||||
background-color: {{ theme_headlinkcolor }};
|
||||
color: white;
|
||||
}
|
||||
|
||||
div.body p, div.body dd, div.body li {
|
||||
text-align: justify;
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div.admonition p.admonition-title + p {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.admonition p {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition pre {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.admonition ul, div.admonition ol {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
div.note {
|
||||
background-color: #eee;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
div.seealso {
|
||||
background-color: #ffc;
|
||||
border: 1px solid #ff6;
|
||||
}
|
||||
|
||||
div.topic {
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
div.warning {
|
||||
background-color: #ffe4e4;
|
||||
border: 1px solid #f66;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
p.admonition-title:after {
|
||||
content: ":";
|
||||
}
|
||||
|
||||
pre {
|
||||
padding: 5px;
|
||||
background-color: {{ theme_codebgcolor }};
|
||||
color: {{ theme_codetextcolor }};
|
||||
line-height: 120%;
|
||||
border: 1px solid #ac9;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
tt {
|
||||
background-color: #ecf0f3;
|
||||
padding: 0 1px 0 1px;
|
||||
font-size: 0.95em;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: #ede;
|
||||
}
|
||||
|
||||
.warning tt {
|
||||
background: #efc2c2;
|
||||
}
|
||||
|
||||
.note tt {
|
||||
background: #d6d6d6;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
font-family: {{ theme_bodyfont }};
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
background-color: #f4debf;
|
||||
border-top: 1px solid #ac9;
|
||||
border-bottom: 1px solid #ac9;
|
||||
}
|
||||
@@ -1,45 +0,0 @@
|
||||
// Copyright 2006-2007 javascript-array.com
|
||||
// http://javascript-array.com/scripts/simple_drop_down_menu/
|
||||
|
||||
var timeout = 250;
|
||||
var closetimer = 0;
|
||||
var ddmenuitem = 0;
|
||||
|
||||
// open hidden layer
|
||||
function mopen(id)
|
||||
{
|
||||
// cancel close timer
|
||||
mcancelclosetime();
|
||||
|
||||
// close old layer
|
||||
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
|
||||
|
||||
// get new layer and show it
|
||||
ddmenuitem = document.getElementById(id);
|
||||
ddmenuitem.style.visibility = 'visible';
|
||||
|
||||
}
|
||||
// close showed layer
|
||||
function mclose()
|
||||
{
|
||||
if(ddmenuitem) ddmenuitem.style.visibility = 'hidden';
|
||||
}
|
||||
|
||||
// go close timer
|
||||
function mclosetime()
|
||||
{
|
||||
closetimer = window.setTimeout(mclose, timeout);
|
||||
}
|
||||
|
||||
// cancel close timer
|
||||
function mcancelclosetime()
|
||||
{
|
||||
if(closetimer)
|
||||
{
|
||||
window.clearTimeout(closetimer);
|
||||
closetimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
// close layer when click-out
|
||||
document.onclick = mclose;
|
||||
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 134 B |
|
Before Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 13 KiB |