This commit is contained in:
SunnyQjm committed 2022-03-24 18:33:04 +08:00
commit 83b605840e
5291 files changed
+2001385

No files matched your search

+1
View File
@@ -0,0 +1 @@
/.idea/
+3
View File
@@ -0,0 +1,3 @@
[submodule "pybindgen"]
path = pybindgen
url = https://gitea.qjm253.cn/SunnyQjm/pybindgen.git
+170
View File
@@ -0,0 +1,170 @@
## 编译运行
1. 首先安装依赖:
- Ubuntu 20.04:
```bash
sudo apt install gir1.2-goocanvas-2.0 gir1.2-gtk-3.0 libgirepository1.0-dev python3-dev python3-gi python3-gi-cairo python3-pip python3-pygraphviz python3-pygccxml
sudo pip3 install kiwi
```
2. 首先拉取代码:
```bash
# 首先进入到一个合适的目录用来存放代码
cd ~/Documents
# 然后分别拉取两次 nwfq-ndnsim 这个项目代码,放在两个不同的目录
git clone https://gitea.qjm253.cn/SunnyQjm/nwfq-ndnsim.git
git clone https://gitea.qjm253.cn/SunnyQjm/nwfq-ndnsim.git nwfq-ndnsim-pcon
```
- 在这边我们拉取了代码两次,分别放到两个不同的文件夹,主要用来存放我们的实验代码和基于PCON做对比实验的代码,拉取结束之后我们的文件目录应该长下面这样:
```bash
.
├── nwfq-ndnsim
│   ├── README.md
│   ├── ns-3
│   ├── pybindgen
│   └── scenario
└── nwfq-ndnsim-pcon
├── README.md
├── ns-3
├── pybindgen
└── scenario
```
3. 然后进行分支切换:
```bash
# 把 nwfq-ndnsim 文件夹下的代码切换到 nwfq-with-rate-congestion 分支
cd nwfq-ndnsim
git checkout nwfq-with-rate-congestion
# 把 nwfq-ndnsim-pcon 文件夹下的代码切换到 pcon 分支
cd ../nwfq-ndnsim-pcon
git checkout pcon
```
可以运行下列命令查看有哪些分支:
```bash
git branch -a
```
输出:
```bash
* master
remotes/origin/HEAD -> origin/master
remotes/origin/master
remotes/origin/nwfq
remotes/origin/nwfq-with-rate-congestion
remotes/origin/pcon
```
- 可以看到远程有若干个分支,我们只关心两个分支:
- `nwfq-with-rate-congestion` 分支存放了我们的DSCPP方案的实验代码;
- `pcon` 分支存放了我们基于 PCON 做对比实验的代码;
- `master` 分支存放的是ndnsim原本的代码,没有经过任何修改,如果要引入新方案对比,可以基于这个分支开一个新分支进行实验编写。
4. 编译 DSCPP 实验代码,我们进入到 `nwfq-ndnsim/ns-3` 目录下开始编译
```bash
cd ../nwfq-ndnsim/ns-3
# 配置(这边使用 -d debug 编译成debug模式,才有日志输出)
./waf configure -d debug --enable-examples
# 开始编译(要挺久的,够喝杯咖啡了)
./waf
# 编译完成开始安装
sudo ./waf install
```
5. 编译 PCON 对比实验代码,我们进入到 `nwfq-ndnsim-pcon/ns-3` 目录下开始编译
```bash
cd ../../nwfq-ndnsim-pcon/ns-3
# 配置(我们基本不用改pcon的代码,所以这边使用 -d optimized 编译成生产模式, 不需要日志输出)
./waf configure -d optimized --enable-examples
# 开始编译(要挺久的,够喝杯咖啡了)
./waf
# 编译完成开始安装
sudo ./waf install
```
6. 先运行一下简单的PCON的代码,运行之前保证当前处于 `nwfq-ndnsim-pcon/ns-3` 目录下
```bash
# 执行下面这个才能进入到虚拟环境,才可以对实验进行可视化
./waf shell
# 进入到实验场景目录
cd ../scenario
# 如果第一次跑实验,需要跑下面的代码进行配置
./waf configure
# 运行实验 1.1 (--vis 参数表示可视化运行实验,会跳出一个可视化界面,不加的话直接运行实验)
./waf --run=experiment1.1 --vis
# 运行结束之后,输入 ctrl+c 退出虚拟环境(这步一定要做,要不然后面不能正常跑)
```
7. 然后我们来跑一下 DSCPP 的时延代码,我们需要进入到 `nwfq-ndnsim/ns-3` 目录下
```bash
cd ../nwfq-ndnsim/ns-3
# 下面这一步是因为刚才我们跑的 PCON 的实验代码,所以需要执行下面的安装一下 DSCPP 的代码的编译结果
# 每次想要切换运行两份代码的时候就需要到对应代码的 ns-3 文件夹下执行这个安装一下链接库
sudo ./waf install
# 执行下面这个才能进入到虚拟环境,才可以对实验进行可视化
./waf shell
# 进入到实验场景目录
cd ../scenario
# 如果第一次跑实验,需要跑下面的代码进行配置
./waf configure
# 运行实验 1.1 (--vis 参数表示可视化运行实验,会跳出一个可视化界面,不加的话直接运行实验)
./waf --run=experiment1.1 --vis
```
8. 输出运行日志:
```bash
NS_LOG=ndn-cxx.nfd.DataRateMerger ./waf --run=experiment2
```
- 上面这个代码运行了实验2,并且不带 `--vis` 表示不需要图形化界面;
- NS_LOG = `ndn-cxx.nfd.DataRateMerger` 表示输出 DataRateMerger 这个类里面的输出
- 只要输出一个错误的值,就会告诉你 NS_LOG 可以取那些值
![image-20210722194839783](https://gitee.com/quejianming/pic-bed/raw/master/uPic/2021/07/22/image-20210722194839783-1626954521.png)
- 目前对我们比较有用的LOG有以下几种:
- `ndn.ConsumerNWFQ` => DSCPP 客户端程序的输出
- `ndn.ConsumerPcon` => PCON 客户端程序的输出
- `ndn-cxx.nfd.GenericLinkService` => NFD 通用链路服务的日志输出
- `ndn-cxx.nfd.DataRateMerger` => DSCPP 转发策略的速率合并模块的日志输出
- `ndn-cxx.nfd.RandomLoadBalancerStrategy` => DSCPP 转发策略的日志输出
- `ndn-cxx.nfd.NWFQ` => DSCPP NFD中的速率整型部分的日志输出
- 如果要输出多种类型的日志,用 `:` 拼接就好了
```bash
NS_LOG=ndn-cxx.nfd.DataRateMerger:ndn.ConsumerNWFQ ./waf --run=experiment2
```
+53
View File
@@ -0,0 +1,53 @@
---
Language: Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: true
AlignOperands: true
AlignTrailingComments: false
AllowAllParametersOfDeclarationOnNextLine: true
AllowShortBlocksOnASingleLine: false
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: None
AllowShortIfStatementsOnASingleLine: false
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: AllDefinitions
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: true
AfterControlStatement: true
AfterFunction: true
AfterNamespace: false
AfterStruct: true
IndentBraces: true
BeforeElse: true
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
ColumnLimit: 100
ConstructorInitializerAllOnOneLineOrOnePerLine: true
Cpp11BracedListStyle: true
DerivePointerAlignment: false
IndentCaseLabels: false
IndentWidth: 2
NamespaceIndentation: None
PointerAlignment: Right
ReflowComments: false
SortIncludes: false
SpaceAfterCStyleCast: true
SpaceAfterTemplateKeyword: true
SpaceBeforeAssignmentOperators: true
SpaceBeforeParens: Always
SpaceInEmptyParentheses: false
SpacesInAngles: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: 'Cpp11'
UseTab: Never
...
+2
View File
@@ -0,0 +1,2 @@
src/*/bindings/* linguist-generated=true
+92
View File
@@ -0,0 +1,92 @@
*.diff
*.orig
*.patch
*.rej
*.o
*.pyc
*.pyo
*.cwnd
*.dat
*.log
*.mob
*.pcap
*.plt
*.routes
*.tr
[D|U]l[A-Z][a-z]*Stats.txt
seventh-packet-byte-count.png
\#*#
~*
testpy-output
bindings/python/pybindgen/
ms_print.*
massif.*
coverity
TAGS
.lock-waf_*_build
.waf*
build-dir/
build/
/.cproject
/.project
*.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
configure
toggle
/.idea/
/cmake-build-debug/
/build/
+104
View File
@@ -0,0 +1,104 @@
Alexander Afanasyev <alexander.afanasyev@ucla.edu>
Alexander Krotov <krotov@iitp.ru> <ilabdsf@gmail.com>
Alexander Krotov <krotov@iitp.ru> <ilabdsf@yandex.ru>
Alina Quereilhac <alina.quereilhac@inria.fr> <aquereilhac@gmail.com>
Andrey Mazo <mazo@iitp.ru> Andrey Hippo <ahippo@yandex.ru>
Andrey Mazo <mazo@iitp.ru> Andrey Mazo <ahippo@yandex.com>
Andrey Mazo <mazo@iitp.ru> Andrey Mazo <mazo@telum.ru>
Anh Nguyen <annguyen@ittc.ku.edu> Anh Nguyen <trucanh524@gmail.com>
Antti Mäkelä <zarhan@cc.hut.fi>
Biljana Bojovic <bbojovic@cttc.es> <biljana.bojovic@gmail.com>
Biljana Bojovic <bbojovic@cttc.es> biljkus <unknown>
Borovkova Elena <borovkovaes@iitp.ru>
Borovkova Elena <borovkovaes@iitp.ru> Elena Buchatskaya <sunnmy@iitp.ru>
Brett Levasseur <brettl@ll.mit.edu> Brett Levasseur <unknown>
Brett Levasseur <brettl@ll.mit.edu> Brett <unknown>
Budiarto Herman <budiarto.herman@magister.fi> Budiarto Herman <buherman@gmx.com>
Budiarto Herman <budiarto.herman@magister.fi> <mailto:budiarto.herman@magister.fi>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@craig-dowells-imac.local>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@ee.washington.edu>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@gmail.com>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@m354-pc01.ee.washington.edu>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@ns-test>
Craig Dowell <craigdo@ee.washington.edu> <craigdo@ns-test.ee.washington.edu>
Craig Dowell <craigdo@ee.washington.edu> Craig Dowell <unknown>
Duy Nguyen <duy@soe.ucsc.edu> Duy Nguyen <dnlove@gmail.com>
Emmanuelle Laprise <emmanuelle.laprise@bluekazoo.ca> Emmanuelle Laprise <unknown>
Faker Moatamri <faker.moatamri@inria.fr> fmoatamr <unknown>
Federico Maguolo <maguolof@dei.unipd.it>
Federico Maguolo <maguolof@dei.unipd.it> Federico Maguolo <federico.maguolo@dei.unipd.it>
Francisco Javier Sánchez-Roselly <fnavarro@ujaen.es> Francisco Javier Sanchez Roselly <franciscojavier.sanchezroselly@ujaen.es>
Frederic Urbani <frederic.urbani@inria.fr> frederic. <urbani@inria.fr>
George F. Riley <riley@ece.gatech.edu>
Giuseppe Piro <g.piro@giuseppepiro.com> Giuseppe Piro <g.piro@poliba.it>
Giuseppe Piro <g.piro@giuseppepiro.com> Giuseppe Piro <unknown>
Guillaume Seguin <guillaume@segu.in> Guillaume Seguin <guillaume.seguin@sophia.inria.fr>
Gustavo J. A. M. Carneiro <gjc@inescporto.pt> Gustavo Carneiro <gjcarneiro@gmail.com>
Gustavo J. A. M. Carneiro <gjc@inescporto.pt> Gustavo Carneiro <gjc@inescporto.pt>
Gustavo J. A. M. Carneiro <gjc@inescporto.pt> Gustavo J. A. M. Carneiro <gjcarneiro@gmail.com>
Gustavo J. A. M. Carneiro <gjc@inescporto.pt> Gustavo J. A. M. Carneiro <gjcarneiro@gmail.com>
Hajime Tazaki <tazaki@sfc.wide.ad.jp> Hajime Tazaki <tazaki@nict.go.jp>
Hemanth Narra <hemanth@ittc.ku.edu> Hemanth Narra <hemanthnarra222@gmail.com>
Jaume Nin <jnin@cttc.cat> Jaume Nin <unknown>
Jaume Nin <jnin@cttc.cat> <jnin@cttc.es>
Jaume Nin <jnin@cttc.cat> jnin <unknown>
Joe Kopena <tjkopena@cs.drexel.edu>
Joe Kopena <tjkopena@nsnam-code.ece.gatech.edu>
John Abraham <john.abraham@gatech.edu> John Abraham <jabraham3@mail.gatech.edu>
John Abraham <john.abraham@gatech.edu> John Abraham <john.abraham.in@gmail.com>
John Abraham <john.abraham@gatech.edu> John Abraham <unknown>
Josh Pelkey <jpelkey@gatech.edu> Josh Pelkey <joshpelkey@gmail.com>
Josh Pelkey <jpelkey@gatech.edu> Josh Pelkey <jpekey@gatech.edu>
Ken Renard <kenneth.renard@arl.army.mil> Ken Renard <kdrenard2@gmail.com>
Kirill Andreev <andreev@iitp.ru> <andreev@telum.ru>
Kirill Andreev <andreev@iitp.ru> Kirill V. Andreev <kirillano@yandex.ru>
Lalith Suresh <suresh.lalith@gmail.com> Lalith Suresh <unknown>
Lluís Parcerisa <parcerisa@gmail.com>
Luciano Jerez Chaves <ljerezchaves@gmail.com>
Manuel Requena <mrequena@cttc.es> <manuel.requena@cttc.es>
Manuel Requena <mrequena@cttc.es> mrequena <unknown>
Marco Miozzo <marco.miozzo@cttc.es> mmiozzo <unknown>
Marco Miozzo <marco.miozzo@cttc.es> Unknown <mmiozzo@cttc.es>
Marco Miozzo <marco.miozzo@cttc.es> Unknown <mmiozzo@ubuntu>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> <lacage@sophia.inria.fr>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> <mathieu.lacage@cutebugs.net>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> <mathieu.lacage@gmail.com>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> <mathieu.lacage@inria.fr>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> <Mathieu.Lacage@sophia.inria.fr>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> Unknown <mathieu@mathieu.inria.fr>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> Unknown <mlacage@nef.inria.fr>
Mathieu Lacage <mathieu.lacage@sophia.inria.fr> Unknown <mlacage@thira.inria.fr>
Matthieu Coudron <matthieu.coudron@lip6.fr>
Matthieu Coudron <matthieu.coudron@lip6.fr> Matthieu Coudron <mattator@gmail.com>
Mirko Banchi <mk.banchi@gmail.com> mirko <unknown>
Mitch Watrous <watrous@u.washington.edu>
Mitch Watrous <watrous@u.washington.edu> Mitch Watrous <unknown>
Mitch Watrous <watrous@u.washington.edu> watrous <unknown>
Mohit P. Tahiliani <tahiliani@nitk.edu.in> Mohit P. Tahiliani <tahiliani.nitk@gmail.com>
Natale Patriciello <natale.patriciello@gmail.com>
Nicola Baldo <nbaldo@cttc.es> <nicola@baldo.biz>
Peter D. Barnes, Jr. <barnes26@llnl.gov> Peter D. Barnes, Jr. <pdbarnes@llnl.gov>
Quincy Tse <quincy.tse@nicta.com.au> Quincy Tse <quincy.tse@gmail.com>
Raj Bhattacharjea <raj.b@gatech.edu> raj. <b@gatech.edu>
Ramon Bauza <monbauza@gmail.com> Ramon Bauza <unknown>
Ruben Merz <ruben@net.t-labs.tu-berlin.de> Ruben Merz <ruben.merz@ieee.org>
Sascha Alexander Jopen <jopen@informatik.uni-bonn.de> Sascha Alexander Jopen <unknown>
Sébastien Deronne <sebastien.deronne@gmail.com> <sebastienderonne@MacBook-Pro-de-Sebastien.local>
Sébastien Deronne <sebastien.deronne@gmail.com> <sebastien.deronne@umons.ac.be>
Sebastien Vincent <vincent@clarinet.u-strasbg.fr> Sebastien Vincent <sebastien.vincent@turnserver.org>
Stefano Avallone <stavallo@unina.it> <stefano.avallone@unina.it>
Timo Bingmann <timo.bingmann@student.kit.edu> Timo Bingmann <tbns@idlebox.net>
<tjkopena@cs.drexel.edu> <tjkopena@nsnam-code.ece.gatech.edu>
Tom Goff <tgoff@tgoff.net> Tom Goff <thomas.goff@boeing.com>
Tom Goff <tgoff@tgoff.net> Unknown <tgoff@tgoff.net>
Tom Henderson <tomh@tomh.org> Tom Henderson <unknown>
Tom Henderson <tomh@tomh.org> Unknown <tomh@ns-regression.ee.washington.edu>
Tom Henderson <tomh@tomh.org> Unknown <tomh@powerbook.local>
Tom Henderson <tomh@tomh.org> Unknown <tomh@ubuntu>
Tommaso Pecorella <tommaso.pecorella@unifi.it>
Tommaso Pecorella <tommaso.pecorella@unifi.it> Tommaso Pecorella <tpecorella@mac.com>
Tommaso Pecorella <tommaso.pecorella@unifi.it> Tommaso Pecorella <unknown>
Vedran Miletić <rivanvx@gmail.com>
Vedran Miletić <rivanvx@gmail.com> Vedran Miletić <unknown>
Yufei Cheng <yfcheng@ittc.ku.edu>
Yufei Cheng <yfcheng@ittc.ku.edu> Yufei Cheng <yfcheng0@gmail.com>
+32
View File
@@ -0,0 +1,32 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) Launch from scratch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/build/scratch/${fileBasenameNoExtension}",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{
"name": "LD_LIBRARY_PATH",
"value": "./build/lib/"
}
],
"externalConsole": true,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
+25
View File
@@ -0,0 +1,25 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"label": "Build",
"type": "shell",
"command": "./waf",
"group": {
"kind": "build",
"isDefault": true
}
},
{
"label": "Run tests",
"type": "shell",
"command": "./test.py",
"group": {
"kind": "test",
"isDefault": true
}
}
]
}
+252
View File
@@ -0,0 +1,252 @@
John Abraham (john.abraham.in@gmail.com)
Danilo Abrignani (dabrignani@gmail.com)
Alexander Afanasyev (alexander.afanasyev@ucla.edu)
Rohit Agarwal (mindprince@gmail.com)
Piyush Aggarwal (piyush8311@gmail.com)
Zoraze Ali (zoraze.ali@cttc.es)
Robert Ammon (ammo6818@vandals.uidaho.edu)
Kirill Andreev (andreev@iitp.ru)
Dean Armstrong (deanarm@gmail.com)
Gabriel Arrobo (gab.arrobo@gmail.com)
Stefano Avallone (stefano.avallone@unina.it)
Vignesh Babu (ns3-dev@esk.fraunhofer.de)
Ghada Badawy (gbadawy@gmail.com)
Shikha Bakshi (shikhabakshi912@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)
Pedro Bellotti (pedro177@gmail.com)
Mehdi Benamor (mehdi.benamor@telecom-bretagne.eu)
Apoorva Bhargava (apoorvabhargava13@gmail.com)
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)
Matthew Bradbury (matt-bradbury@live.co.uk)
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)
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)
Luciano Jerez Chaves (ljerezchaves@gmail.com)
Eugene Chemeritskiy (echemeritskiy@arccn.ru)
Yufei Cheng (yfcheng@ittc.ku.edu)
Andrey Churin (aachurin@gmail.com)
Micolaj Chwalisz (chwalisz@tkn.tu-berlin.de)
Fernando J. Cintrón (fernando.cintron@nist.gov)
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)
Ankit Deepak (adadeepak8@gmail.com)
Sébastien Deronne (sebastien.deronne@gmail.com)
Christoph Döpmann (doepmanc@informatik.hu-berlin.de)
Craig Dowell (craigdo@ee.washington.edu)
Gilaras Drakeson (gilaras@gmail.com)
Mathias Ettinger (mettinger@toulouse.viveris.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)
Nichit Bodhak Goel (nichit93@gmail.com)
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)
Federico Guerra (federico@guerra-tlc.com)
Shefali Gupta (shefaligups11@gmail.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)
Jack Higgins (shattered.feelings@gmail.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)
Pasquale Imputato (p.imputato@gmail.com)
Muhammad Inamullah (inamullah.m@gmail.com)
Muhammad Iqbal CR (muh.iqbal.cr@gmail.com)
ishan (ishan.chhabra@gmail.com)
Mohamed Amine Ismail (amine.ismail@inria.fr, iamine@udcast.com)
Toshio Ito (debug.ito@gmail.com)
Jared Ivey (j.ivey@gatech.edu)
Atishay Jain (atishayjain25@gmail.com)
Sourabh Jain (jainsourabh679@gmail.com)
Vivek Jain (jain.vivek.anand@gmail.com)
Sam Jansen (sam.jansen@gmail.com)
Liu Jian (liujatp@gmail.com)
Sascha Alexander Jopen (jopen@informatik.uni-bonn.de)
Piotr Jurkiewicz (piotr.jerzy.jurkiewicz@gmail.com)
Evgeny Kalishenko (ydginster@gmail.com)
Vignesh Kannan (vignesh2496@gmail.com)
Tanmay Kathpalia (tanmay.kathpalia@landisgyr.com)
Konstantinos Katsaros (dinos.katsaros@gmail.com)
Morteza Kheirkhah (m.kheirkhah@sussex.ac.uk)
Jörg Christian Kirchhof (christian.kirchhof@rwth-aachen.de)
Shravya Ks (shravya.ks0@gmail.com)
Flavio Kubota (flaviokubota@gmail.com)
Manish Kumar B.(bmanish15597@gmail.com)
Joe Kopena (tjkopena@cs.drexel.edu)
Christopher Kosecki (christopher.l.kosecki.ctr@mail.mil)
Aleksey Kovalenko (kovalenko@iitp.ru)
Hossam Khader (hossamkhader@gmail.com)
Alexander Krotov (ilabdsf@yandex.ru)
Mathieu Lacage (mathieu.lacage@inria.fr)
Emmanuelle Laprise (emmmanuelle.laprise@bluekazoo.ca)
Harsh Lara (harshapplefan@gmail.com)
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)
Andrea Lupia (alupia@dimes.unical.it)
Radu Lupu (rlupu@elcom.pub.ro)
Keith Ma (keith.nwsuaf@gmail.com)
Davide Magrin (magrin.davide@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)
Ryan Mast (mast9@llnl.gov)
Fabian Mauchle (f1mauchl@hsr.ch)
Andrey Mazo (mazo@iitp.ru)
Jonathan McCrohan (jmccroha@tcd.ie)
Andrew McGregor (andrewmcgr@gmail.com)
Miralem Mehic (miralemmehic@gmail.com)
Vedran Miletić (rivanvx@gmail.com)
Saswat Mishra (clicksaswat@gmail.com)
Jens Mittag (jens.mittag@kit.edu)
Viyom Mittal (viyommittal@gmail.com)
Marco Miozzo (mmiozzo@cttc.es)
Faker Moatamri (faker.moatamri@inria.fr)
Edvin Močibob (edvin.mocibob@gmail.com)
Amir Modarresi (amodarresi@ittc.ku.edu)
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)
Alfonso Oliveira (af.oliveira.16@gmail.com)
Luis Pacheco (luisbelem@gmail.com)
Jendaipou Palmei (jendaipoupalmei@gmail.com)
Lluís Parcerisa (parcerisa@gmail.com)
Mingyu Park (darkpmg@naver.com)
Jendaipou Palmei (jendaipoupalmei@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)
Eduardo Pinto (epmcj@hotmail.com)
Giuseppe Piro (g.piro@poliba.it)
Randall Plate (rplate@spawar.navy.mil)
Yana Podkosova (yanapdk@rambler.ru)
Michele Polese (michele.polese@gmail.com)
Ovidiu Poncea (ovidiu.poncea@cs.pub.ro)
Vikas Pushkar (vikaskupushkar@gmail.com)
Alberto Gallegos Ramonet (ramonet@fc.ritsumei.ac.jp)
Manoj Kumar Rana (manoj24.rana@gmail.com)
Andrea Ranieri (andreran@uno.it)
Bruno Ranieri (Yrrsinn@googlemail.com)
Deepti Rajagopal (deeptir96@gmail.com)
Varun Reddy (varunamarreddy@gmail.com)
Ken Renard (kenneth.renard@arl.army.mil)
Getachew Redieteab (getachew.redieteab@orange.com)
Manuel Requena (mrequena@cttc.es)
Jakub Rewienski (jrewienski@gmail.com)
Matias Richart (mrichart@fing.edu.uy)
George F. Riley (riley@ece.gatech.edu)
Juergen Rinas (jrinas@gmx.de)
Sebastian Rohde (sebastian.rohde@tu-dortmund.de)
Karsten Roscher (sfx@rocktale.de)
Ali Rostami (a.rostami@rutgers.edu)
Bill Roome (wdr@bell-labs.com)
David (david.rua@gmail.com)
Andrea Sacco (andrea.sacco85@gmail.com)
Richard Sailer (richard_sailer@systemli.org)
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)
Charitha Sangaraju (charitha29193@gmail.com)
Siddharth Santurkar (siddharth.santurkar@ieee.org)
Gaurav Sathe (gaurav.sathe@tcs.com)
Robert R. Schmidt (rschmi@gmx.net)
Florian Schmidt (Florian.Schmidt@cs.rwth-aachen.de)
Surya Seetharaman (suryaseetharaman.9@gmail.com)
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)
Izydor Sokoler (Izydor.sokoler@gmail.com)
Lauri Sormunen (lauri.sormunen@magister.fi)
Akin Soysal (akinsoysal@gmail.com)
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)
Mohit P. Tahiliani (tahiliani.nitk@gmail.com)
Rohit P. Tahiliani (tahiliar@tcd.ie)
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)
Tinghui Wang (tinghui.wang@wsu.edu)
Mitch Watrous (watrous@u.washington.edu)
Florian Westphal (fw@strlen.de)
He Wu (mdzz@u.washington.edu)
Yoshihiko Yazawa (yoshiyaz@gmail.com)
Menglei Zhang (menglei@nyu.edu)
Dizhi Zhou (dizhi.zhou@gmail.com)
+3151
View File
File diff suppressed because it is too large. Load diff
+103
View File
@@ -0,0 +1,103 @@
cmake_minimum_required (VERSION 2.6)
project (ns)
add_definitions(-DNS3_BUILD_PROFILE_DEBUG -DNS3_ASSERT_ENABLE -DNS3_LOG_ENABLE -DHAVE_SYS_IOCTL_H=1 -DHAVE_IF_NETS_H=1 -DHAVE_NET_ETHERNET_H=1 -DHAVE_PACKET_H=1 -DHAVE_STD_TO_STRING=1 -DHAVE_PTHREAD=1 -DHAVE_SQLITE3=1 -DHAVE_OPENSSL=1 -DHAVE_IF_TUN_H=1)
set(CMAKE_CXX_FLAGS "-std=c++14 -O0 -ggdb -g3 -Wall -Wno-error=deprecated-declarations -fstrict-aliasing -Wstrict-aliasing -std=c++14")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/antenna)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/aodv)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/aodv/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/applications)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/applications/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/bridge)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/bridge/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/buildings)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/buildings/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/config-store)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/config-store/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/core)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/core/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/csma)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/csma/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/csma-layout)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/csma-layout/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/dsdv)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/dsdv/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/dsr)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/dsr/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/energy)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/energy/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/fd-net-device)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/fd-net-device/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/flow-monitor)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/internet)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/internet/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/internet-apps)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/internet-apps/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/lr-wpan)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/lr-wpan/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/lte)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/lte/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mesh)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mesh/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mobility)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mobility/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mpi)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/mpi/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/ndnSIM)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/ndnSIM/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/netanim)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/netanim/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/network)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/network/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/nix-vector-routing)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/nix-vector-routing/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/olsr)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/olsr/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/point-to-point)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/point-to-point/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/point-to-point-layout)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/propagation)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/propagation/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/sixlowpan)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/sixlowpan/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/spectrum)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/spectrum/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/stats)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/stats/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/tap-bridge)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/tap-bridge/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/test)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/topology-read)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/topology-read/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/traffic-control)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/traffic-control/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/uan)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/uan/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/virtual-net-device)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/virtual-net-device/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wave)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wave/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wifi)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wifi/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wimax)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/wimax/examples)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/tcp)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/traffic-control)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/ipv6)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/error-model)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/matrix-topology)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/udp-client-server)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/naming)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/energy)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/wireless)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/routing)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/socket)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/tutorial)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/stats)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/realtime)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/examples/udp)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/scratch)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/scratch/subdir)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/utils)
+288
View File
@@ -0,0 +1,288 @@
# Contributing to ns-3
*This file is heavily inspired by Atom's [CONTRIBUTING.md file](https://raw.githubusercontent.com/atom/atom/master/CONTRIBUTING.md).*
The following is a set of guidelines for contributing to ns-3, which are hosted in the [nsnam organization](https://gitlab.com/nsnam) on GitLab.com. These are mostly guidelines, not rules. Use your best judgment, and feel free to propose changes to this document in a merge request.
#### Table Of Contents
[I don't want to read this whole thing, I just have a question!!!](#i-dont-want-to-read-this-whole-thing-i-just-have-a-question)
[What should I know before I get started?](#what-should-i-know-before-i-get-started)
* [ns-3 Documentation](#ns-3-documentation)
* [ns-3 modules](#ns-3-modules)
[How Can I Contribute?](#how-can-i-contribute)
* [Reporting Bugs](#reporting-bugs)
* [Suggesting Enhancements](#suggesting-enhancements)
* [Your First Code Contribution](#your-first-code-contribution)
* [Pull Requests](#pull-requests)
[Styleguides](#styleguides)
* [Git Commit Messages](#git-commit-messages)
* [C++ Styleguide](#c-styleguide)
* [Documentation Styleguide](#documentation-styleguide)
[Additional Notes](#additional-notes)
* [Issue and Pull Request Labels](#issue-and-pull-request-labels)
## I don't want to read this whole thing I just have a question!!!
> **Note:** Please don't file an issue to ask a question.
You'll get faster results by using the resources below.
We have an official message board where the community chimes in with helpful advice if you have questions.
* [ns-3-users, the official ns-3 message board](https://groups.google.com/forum/#!forum/ns-3-users)
* [ns-3 manual](https://www.nsnam.org/docs/manual/html/index.html)
If chat is more your speed, you can join the ns-3 Zulip channel:
* [Join the ns-3 Zulip chat](https://ns-3.zulipchat.com/)
* Even though Zulip is a chat service, sometimes it takes several hours
for community members to respond &mdash; please be patient!
* Use the `#general` channel for general questions or discussion about ns-3
* Use the `#GSoC` channel for questions about GSoC
* There are many other channels available, check the channel list
## What should I know before I get started?
### ns-3 Documentation
The ns-3 project maintains the documentation in different places, dependings on the need. The documentation that is current with the development tree is the following:
* [Tutorial](https://www.nsnam.org/docs/tutorial/html/index.html): Its purpose is to get you started, with simple examples, into the ns-3 world.
* [Manual](https://www.nsnam.org/docs/manual/html/index.html) is a descriptive documentation of the ns-3 core capabilities, as well as shared development practices.
* [Model library](https://www.nsnam.org/docs/models/html/index.html), contains a list of manuals, one for each module we officially ship.
* [Doxygen](https://www.nsnam.org/docs/doxygen/index.html), contains the programming interface of ns-3, as well as description of classes, methods, and functions that permit the interaction between different modules, as well as with the user code.
### ns-3 modules
ns-3 is an open source project &mdash; it's made up of [49 modules](https://gitlab.com/nsnam/ns-3-dev/tree/master/src). When you initially consider contributing to ns-3, you might be unsure about which of those 49 modules implements the functionality you want to change or report a bug for. This section should help you with that.
ns-3 is intentionally very modular, and it's possible that a feature you need or a technology you may want to use is not coming from the official repository, but rather from a [community maintained module](https://apps.nsnam.org/). Each community module has its own repository too, and it should contain the information on how to integrate that code with ns-3. If you are looking, instead, more information on how to work with ns-3's official modules, please take a look first to the [model documentation](https://www.nsnam.org/docs/models/html/index.html).
### Design Decisions
When we make a significant decision in how we maintain the project and what we can or cannot support, we will document it through the [ns-developers mailing list](https://mailman.isi.edu/mailman/listinfo/ns-developers). If you have a question around how we do things, please subscribe and lurk around to see how we proceed. If you have a development problem, please open a new topic and ask your question.
## How Can I Contribute?
### Reporting Bugs
This section guides you through submitting a bug report for ns-3. Following these guidelines helps maintainers and the community understand your report :pencil:, reproduce the behavior :computer: :computer:, and find related reports :mag_right:.
Before creating bug reports, please check [this list](#before-submitting-a-bug-report) as you might find out that you don't need to create one. When you are creating a bug report, please [include as many details as possible](#how-do-i-submit-a-good-bug-report).
> **Note:** If you find a **Closed** issue that seems like it is the same thing that you're experiencing, open a new issue and include a link to the original issue in the body of your new one.
#### Before Submitting A Bug Report (to be completed)
* **Perform a [cursory search](https://gitlab.com/nsnam/ns-3-dev/issues)** to see if the problem has already been reported. If it has **and the issue is still open**, add a comment to the existing issue instead of opening a new one.
* **Check the [release notes](RELEASE_NOTES)** for a list of known bugs.
#### How Do I Submit A (Good) Bug Report?
Bugs are tracked as [GitLab issues](https://docs.gitlab.com/ee/user/project/issues/). After you've determined [which module](#ns-3-modules) your bug is related to, if it is inside the official distribution then create an issue, label it with the name of the module, and provide as much information as possible.
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you coded your script, e.g. which functions exactly you called. When listing steps, **don't just say what you did, but explain how you did it**. For example, if your program includes modifications to the ns-3 core or a module, please list them (or even better, provide the diffs).
* **Provide specific examples to demonstrate the steps**. Include links to files or projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://docs.gitlab.com/ee/user/markdown.html).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **If you're reporting that ns-3 crashed**, include a crash report with a stack trace from the operating system. On macOS, the crash report will be available in `Console.app` under "Diagnostic and usage information" > "User diagnostic reports". Include the crash report in the issue in a [code block](https://docs.gitlab.com/ee/user/markdown.html), or a file attachment.
* **If the problem is related to performance or memory**, include a CPU profile capture with your report.
Include details about your configuration and environment:
* **Which version of ns-3 are you using?**
* **What's the name and version of the OS you're using**?
* **Which [modules](#ns-3-modules) do you have installed?**
### Suggesting Enhancements
This section guides you through submitting an enhancement suggestion for ns-3, including completely new features and minor improvements to existing functionality. Following these guidelines helps maintainers and the community understand your suggestion :pencil: and find related suggestions :mag_right:.
Before creating enhancement suggestions, please check [this list](#before-submitting-an-enhancement-suggestion) as you might find out that you don't need to create one. When you are creating an enhancement suggestion, please [include as many details as possible](#how-do-i-submit-a-good-enhancement-suggestion).
#### Before Submitting An Enhancement Suggestion
* **Check if there's already [a merge request](https://gitlab.com/nsnam/ns-3-dev/merge_requests) which provides that enhancement.**
* **Determine [which module the enhancement should be suggested in](#ns-3-modules).**
* **Perform a [cursory search](https://gitlab.com/nsnam/ns-3-dev/issues)** to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion?
Enhancement suggestions are tracked as [Gitlab issues](https://docs.gitlab.com/ee/user/project/issues/). After you've determined [which module](#ns-3-modules) your enhancement suggestion is related to, create an issue on that repository and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples.
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
* **Explain why this enhancement would be useful** to most ns-3 users.
* **Specify the name and version of the OS you're using.**
### Your First Code Contribution
Unsure where to begin contributing to ns-3? You can start by looking through these `beginner` and `help-wanted` issues:
* [Beginner issues][beginner] - issues which should only require a few lines of code, and a test or two.
* [Help wanted issues][help-wanted] - issues which should be a bit more involved than `beginner` issues.
Both issue lists are sorted by total number of comments. While not perfect, number of comments is a reasonable proxy for impact a given change will have.
#### Local development
ns-3 and all packages can be developed locally. For instructions on how to do this, see the following sections in the [ns-3 Manual](https://www.nsnam.org/docs/manual/html/index.html):
* Contributing to ns-3 with git
### Merge Requests
The process described here has several goals:
- Maintain ns-3's quality
- Fix problems that are important to users
- Engage the community in working toward the best possible ns-3
- Enable a sustainable system for ns-3's maintainers to review contributions
Please follow these steps to have your contribution considered by the maintainers:
1. Follow the [styleguides](#styleguides)
2. After you submit your merge request, verify that all status checks are passing <details><summary>What if the status checks are failing?</summary>If a status check is failing, and you believe that the failure is unrelated to your change, please leave a comment on the merge request explaining why you believe the failure is unrelated. A maintainer will re-run the status check for you. If we conclude that the failure was a false positive, then we will open an issue to track that problem with our status check suite.</details>
While the prerequisites above must be satisfied prior to having your merge request reviewed, the reviewer(s) may ask you to complete additional design work, tests, or other changes before your pull request can be ultimately accepted.
## Styleguides
### Git Commit Messages
* Use the present tense ("Add feature" not "Added feature")
* Use the imperative mood ("Move cursor to..." not "Moves cursor to...")
* Limit the first line to 72 characters or less
* Reference issues in the first line, by prepending `[#issue]`
### C++ Styleguide
We maintain our code style through the webpage [Code Style](https://www.nsnam.org/develop/contributing-code/coding-style/) and as a `clang-format` configure [file](https://gitlab.com/nsnam/ns-3-dev/blob/master/.clang-format)
### Documentation Styleguide
* Use [Doxygen](http://www.doxygen.nl/) for what regards the code.
* Use [ReStructuredText](http://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html) for what regards the model (module) documentation.
## Additional Notes
### Issue and Pull Request Labels
This section lists the labels we use to help us track and manage issues and pull requests.
The labels are loosely grouped by their purpose, but it's not required that every issue have a label from every group or that an issue can't have more than one label from the same group.
Please open an issue on `infrastructure` if you have suggestions for new labels, and if you notice some labels are missing.
#### Type of Issue and Issue State
| Label name | Description |
| --- | --- |
| `enhancement` | Feature requests. |
| `bug` | Confirmed bugs or reports that are very likely to be bugs. |
| `question` | Questions more than bug reports or feature requests (e.g. how do I do X). |
| `feedback` | General feedback more than bug reports or feature requests. |
| `help-wanted` | The ns-3 team would appreciate help from the community in resolving these issues. |
| `beginner` | Less complex issues which would be good first issues to work on for users who want to contribute to ns-3. |
| `more-information-needed` | More information needs to be collected about these problems or feature requests (e.g. steps to reproduce). |
| `needs-reproduction` | Likely bugs, but haven't been reliably reproduced. |
| `blocked` | Issues blocked on other issues. |
| `duplicate` | Issues which are duplicates of other issues, i.e. they have been reported before. |
| `wontfix` | The ns-3 team has decided not to fix these issues for now, either because they're working as intended or for some other reason. |
| `invalid` | Issues which aren't valid (e.g. user errors). |
#### Topic Categories
| Label name | Description |
| --- | --- |
| `windows` | Related to ns-3 running on Windows. |
| `linux` | Related to ns-3 running on Linux. |
| `mac` | Related to ns-3 running on macOS. |
| `documentation` | Related to any type of documentation (doxygen, manual, models) |
| `git` | Related to Git functionality (e.g. problems with gitignore files or with showing the correct file status). |
| `infrastructure` | Related to gitlab infrastructure. |
| `utils` | Related to scripts and various utils for ns-3 repository or code maintenance. |
#### Pull Request Labels
| Label name | Description
| --- | --- |
| `work-in-progress` | Pull requests which are still being worked on, more changes will follow. |
| `needs-review` | Pull requests which need code review, and approval from maintainers or ns-3 core team. |
| `under-review` | Pull requests being reviewed by maintainers or ns-3 core team. |
| `requires-changes` | Pull requests which need to be updated based on review comments and then reviewed again. |
| `needs-testing` | Pull requests which need manual testing. |
### Module Labels
| Label name | Description |
| --- | --- |
| `animator` | Any issue related to this module |
| `antenna` | Any issue related to this module |
| `aodv` | Any issue related to this module |
| `applications` | Any issue related to this module |
| `bridge` | Any issue related to this module |
| `brite` | Any issue related to this module |
| `build system` | Any issue related to this module |
| `buildings` | Any issue related to this module |
| `click` | Any issue related to this module |
| `config-store` | Any issue related to this module |
| `core` | Any issue related to this module |
| `csma` | Any issue related to this module |
| `designer` | Any issue related to this module |
| `devices` | Any issue related to this module |
| `dsdv` | Any issue related to this module |
| `dsr` | Any issue related to this module |
| `emulation` | Any issue related to this module |
| `energy` | Any issue related to this module |
| `examples` | Any issue related to this module |
| `fd-net-device` | Any issue related to this module |
| `flow-monitor` | Any issue related to this module |
| `global-routing` | Any issue related to this module |
| `general` | Any issue related to this module |
| `helpers` | Any issue related to this module |
| `internet` | Any issue related to this module |
| `internet-apps` | Any issue related to this module |
| `ipv6` | Any issue related to this module |
| `lr-wpan` | Any issue related to this module |
| `lwip` | Any issue related to this module |
| `lte` | Any issue related to this module |
| `mesh` | Any issue related to this module |
| `mobility-models` | Any issue related to this module |
| `mpi` | Any issue related to this module |
| `netanim` | Any issue related to this module |
| `network` | Any issue related to this module |
| `nix-vector` | Any issue related to this module |
| `nsc-tcp` | Any issue related to this module |
| `olsr` | Any issue related to this module |
| `openflow` | Any issue related to this module |
| `point-to-point` | Any issue related to this module |
| `python bindings` | Any issue related to this module |
| `propagation` | Any issue related to this module |
| `quagga` | Any issue related to this module |
| `regression` | Any issue related to this module |
| `routing` | Any issue related to this module |
| `sixlowpan` | Any issue related to this module |
| `spectrum` | Any issue related to this module |
| `stats` | Any issue related to this module |
| `tap-bridge` | Any issue related to this module |
| `tcp` | Any issue related to this module |
| `test framework` | Any issue related to this module |
| `topology-read` | Any issue related to this module |
| `traffic-control` | Any issue related to this module |
| `uan` | Any issue related to this module |
| `virtual-net-device` | Any issue related to this module |
| `visualizer` | Any issue related to this module |
| `wave module` | Any issue related to this module |
| `wifi` | Any issue related to this module |
| `wimax` | Any issue related to this module |
+339
View File
@@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
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.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.
+20
View File
@@ -0,0 +1,20 @@
# Makefile wrapper for waf
all:
./waf
# free free to change this part to suit your requirements
configure:
./waf configure --enable-examples --enable-tests
build:
./waf build
install:
./waf install
clean:
./waf clean
distclean:
./waf distclean
+132
View File
@@ -0,0 +1,132 @@
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
================================
## Table of Contents:
1) [An overview](#an-open-source-project)
2) [Building ns-3](#building-ns-3)
3) [Running ns-3](#running-ns3)
4) [Getting access to the ns-3 documentation](#getting-access-to-the-ns-3-documentation)
5) [Working with the development version of ns-3](#working-with-the-development-version-of-ns-3)
Note: Much more substantial information about ns-3 can be found at
http://www.nsnam.org
## 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
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/
This README excerpts some details from a more extensive
tutorial that is maintained at:
http://www.nsnam.org/documentation/latest/
## Building ns-3
The code for the framework and the default models provided
by ns-3 is built as a set of libraries. User simulations
are expected to be written as simple programs that make
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 to use waf is
included in the file doc/build.txt
However, the real quick and dirty way to get started is to
type the command
```shell
./waf configure --enable-examples
```
followed by
```shell
./waf
```
in the directory which contains this README file. The files
built will be copied in the build/ directory.
The current codebase is expected to build and run on the
set of platforms listed in the [release notes](RELEASE_NOTES)
file.
Other platforms may or may not work: we welcome patches to
improve the portability of the code to these other platforms.
## 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:
```shell
./waf --run simple-global-routing
```
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.
## 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 3) 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/.
This documentation includes:
- a tutorial
- a reference manual
- models in the ns-3 model library
- a wiki for user-contributed tips: http://www.nsnam.org/wiki/
- 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
## Working with the development version of ns-3
If you want to download and use the development version of ns-3, you
need to use the tool `git`. A quick and dirty cheat sheet is included
in the manual, but reading through the git
tutorials found in the Internet is usually a good idea if you are not
familiar with it.
If you have successfully installed git, you can get
a copy of the development version with the following command:
```shell
git clone https://gitlab.com/nsnam/ns-3-dev.git
```
However, we recommend to follow the Gitlab guidelines for starters,
that includes creating a Gitlab account, forking the ns-3-dev project
under the new account's name, and then cloning the forked repository.
You can find more information in the manual [link].
+3410
View File
File diff suppressed because it is too large. Load diff
+1
View File
@@ -0,0 +1 @@
3-dev
@@ -0,0 +1,13 @@
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')])
Whitespace-only changes.
Whitespace-only changes.
+7
View File
@@ -0,0 +1,7 @@
from _ns3 import *
import atexit
atexit.register(Simulator.Destroy)
del atexit
+336
View File
@@ -0,0 +1,336 @@
#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;
}
@@ -0,0 +1,131 @@
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)
+177
View File
@@ -0,0 +1,177 @@
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.values():
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()
@@ -0,0 +1,446 @@
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_transformation(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:
param = Parameter.new(str(param_ctype), arg_name, **kwargs)
cpp_class = getattr(param, "cpp_class", None)
if isinstance(cpp_class, cppclass.CppClass):
# check if the "helper class" can be constructed
if cpp_class.helper_class is not None:
cpp_class.helper_class.generate_forward_declarations(
MemoryCodeSink())
if cpp_class.helper_class.cannot_be_constructed:
cpp_class.helper_class = None
cpp_class.helper_class_disabled = True
arguments.append(param)
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_base::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_base::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"
@@ -0,0 +1,284 @@
#! /usr/bin/env python3
import sys
import os.path
import pybindgen.settings
from pybindgen.castxmlparser 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.free_calldef import free_function_t
from pygccxml.declarations.calldef_members import constructor_t, member_function_t
from pygccxml.declarations.calldef import calldef_t
## we need the smart pointer type transformation to be active even
## during castxml scanning.
import ns3modulegen_core_customizations
## silence castxmlparser 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
if not path:
return
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.
if not ns3_header:
return
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(str(pygccxml_definition))
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(pygccxml_definition, file=sys.stderr)
# 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 list(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)
castxml_options = dict(
include_paths=[top_builddir],
define_symbols={
#'NS3_ASSERT_ENABLE': None,
#'NS3_LOG_ENABLE': None,
},
cflags=('-std=c++14 %s' % 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],
pygen_sink=output_sink,
castxml_options=castxml_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, 0o400)
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)
+335
View File
@@ -0,0 +1,335 @@
#! /usr/bin/env python3
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.free_calldef import free_function_t
from pygccxml.declarations.calldef_members import constructor_t, member_function_t
from pygccxml.declarations.calldef import 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
View File
@@ -0,0 +1 @@
+1
View File
@@ -0,0 +1 @@
placeholder
+909
View File
@@ -0,0 +1,909 @@
# 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 arbitrary) 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 division 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 -- separator 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__'])
+392
View File
@@ -0,0 +1,392 @@
# 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__'])
+551
View File
@@ -0,0 +1,551 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from __future__ import print_function
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/gjcarneiro/pybindgen
REQUIRED_PYBINDGEN_VERSION = '0.21.0'
REQUIRED_PYGCCXML_VERSION = (1, 9, 1)
REQUIRED_CASTXML_VERSION = '0.2'
RUN_ME=-3
# return types of some APIs differ in Python 2/3 (type string vs class bytes)
# This method will decode('utf-8') a byte object in Python 3,
# and do nothing in Python 2
def maybe_decode(input):
if sys.version_info < (3,):
return input
else:
try:
return input.decode('utf-8')
except:
sys.exc_clear()
return input
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 CastXML / 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")
def split_version(version):
if (re.search ('post', version)):
# Version format such as '0.17.0.post58+ngcf00cc0'
ver = re.split('[.+]', version)[:4]
return (int(ver[0]), int(ver[1]), int(ver[2]), int(ver[3].split('post')[1]))
else:
# Version format such as '0.18.0'
ver = re.split('[.]', version)[:3]
return (int(ver[0]), int(ver[1]), int(ver[2]), 0)
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 as 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 as 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 as 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-" + 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.check_output(
[conf.env['PYTHON'][0], "-c",
"try:\n"
" import pybindgen.version\n"
" print(pybindgen.__version__)\n"
"except:\n"
" pass"],
universal_newlines=True).strip()
return out
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:
Logs.warn("pybindgen_version is an empty string")
conf.report_optional_feature("python", "Python Bindings", False,
"PyBindGen version not found")
return
if not (split_version(pybindgen_version) >= split_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("castxml", "Python API Scanning Support", False,
"Missing 'pygccxml' Python module")
return
try:
import pygccxml as pygccxml_imported
pygccxml_version_str = pygccxml_imported.__version__
except (ImportError, AttributeError):
Logs.warn("pygccxml version cannot be determined")
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"pygccxml Python module version is unknown")
return
# 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("castxml", "Python API Scanning Support", False,
"pygccxml Python module too old")
return
## Check castxml version
try:
castxml = conf.find_program('castxml', var='CASTXML')
except WafError:
castxml = None
if not castxml:
Logs.warn("castxml missing; automatic scanning of API definitions will not be possible")
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"castxml missing")
return
out = subprocess.Popen([castxml[0], '--version'],
stdout=subprocess.PIPE).communicate()[0]
castxml_version_line = maybe_decode(out).split('\n', 1)[0].strip()
## Expecting version string such as 'castxml version 0.1-gfab9c47'
m = re.match( "^castxml version (\d\.\d)(-)?(\w+)?", castxml_version_line)
try:
castxml_version = m.group(1)
castxml_version_ok = castxml_version >= REQUIRED_CASTXML_VERSION
except AttributeError:
castxml_version = castxml_version_line
castxml_version_ok = False
conf.msg('Checking for castxml version', castxml_version)
if not castxml_version_ok:
Logs.warn("castxml version unknown or too old, need version >= %s; automatic scanning of API definitions will not be possible" % REQUIRED_CASTXML_VERSION)
conf.report_optional_feature("castxml", "Python API Scanning Support", False,
"castxml too old")
return
## If we reached
conf.env['ENABLE_PYTHON_SCANNING'] = True
conf.report_optional_feature("castxml", "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 castxml to scan the file 'everything.h' and extract API definitions.
"""
before = ['cxxprogram', 'cxxshlib', 'cxxstlib', 'command']
after = ['gen_ns3_module_header', 'ns3header']
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.encode())
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()
if retval >= 0 and "LP64" in self.target:
self.lp64_to_ilp32(
os.path.join(module_path, "bindings", 'modulegen__%s.py' % (self.target)),
os.path.join(module_path, "bindings", 'modulegen__%s.py' % "gcc_ILP32")
)
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 lp64_to_ilp32(self, lp64path, ilp32path):
lp64file = open(lp64path, "r")
lp64bindings = lp64file.read()
lp64file.close()
ilp32file = open(ilp32path, "w")
ilp32bindings = re.sub("unsigned long(?!( long))", "unsigned long long", lp64bindings)
ilp32file.write(ilp32bindings)
ilp32file.close()
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 = ['cxxprogram', 'cxxshlib', 'cxxstlib']
color = 'BLUE'
def run(self):
assert len(self.outputs) == 1
outfile = open(self.outputs[0].abspath(), "w")
print("import warnings", file=outfile)
print('warnings.warn("the ns3 module is a compatibility layer '\
'and should not be used in newly written code", DeprecationWarning, stacklevel=2)', file=outfile)
print(file=outfile)
for module in self.bld.env['PYTHON_MODULES_BUILT']:
print("from ns.%s import *" % (module.replace('-', '_')), file=outfile)
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:
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.
+6
View File
@@ -0,0 +1,6 @@
# Ignore everything by default, contrib sources are coming from other places
*
# Include specific files that should be tracked by Git
!wscript
!.gitignore
+274
View File
@@ -0,0 +1,274 @@
## -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from __future__ import print_function
import os, os.path
import sys
import shutil
import types
import warnings
from waflib import TaskGen, Task, Options, Build, Utils
from waflib.Errors import WafError
import wutils
try:
set
except NameError:
from sets import Set as set # Python 2.3 fallback
all_contrib_modules = []
for dirname in os.listdir('contrib'):
if dirname.startswith('.') or dirname == 'CVS':
continue
path = os.path.join('contrib', dirname)
if not os.path.isdir(path):
continue
if os.path.exists(os.path.join(path, 'wscript')):
all_contrib_modules.append(dirname)
all_contrib_modules.sort()
def get_required_boost_libs(conf):
for module in all_contrib_modules:
conf.recurse (module, name="required_boost_libs", mandatory=False)
def options(opt):
for module in all_contrib_modules:
opt.recurse(module, mandatory=False)
def configure(conf):
for module in all_contrib_modules:
conf.recurse(module, mandatory=False)
## Used to link the 'test-runner' program with all of ns-3 code
conf.env['NS3_CONTRIBUTED_MODULES'] = ['ns3-' + module.split('/')[-1] for module in all_contrib_modules]
# we need the 'ns3module' waf "feature" to be created because code
# elsewhere looks for it to find the ns3 module objects.
def create_ns3_module(bld, name, dependencies=(), test=False):
static = bool(bld.env.ENABLE_STATIC_NS3)
# Create a separate library for this module.
if static:
module = bld(features='cxx cxxstlib ns3module')
else:
module = bld(features='cxx cxxshlib ns3module')
module.target = '%s/lib/ns%s-%s%s' % (bld.srcnode.path_from(module.path), wutils.VERSION,
name, bld.env.BUILD_SUFFIX)
linkflags = []
cxxflags = []
ccflags = []
if not static:
cxxflags = module.env['shlib_CXXFLAGS']
ccflags = module.env['shlib_CXXFLAGS']
# Turn on the link flags for shared libraries if we have the
# proper compiler and platform.
if module.env['CXX_NAME'] in ['gcc', 'icc'] and module.env['WL_SONAME_SUPPORTED']:
# Get the module library name without any relative paths
# at its beginning because all of the libraries will end
# up in the same directory.
module_library_name = module.env.cshlib_PATTERN % (os.path.basename(module.target),)
linkflags = '-Wl,--soname=' + module_library_name
cxxdefines = ["NS3_MODULE_COMPILATION"]
ccdefines = ["NS3_MODULE_COMPILATION"]
module.env.append_value('CXXFLAGS', cxxflags)
module.env.append_value('CCFLAGS', ccflags)
module.env.append_value('LINKFLAGS', linkflags)
module.env.append_value('CXXDEFINES', cxxdefines)
module.env.append_value('CCDEFINES', ccdefines)
module.is_static = static
module.vnum = wutils.VNUM
# Add the proper path to the module's name.
# Set the libraries this module depends on.
module.module_deps = list(dependencies)
module.install_path = "${LIBDIR}"
module.name = "ns3-" + name
module.dependencies = dependencies
# Initially create an empty value for this because the pcfile
# writing task assumes every module has a uselib attribute.
module.uselib = ''
module.use = ['ns3-' + dep for dep in dependencies]
module.test = test
module.is_ns3_module = True
module.ns3_dir_location = bld.path.path_from(bld.srcnode)
module.env.append_value("INCLUDES", '#')
module.pcfilegen = bld(features='ns3pcfile')
module.pcfilegen.module = module.name
return module
def create_ns3_module_test_library(bld, name):
# Create an ns3 module for the test library that depends only on
# the module being tested.
library_name = name + "-test"
library = bld.create_ns3_module(library_name, [name], test=True)
library.features += " ns3testlib"
# Modify attributes for the test library that are different from a
# normal module.
del library.is_ns3_module
library.is_ns3_module_test_library = True
library.module_name = 'ns3-' + name
# Add this module and test library to the list.
bld.env.append_value('NS3_MODULES_WITH_TEST_LIBRARIES', [(library.module_name, library.name)])
# Set the include path from the build directory to modules.
relative_path_from_build_to_here = bld.path.path_from(bld.bldnode)
include_flag = '-I' + relative_path_from_build_to_here
library.env.append_value('CXXFLAGS', include_flag)
library.env.append_value('CCFLAGS', include_flag)
return library
def create_obj(bld, *args):
warnings.warn("(in %s) Use bld(...) call now, instead of bld.create_obj(...)" % str(bld.path),
DeprecationWarning, stacklevel=2)
return bld(*args)
def ns3_python_bindings(bld):
# this method is called from a module wscript, so remember bld.path is not bindings/python!
module_abs_src_path = bld.path.abspath()
module = os.path.basename(module_abs_src_path)
env = bld.env
env.append_value("MODULAR_BINDINGS_MODULES", "ns3-"+module)
if Options.options.apiscan:
return
if not env['ENABLE_PYTHON_BINDINGS']:
return
bindings_dir = bld.path.find_dir("bindings")
if bindings_dir is None or not os.path.exists(bindings_dir.abspath()):
warnings.warn("(in %s) Requested to build modular python bindings, but apidefs dir not found "
"=> skipped the bindings." % str(bld.path),
Warning, stacklevel=2)
return
if ("ns3-%s" % (module,)) not in env.NS3_ENABLED_MODULES:
#print "bindings for module %s which is not enabled, skip" % module
return
env.append_value('PYTHON_MODULES_BUILT', module)
try:
apidefs = env['PYTHON_BINDINGS_APIDEFS'].replace("-", "_")
except AttributeError:
# we likely got an empty list for env['PYTHON_BINDINGS_APIDEFS']
return
#debug = ('PYBINDGEN_DEBUG' in os.environ)
debug = True # XXX
source = [bld.srcnode.find_resource('bindings/python/ns3modulegen-modular.py'),
bld.path.find_resource("bindings/modulegen__%s.py" % apidefs)]
modulegen_customizations = bindings_dir.find_resource("modulegen_customizations.py")
if modulegen_customizations is not None:
source.append(modulegen_customizations)
modulegen_local = bld.path.find_resource("bindings/modulegen_local.py")
# the local customization file may or not exist
if modulegen_local is not None:
source.append("bindings/modulegen_local.py")
module_py_name = module.replace('-', '_')
module_target_dir = bld.srcnode.find_dir("bindings/python/ns").path_from(bld.path)
# if bindings/<module>.py exists, it becomes the module frontend, and the C extension befomes _<module>
if bld.path.find_resource("bindings/%s.py" % (module_py_name,)) is not None:
bld(features='copy',
source=("bindings/%s.py" % (module_py_name,)),
target=('%s/%s.py' % (module_target_dir, module_py_name)))
extension_name = '_%s' % (module_py_name,)
bld.install_files('${PYTHONARCHDIR}/ns', ["bindings/%s.py" % (module_py_name,)])
else:
extension_name = module_py_name
target = ['bindings/ns3module.cc', 'bindings/ns3module.h', 'bindings/ns3modulegen.log']
#if not debug:
# target.append('ns3modulegen.log')
argv = ['NS3_ENABLED_FEATURES=${FEATURES}',
'GCC_RTTI_ABI_COMPLETE=${GCC_RTTI_ABI_COMPLETE}',
'${PYTHON}']
#if debug:
# argv.extend(["-m", "pdb"])
argv.extend(['${SRC[0]}', module_abs_src_path, apidefs, extension_name, '${TGT[0]}'])
argv.extend(['2>', '${TGT[2]}']) # 2> ns3modulegen.log
features = []
for (name, caption, was_enabled, reason_not_enabled) in env['NS3_OPTIONAL_FEATURES']:
if was_enabled:
features.append(name)
bindgen = bld(features='command', source=source, target=target, command=argv)
bindgen.env['FEATURES'] = ','.join(features)
bindgen.dep_vars = ['FEATURES', "GCC_RTTI_ABI_COMPLETE"]
bindgen.before = ['cxxprogram', 'cxxshlib', 'cxxstlib']
bindgen.after = 'gen_ns3_module_header'
bindgen.name = "pybindgen(ns3 module %s)" % module
bindgen.install_path = None
# generate the extension module
pymod = bld(features='cxx cxxshlib pyext')
pymod.source = ['bindings/ns3module.cc']
pymod.target = '%s/%s' % (module_target_dir, extension_name)
pymod.name = 'ns3module_%s' % module
pymod.use = ["%s" % mod for mod in pymod.env['NS3_ENABLED_MODULES']] # Should be '"ns3-"+module', but see bug 1117
if pymod.env['ENABLE_STATIC_NS3']:
if sys.platform == 'darwin':
pymod.env.append_value('LINKFLAGS', '-Wl,-all_load')
for mod in pymod.usel:
#mod = mod.split("--lib")[0]
pymod.env.append_value('LINKFLAGS', '-l' + mod)
else:
pymod.env.append_value('LINKFLAGS', '-Wl,--whole-archive,-Bstatic')
for mod in pymod.use:
#mod = mod.split("--lib")[0]
pymod.env.append_value('LINKFLAGS', '-l' + mod)
pymod.env.append_value('LINKFLAGS', '-Wl,-Bdynamic,--no-whole-archive')
defines = list(pymod.env['DEFINES'])
defines.extend(['NS_DEPRECATED=', 'NS3_DEPRECATED_H'])
if Utils.unversioned_sys_platform() == 'win32':
try:
defines.remove('_DEBUG') # causes undefined symbols on win32
except ValueError:
pass
pymod.env['DEFINES'] = defines
pymod.includes = '# bindings'
pymod.install_path = '${PYTHONARCHDIR}/ns'
# Workaround to a WAF bug, remove this when ns-3 upgrades to WAF > 1.6.10
# https://www.nsnam.org/bugzilla/show_bug.cgi?id=1335
# http://code.google.com/p/waf/issues/detail?id=1098
if Utils.unversioned_sys_platform() == 'darwin':
pymod.mac_bundle = True
return pymod
def build(bld):
bld.create_ns3_module = types.MethodType(create_ns3_module, bld)
bld.create_ns3_module_test_library = types.MethodType(create_ns3_module_test_library, bld)
bld.create_obj = types.MethodType(create_obj, bld)
bld.ns3_python_bindings = types.MethodType(ns3_python_bindings, bld)
# Remove these modules from the list of all modules.
for not_built in bld.env['MODULES_NOT_BUILT']:
if not_built in all_contrib_modules:
all_contrib_modules.remove(not_built)
bld.recurse(list(all_contrib_modules))
for module in all_contrib_modules:
modheader = bld(features='ns3moduleheader')
modheader.module = module.split('/')[-1]
+13
View File
@@ -0,0 +1,13 @@
html/
html-warn/
latex/
ns3-object.txt
introspected-doxygen.h
doxygen.log
doxygen.warnings.log
*/build/
*/figures/*.eps
*/figures/*.pdf
*/figures/*.png
*/source-temp/
ns3_html_theme/static/ns3_version.js
+107
View File
@@ -0,0 +1,107 @@
The Waf build system is used to build ns-3. Waf is a Python-based
build system (http://www.freehackers.org/~tnagy/waf.html)
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
=== Installing Waf ===
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:
https://code.launchpad.net/~gjc/waf/cmd
=== Building with Waf ===
To build ns-3 with waf type the commands from the top-level directory:
1. ./waf configure [options]
2. ./waf
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.
[ 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" ]
The resulting executables and libraries are placed in build/.
Other waf usages include:
1. ./waf configure --enable-examples --enable-tests
Turn on examples and tests.
2. ./waf configure --disable-python
Disable python bindings.
3. ./waf --doxygen
Run doxygen to generate documentation
4. ./waf --run "program [args]"
Run a ns3 program, given its target name, with the given
arguments. This takes care of automatically modifying 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.
4.1 ./waf --run programname --command-template "... %s ..."
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:
./waf --run run-tests --command-template "valgrind %s"
5. ./waf --shell
Starts a nested system shell with modified environment to run ns3 programs.
6. ./waf distclean
Cleans out the entire build/ directory
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 ~).
=== Extending ns-3 ===
To add new modules:
1. Create the module directory under src;
2. Add the source files to it;
3. Add a 'wscript' describing it;
A convenience program to auto-generate the template of a new module can
be found in src/create-module.py.
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):
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()
+2
View File
@@ -0,0 +1,2 @@
The project coding style document is maintained at:
http://www.nsnam.org/developers/contributing-code/coding-style/
+7
View File
@@ -0,0 +1,7 @@
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/
File diff suppressed because it is too large. Load diff
+442
View File
@@ -0,0 +1,442 @@
#!/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 -P)
# 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 [-beithv] [-s <log-file> | -l | -w] [-m <module>] [-f <regex>] [-F <regex>]
Run doxygen to generate all errors; report error counts
by module and file.
-i Skip build and print-introspected-doxygen.
-s Skip doxygen run; use existing <log-file>.
-w Skip doxygen run; use existing warnings log doc/$WARNINGSLOGFILE
-l Skip doxygen run; use the normal doxygen log doc/$STANDARDLOGFILE
-b Omit the blacklist filter of files whose warnings we ignore
-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>
-F Exclude files matching the <regex>
-v Show detailed output from each step. Multiple allowed.
-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_blacklist=1
filter_examples=0
filter_test=0
filter_module=""
filter_in=""
filter_out=""
echo
echo "$me:"
while getopts :bef:F:hilm:s:tvw option ; do
case $option in
(b) filter_blacklist=0 ;;
(e) filter_examples=1 ;;
(f) filter_in="$OPTARG" ;;
(F) filter_out="$OPTARG" ;;
(h) usage ;;
(i) skip_intro=1 ;;
(l) use_standard=1 ;;
(m) filter_module="$OPTARG" ;;
(s) use_filearg=1
logfile_arg="$OPTARG"
;;
(t) filter_test=1 ;;
(v) verbosity=1
exec 6>&1
;;
(w) use_filearg=1
logfile_arg="$DIR/$WARNINGSLOGFILE"
;;
(:) 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..."
(cd "$ROOT" && ./waf build >&6 2>&6 )
status_report $? "./waf build"
verbose -n "Running print-introspected-doxygen..."
(cd "$ROOT" && ./waf --run print-introspected-doxygen >doc/introspected-doxygen.h >&6 2>&6 )
status_report $? "./waf --run print-introspected-doxygen"
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|CLASS_DIAGRAMS|WARNINGS|SOURCE_BROWSER) /s/(YES|yes)/no/;/^HTML_OUTPUT /s/html/html-warn/' $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 --------------------------
#
# Append a regular expression to a parameter
# with '\|' alternation operator if the parameter wasn't empty to begin with.
function REappend
{
local param="$1"
local token="$2"
eval "${param}=\"${!param:-}${!param:+\\|}$token\""
}
# Filter in regular expression for -m and -f
filter_inRE=""
if [ "$filter_module" != "" ] ; then
REappend filter_inRE src/$filter_module
fi
if [ "$filter_in" != "" ] ; then
REappend filter_inRE "$filter_in"
fi
# Blacklist filter of files whose warnings we ignore
filter_blacklistRE=""
# External files: adding our own doxygen makes diffs with upstream very hard
# cairo-wideint
REappend filter_blacklistRE "cairo-wideint"
# Functions with varying numbers of arguments
# This is temporary until we move to C++-14
REappend filter_blacklistRE "Schedule(Time"
REappend filter_blacklistRE "ScheduleWithContext(uint32_t"
REappend filter_blacklistRE "Schedule\\(Now\\|Destroy\\)(\\(MEM\\|void\\)"
# ATTRIBUTE_HELPER_CPP( and _HEADER(
REappend filter_blacklistRE "ATTRIBUTE_HELPER_\\(CPP\\|HEADER\\)("
filter_outRE=""
if [ $filter_blacklist -eq 1 ]; then
echo "Filtering out blacklist: \"$filter_blacklistRE\""
REappend filter_outRE "$filter_blacklistRE"
fi
# Filter out regular expression for -e, -t and -F
if [ $filter_examples -eq 1 ]; then
REappend filter_outRE "/examples/"
fi
if [ $filter_test -eq 1 ]; then
REappend filter_outRE "/test/"
fi
if [ "$filter_out" != "" ] ; then
REappend filter_outRE "$filter_out"
fi
# Show the resulting filters
echo
echo "Net result of all filters:"
if [ "${filter_inRE:-}" != "" ] ; then
echo "Filtering in \"$filter_inRE\""
fi
if [ "${filter_outRE:-}" != "" ] ; then
echo "Filtering out \"$filter_outRE\""
fi
# 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 ----------------------
#
verbose -n "Filtering the doxygen 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
status_report 0 "Filter"
echo
# 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
+109
View File
@@ -0,0 +1,109 @@
/**
* \file
* Main page of the Doxygen-generated documentation.
*/
/**
* \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
* - traffic-control
* - 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.
*/
/**
* \name 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
/**@}*/
+255
View File
@@ -0,0 +1,255 @@
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/working-with-git.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)
+18
View File
@@ -0,0 +1,18 @@
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
Binary file not shown.
Whitespace-only changes.
File diff suppressed because it is too large. Load diff
+610
View File
@@ -0,0 +1,610 @@
.. 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 to the ``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.
+216
View File
@@ -0,0 +1,216 @@
# -*- 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.imgmath']
# 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.30'
# The full version, including alpha/beta/rc tags.
release = 'ns-3.30'
# 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)
]
+647
View File
@@ -0,0 +1,647 @@
.. 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 scanning 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.
* Implementation 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 further
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, as in
``Create<MyObject> (...)``
* ``\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);
+162
View File
@@ -0,0 +1,162 @@
.. 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 interested 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)
+159
View File
@@ -0,0 +1,159 @@
.. 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)
+201
View File
@@ -0,0 +1,201 @@
.. 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 explicitly 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
View File
@@ -0,0 +1 @@
../figures
+305
View File
@@ -0,0 +1,305 @@
.. 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 --run src/stats/examples/gnuplot-example
This should produce the following gnuplot control files:
.. 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:
.. 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 ();
+118
View File
@@ -0,0 +1,118 @@
.. 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 constructor::
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
+38
View File
@@ -0,0 +1,38 @@
.. 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 granularity. 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.
@@ -0,0 +1,143 @@
.. 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
********************************
+37
View File
@@ -0,0 +1,37 @@
.. 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>`_
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.
.. toctree::
:maxdepth: 2
organization
working-with-git
random-variables
hash-functions
events
callbacks
object-model
attributes
object-names
logging
tracing
data-collection
statistics
realtime
helpers
gnuplot
python
tests
support
+516
View File
@@ -0,0 +1,516 @@
.. 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.
In case you want to add logging statements to the methods of your template class
(which are defined in an header file):
1. Invoke the ``NS_LOG_TEMPLATE_DECLARE;`` macro in the private section of
your class declaration. For instance:
::
template <typename Item>
class Queue : public QueueBase
{
...
private:
std::list<Ptr<Item> > m_packets; //!< the items in the queue
NS_LOG_TEMPLATE_DECLARE; //!< the log component
};
This requires you to perform these steps for all the subclasses of your class.
2. Invoke the ``NS_LOG_TEMPLATE_DEFINE (...);`` macro in the constructor of
your class by providing the name of a log component registered by calling
the ``NS_LOG_COMPONENT_DEFINE (...);`` macro in some module. For instance:
::
template <typename Item>
Queue<Item>::Queue ()
: NS_LOG_TEMPLATE_DEFINE ("Queue")
{
}
3. Add logging statements (macro calls) to the methods of your class.
In case you want to add logging statements to a static member template
(which is defined in an header file):
1. Invoke the ``NS_LOG_STATIC_TEMPLATE_DEFINE (...);`` macro in your static
method by providing the name of a log component registered by calling
the ``NS_LOG_COMPONENT_DEFINE (...);`` macro in some module. For instance:
::
template <typename Item>
void
NetDeviceQueue::PacketEnqueued (Ptr<Queue<Item> > queue,
Ptr<NetDeviceQueueInterface> ndqi,
uint8_t txq, Ptr<const Item> item)
{
NS_LOG_STATIC_TEMPLATE_DEFINE ("NetDeviceQueueInterface");
...
2. Add logging statements (macro calls) to your static method.
Controlling timestamp precision
*******************************
Timestamps are printed out in units of seconds. When used with the default
|ns3| time resolution of nanoseconds, the default timestamp precision is 9
digits, with fixed format, to allow for 9 digits to be consistently printed
to the right of the decimal point. Example:
::
+0.000123456s RandomVariableStream:SetAntithetic(0x805040, 0)
When the |ns3| simulation uses higher time resolution such as picoseconds
or femtoseconds, the precision is expanded accordingly; e.g. for picosecond:
::
+0.000123456789s RandomVariableStream:SetAntithetic(0x805040, 0)
When the |ns3| simulation uses a time resolution lower than microseconds,
the default C++ precision is used.
An example program at ``src\core\examples\sample-log-time-format.cc``
demonstrates how to change the timestamp formatting.
The maximum useful precision is 20 decimal digits, since Time is signed 64
bits.
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="***"``).
* Use an explicit cast for any variable of type uint8_t or int8_t,
e.g., ``NS_LOG_LOGIC ("Variable i is " << static_cast<int> (i));``.
Without the cast, the integer is interpreted as a char, and the result
will be most likely not in line with the expectations.
This is a well documented C++ 'feature'.
+573
View File
@@ -0,0 +1,573 @@
.. 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
++++++++++++++++++
+384
View File
@@ -0,0 +1,384 @@
.. 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 implementation 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.
+298
View File
@@ -0,0 +1,298 @@
.. 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.
+9
View File
@@ -0,0 +1,9 @@
.. include:: replace.txt
.. highlight:: cpp
.. _Object-names:
Object names
------------
*Placeholder chapter*
+61
View File
@@ -0,0 +1,61 @@
.. 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>`_).
+462
View File
@@ -0,0 +1,462 @@
.. include:: replace.txt
.. highlight:: python
.. heading hierarchy:
------------- Chapter
************* Section (#.#)
============= Subsection (#.#.#)
############# Paragraph (no number)
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
************
Python bindings provide support for importing |ns3| model libraries as Python
modules. Coverage of most of the |ns3| C++ API is provided. The intent
has been to allow the programmer to write complete simulation scripts in
Python, to allow integration of |ns3| with other Python tools and workflows.
The intent is not to provide a different language choice to author new
|ns3| models implemented in Python.
Python bindings for |ns3| use a tool called PyBindGen (https://github.com/gjcarneiro/pybindgen) to create Python modules from the C++ libraries built by
Waf. The Python bindings that PyBindGen uses are maintained in a
``bindings`` directory in each module, and must be maintained to match the
C++ API of that ns-3 module. If the C++ API changes, the Python bindings file
must either be modified by hand accordingly, or the bindings must be
regenerated by an automated scanning process.
If a user is not interested in Python, he or she may disable the use of
Python bindings at Waf configure time. In this case, changes to the C++
API of a provided module will not cause the module to fail to compile.
The process for automatically generating Python bindings relies on a toolchain
involving a development installation of the Clang compiler, a program called
CastXML (https://github.com/CastXML/CastXML), and a program called
pygccxml (https://github.com/gccxml/pygccxml). The toolchain can be installed
using the ns-3 ``bake`` build tool.
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
As of ns-3.30, a --pyrun-no-build option was added to allow the running of
a program without invoking a project rebuild. This option may be useful
to improve execution time when running the same program repeatedly but with
different arguments, such as from scripts. It can be used in place of
--pyrun such as:
.. sourcecode:: bash
$ ./waf --pyrun-no-build 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. (Note: Need to confirm this limitation still exists with CastXML)
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 = ns.internet.Ipv4AddressHelper()
ipAddrs.SetBase(ns.network.Ipv4Address("192.168.0.0"), ns.network.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.
Working with Python Bindings
****************************
Python bindings are built on a module-by-module basis, and can be found in each module's ``bindings`` directory.
Overview
========
The python bindings are generated into an 'ns' namespace. Examples:
::
from ns.network import Node
n1 = Node()
or
::
import ns.network
n1 = ns.network.Node()
The best way to explore the bindings is to look at the various example
programs provided in |ns3|; some C++ examples have a corresponding Python
example. There is no structured documentation for the Python bindings
like there is Doxygen for the C++ API, but the Doxygen can be consulted
to understand how the C++ API works.
Python Bindings Workflow
========================
The process by which Python bindings are handled is the following:
#. Periodically a developer uses a CastXML (https://github.com/CastXML/CastXML) 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 configure --disable-python ...
To add support for modular bindings to an existing or new |ns3| module, simply add the following line to its wscript build() function:
::
bld.ns3_python_bindings()
One must also provide the bindings files (usually by running the scanning
framework).
Regenerating the Python bindings
================================
|ns3| will fail to successfully compile the Python bindings if the C++
headers are changed and no longer align with the stored Python bindings.
In this case, the developer has two main choices: 1) disable Python
as described above, or 2) update the bindings to align with the new C++
API.
Process Overview
################
|ns3| has an automated process to regenerate Python bindings from the C++
header files. The process is only supported for Linux at the moment
(ns-3.29) because we are in the midst of transition to new tools. The
current process is outlined below. In short, the process currently
requires the following steps.
1. Prepare the system for scanning by installing the prerequisites,
including a development version of ``clang``, the ``CastXML`` package,
and ``pygccxml``.
2. Perform a scan of the module of interest or all modules
Installing a clang development environment
##########################################
Make sure you have a development version of the clang compiler installed
on your system. This can take a long time to build from source. Linux
distributions provide binary library packages such as ``clang-dev`` or
``clang-devel``. The version should not be too important; version 3.8 is
known to work. Note that there is a problem with the Ubuntu
package installation of ``clang-dev``; see the Installation wiki page for
details on how to fix using some symlinks.
Installing other prerequisites
##############################
``cxxfilt`` is a new requirement, typically installed using ``pip``; e.g.
.. sourcecode:: bash
sudo pip install cxxfilt
See also the wiki for installation notes for your system.
Set up a ``bake`` build environment
###################################
Try the following commands:
.. sourcecode:: bash
$ cd bake
$ export BAKE_HOME=`pwd`
$ export PATH=$PATH:$BAKE_HOME/build/bin
$ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$BAKE_HOME/build/lib
$ export PYTHONPATH=$PYTHONPATH:$BAKE_HOME/build/lib
$ mkdir -p build/lib
Configure
#########
Perform a configuration at the bake level:
.. sourcecode:: bash
$ ./bake.py configure -e ns-3-dev -e pygccxml-1.9.1
The output of ``bake show`` should show something like this:
.. sourcecode:: bash
$ ./bake.py show
Should say:
.. sourcecode:: text
-- System Dependencies --
> clang-dev - OK
> g++ - OK
> libxml2-dev - OK
> pygoocanvas - OK
> pygraphviz - OK
> python-dev - OK
> qt - OK
> setuptools - OK
Download
########
Try the following command:
.. sourcecode:: bash
$ ./bake.py download
>> Searching for system dependency python-dev - OK
>> Searching for system dependency clang-dev - OK
>> Searching for system dependency g++ - OK
>> Searching for system dependency setuptools - OK
>> Searching for system dependency pygoocanvas - OK
>> Searching for system dependency pygraphviz - OK
>> Searching for system dependency qt - OK
>> Downloading castxml - OK
>> Downloading netanim - OK
>> Downloading pygccxml-1.9.1 - OK
>> Downloading pygccxml - OK
>> Downloading pybindgen - OK
>> Downloading ns-3-dev - OK
Build
#####
Try the following commands:
.. sourcecode:: bash
$ mkdir -p build/lib
$ ./bake.py build
It should fail on the |ns3| bindings complilation.
The output of './waf configure' can be inspected to see if Python API scanning
support is enabled:
.. sourcecode:: text
Python API Scanning Support : enabled
It may say something like this, if the support is not active:
.. sourcecode:: text
Python API Scanning Support : not enabled (Missing 'pygccxml' Python module)
In this case, the user must take steps to install castxml and pygccxml;
castxml binary must be in the shell's path, and pygccxml must be in the
Python path.
LP64 vs ILP32 bindings
######################
Linux (64-bit, as most modern installations use) and MacOS use different
data models, as explained here: https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.cbcpx01/datatypesize64.htm
Linux uses the LP64 model, and MacOS (as well as 32-bit Linux) use the ILP32
model. Users will note that there are two versions of bindings files in
each ns-3 module directory; one with an ILP32.py suffix and one with
an LP64.py suffix. Only one is used on any given platform. The main
difference is in the representation of the 64 bit integer type as either
a 'long' (LP64) or 'long long' (ILP32).
The process (only supported on Linux at present) generates the LP64
bindings using the toolchain and then copies the LP64 bindings to the
ILP32 bindings with some type subsitutions automated by Waf scripts.
Rescanning a module
###################
To re-scan a module:
.. sourcecode:: bash
$ cd source/ns-3-dev
$ ./waf --apiscan=wifi
To re-scan all modules:
.. sourcecode:: bash
$ cd source/ns-3-dev
$ ./waf --apiscan=all
Generating bindings on MacOS
############################
In principle, this should work (and should generate the 32-bit bindings).
However, maintainers have not been able to complete this port as of ns-3.29.
We would welcome suggestions on how to enable scanning for MacOS.
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>`_.
+382
View File
@@ -0,0 +1,382 @@
.. 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
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.
+96
View File
@@ -0,0 +1,96 @@
.. 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.
+5
View File
@@ -0,0 +1,5 @@
.. |ns3| replace:: *ns-3*
.. |ns2| replace:: *ns-2*
.. |check| replace:: :math:`\checkmark`
+11
View File
@@ -0,0 +1,11 @@
Support
-------
.. toctree::
new-models
new-modules
documentation
enable-modules
enable-tests
troubleshoot
+219
View File
@@ -0,0 +1,219 @@
.. 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.
+814
View File
@@ -0,0 +1,814 @@
.. 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.
+13
View File
@@ -0,0 +1,13 @@
.. 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
+9
View File
@@ -0,0 +1,9 @@
Tests
-----
.. toctree::
test-overview
test-background
test-framework
how-to-write-tests
File diff suppressed because it is too large. Load diff
+87
View File
@@ -0,0 +1,87 @@
.. 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"
+303
View File
@@ -0,0 +1,303 @@
.. include:: replace.txt
.. highlight:: cpp
.. Section Separators:
----
****
++++
====
~~~~
.. _Working with git:
Working with git as a user
--------------------------
The ns-3 project used Mercurial in the past as its source code control system, but it has moved to Git in December 2018. Git is a VCS like Mercurial, Subversion or CVS, and it is used to maintain many open-source (and closed-source) projects. While git and mercurial have a lot of common properties, if you are new to git you should read first an introduction to it. The most up-to-date guide is the Git Book, at https://git-scm.com/book/en/v2/Getting-Started-Git-Basics.
The ns-3 project is officially hosted on GitLab.com at https://gitlab.com/nsnam/. For convenience and historical reasons, ns-3-dev mirrors are currently posted on Bitbucket.com and GitHub.com, and kept in sync with the official repository periodically via cron jobs. We recommend that users who have been working from one of these mirrors repoint their remotes so that they pull origin or upstream from GitLab.com (see below explanation about how to configure remotes).
This section of the manual provides common tips for both users and maintainers. Since the first part is shared, in this manual section we will start a personal repository and then explain what to do in some typical cases. ns-3 users often combine ns-3-dev with other repositories (pybindgen, netanim, apps from the app store). This manual chapter does not cover this use case; it only focuses on the single ns-3-dev repository. See other project documentation such as the ns-3 tutorial for descriptions on bundled releases distributed as source archives, or on the bake build tool for managing multiple repositories. The guidelines listed below also largely pertain to the user who is using (and cloning) bake from the GitLab.com repository.
ns-3's Git workflow in a nutshell
*********************************
Experienced git users will not necessarily need instruction on how to set up personal repositories (below). However, they should be aware of the project's workflow:
* The main repository's master branch is the main development branch.
* Releases are tagged changesets on the master branch. If a hotfix release must be made from a changeset in the past, a new hotfix support branch will be created and maintained for any such releases. Hotfix releases will be tagged from the hotfix support branch if it has diverged from master.
* Maintainers can commit obvious non-critical fixes (documentation improvements, typos etc.) directly into the master branch. Users who are not maintainers can create Merge Requests for small items such as these.
* For each new major feature, create a feature branch in your own fork. Create Merge Requests as needed to solicit review. Merge feature branches into master after review. Merged feature branches can then be deleted.
Setup of a personal repository
******************************
We will provide two ways, one anonymous (but will impede the creation of merge requests) and the other, preferred, that include forking the repository through the GitLab.com web interface.
Directly cloning ns-3-dev
+++++++++++++++++++++++++
If you go to the official ns-3-dev page, hosted at https://gitlab.com/nsnam/ns-3-dev, you can find a button that says ```Clone``. If you are not logged in, then you will see only the option of cloning the repository through HTTPS, with this command:
.. sourcecode:: bash
$ git clone https://gitlab.com/nsnam/ns-3-dev.git
If this command exits successfully, you will have a newly created `ns-3-dev` directory with all the source code.
Forking ns-3-dev on GitLab.com
++++++++++++++++++++++++++++++
Assume that you are the user *john* on GitLab.com and that you want to create a new repository that is synced with nsnam/ns-3-dev.
#. Log into GitLab.com
#. Navigate to https://gitlab.com/nsnam/ns-3-dev
#. In the top-right corner of the page, click ``Fork``.
Note that you may only do this once; if you try to fork again, Gitlab will take you to the page of the original fork. So, if you are planning to maintain two or more separate forks (for example, one for your private work, another for maintenance, etc.), you are doing a mistake. Instead, you should add these forks as a remote of your existing directory (see below for adding remotes). Usually, it is a good thing to add the maintainer's repository as remotes, because it can happen that "bleeding edge" features will appear there before landing in ns-3.
For more information on forking with Gilab, there is plenty of visual documentation (https://docs.gitlab.com/ee/gitlab-basics/fork-project.html). To work with your forked repository, you have two way: one is a clean clone while the other is meant to re-use an existing ns-3 git repository.
Clone your forked repository on your machine
============================================
Git is a distributed versioning system. This means that *nobody* will touch your personal repository, until you do something. Please note that every gitlab user has, at least, two repositories: the first is represented by the repository hosted on gitlab servers, which will be called in the following ``origin``. Then, you have your clone on your machine. This means that you could have many clones, on different machines, which points to ``origin``.
To clone the newly created fork to your system go to the homepage of your fork (that should be in the form `https://gitlab.com/your-user-name/ns-3-dev`) and click the `Clone` button. Then, go to your computer's terminal, and issue the command (please refer to https://docs.gitlab.com/ee/gitlab-basics/command-line-commands.html#clone-your-project for more documentation):
.. sourcecode:: bash
$ git clone https://gitlab.com/your-user-name/ns-3-dev
$ cd ns-3-dev-git
In this example we used the HTTPS address because in some place the git + ssh address is blocked by firewalls. If you are not under this constraint, it is recommended to use the git + ssh address to avoid the username/password typing at each request.
Naming conventions
==================
Git is able to fetch and push changes to several repositories, each of them is called ``remote``. With time, you probably will have many remotes, each one with many branches. To avoid confusion, it is recommended to give meaningful names to the remotes; in the following, we will use ``origin`` to indicate the ns-3-dev repository in your personal namespace (your forked version, server-side) and ``nsnam`` to indicate the ns-3-dev repository in the nsnam namespace, server-side.
Add the official ns-3 repository as remote upstream
***************************************************
You could have already used git in the past, and therefore already having a ns-3 git repository somewhere. Or, instead, you could have it cloned for the first time in the step above. In both cases, when you fork/clone a repository, your history is no more bound to the repository itself. At this point, it is your duty to sync your fork with the original repository. The first remote repository we have encountered is ``origin``; we must add the official ns-3 repo as another remote repository:
.. sourcecode:: bash
$ git remote add nsnam https://gitlab.com/nsnam/ns-3-dev
With the command above, we added a remote repository, named upstream, which links to the official ns-3 repo. To show your remote repositories:
.. sourcecode:: bash
$ git remote show
To see to what ``origin`` is linking to:
.. sourcecode:: bash
$ git remote show origin
Many options are available; please refer to the git manual for more.
Add your forked repository as remote
************************************
If you were a user of the old github mirror, you probably have an existing git repository installed somewhere. In your case, it is not necessary to clone your fork and to port all your work in the new directory; you can add the fork as new remote.
.. sourcecode:: bash
$ git remote rename origin old-origin
$ git remote add origin https://gitlab.com/your-user-name/ns-3-dev
After these two commands, you will have a remote, named origin, that points
to your forked repository on gitlab.
Keep in sync your repository with latest ns-3-dev updates
*********************************************************
We assume, from now to the end of this document, that you will not make commits on top of the master branch. It should be kept clean from *any* personal modifications: all the works must be done in branches. Therefore, to move the current HEAD of the master branch to the latest commit in ns-3-dev, you should do:
.. sourcecode:: bash
$ git checkout master
$ git fetch nsnam
$ git pull nsnam master
If you tried a pull which resulted in a conflict and you would like to start over, you can recover with git reset (but this never happens if you do not commit over master).
Start a new branch to do some work
**********************************
Look at the available branches:
.. sourcecode:: bash
$ git branch -a
you should see something like:
.. sourcecode:: bash
* master
remotes/origin/master
remotes/nsnam/master
The branch master is your local master branch; remotes/origin/master point at the master branch on your repository located in the Gitlab server, while remotes/nsnam/master points to the official master branch.
Before entering in details on how to create a new branch, we have to explain why it is recommended to do it. First of all, if you put all your work in a separate branch, you can easily see the diff between ns-3 mainline and your feature branch (with ``git diff master``). Also, you can integrate more easily the upstream advancements in your work, and when you wish, you can create a *conflict-free* merge request, that will ease the maintainer's job in reviewing your work.
To create a new branch, starting from master, the command is:
.. sourcecode:: bash
$ git checkout master
$ git checkout -b [name_of_your_new_branch]
To switch between branches, remove the -b option. You should now see:
.. sourcecode:: bash
$ git branch -a
* master
[name_of_your_new_branch]
remotes/origin/master
remotes/upstream/master
Edit and commit the modifications
*********************************
After you edit some file, you should commit the difference. As a policy, git users love small and incremental patches. So, commit early, and commit often: you could rewrite your history later.
Suppose we edited ``src/internet/model/tcp-socket-base.cc``. With git status, we can see the repository status:
.. sourcecode:: bash
$ git status
On branch tcp-next
Your branch is up-to-date with 'mirror/tcp-next'.
Changes not staged for commit:
modified: src/internet/model/tcp-socket-base.cc
and we can see the edits with git diff:
.. sourcecode:: text
$ git diff
nat@miyamoto ~/Work/ns-3-dev-git (tcp-next)$ git diff
diff --git i/src/internet/model/tcp-socket-base.cc w/src/internet/model/tcp-socket-base.cc
index 1bf0f69..e2298b0 100644
--- i/src/internet/model/tcp-socket-base.cc
+++ w/src/internet/model/tcp-socket-base.cc
@@ -1439,6 +1439,10 @@ TcpSocketBase::ReceivedAck (Ptr<Packet> packet, const TcpHeader& tcpHeader)
// There is a DupAck
++m_dupAckCount;
+ // I'm introducing a subtle bug!
+
+ m_tcb->m_cWnd = m_tcb->m_ssThresh;
+
if (m_tcb->m_congState == TcpSocketState::CA_OPEN)
{
// From Open we go Disorder
To create a commit, select the file you want to add to the commit with git add:
.. sourcecode:: bash
$ git add src/internet/model/tcp-socket-base.cc
and then commit the result:
.. sourcecode:: bash
$ git commit -m "My new TCP broken"
Of course, it would be better to have some rules for the commit message: they will be reported in the next subsection.
Commit message guidelines
+++++++++++++++++++++++++
The commit title should not go over the 80 char limit. It should be prefixed by the name of the module you are working on, and if it fixes a bug, it should reference it in the commit title. For instance, a good commit title would be:
tcp: My new TCP broken
Another example is:
tcp: (fixes #2322) Corrected the uint32_t wraparound during recovery
In the body message, try to explain what the problem was, and how you resolved that. If it is a new feature, try to describe it at a very high level, and highlight any modifications that changed the behaviour or the interface towards the users or other modules.
Commit log
++++++++++
You can see the history of the commits with git log. To show a particular commit, copy the sha-id and use ``git show <sha-id>``. The ID is unique, so it can be referenced in emails or in issues. The next step is useful if you plan to contribute back your changes, but also to keep your feature branch updated with the latest changes from ns-3-dev.
Rebase your branch on top of master
***********************************
Meanwhile you were busy with your branch, the upstream master could have changed. To rebase your work with the now new master, first of all sync your master branch (pulling the nsnam/master branch into your local master branch) as explained before; then
.. sourcecode:: bash
$ git checkout [name_of_your_new_branch]
$ git rebase master
The last command will rewind your work, update the HEAD of your branch to the actual master, and then re-apply all your work. If some of your work conflicts with the actual master, you will be asked to fix these conflicts if automatic merge fails.
Pushing your changes to origin
******************************
After you have done some work on a branch, if you would like to share it with others there is nothing better than pushing your work to your origin repository, on Gitlab servers.
.. sourcecode:: bash
$ git checkout [name_of_your_new_branch]
$ git push origin [name_of_your_new_branch]
The ``git push`` command can be used every time you need to push something from your computer to a remote repository, except when you propose changes to the main ns-3-dev repository: your changes must pass a review stage.
Please note that for older git version, the push command looks like:
.. sourcecode:: bash
$ git push -u origin [name_of_your_new_branch]
Submit work for review
**********************
After you push your branch to origin, you can follow the instructions here https://docs.gitlab.com/ee/gitlab-basics/add-merge-request.html to create a merge request. Please remember to add, as reviewer, at least one maintainer. To get the information on who is maintaining what, please refer to the nsnam website.
Porting patches from mercurial repositories to git
**************************************************
Placeholder section. Please improve it.
Working with git as a maintainer
--------------------------------
As a maintainer, you are a person who has write access to the main nsnam repository. You could push your own work (without passing from code review) or push someone else's work. Let's investigate the two cases.
Pushing your own work
*********************
Since you have been added to the Developer list on Gitlab (if not, please open an issue) you can use the git + ssh address when adding nsnam as remote. Once you have done that, you can do your modifications to a local branch, then update the master to point to the latest changes of the nsnam repo, and then:
.. sourcecode:: bash
$ git checkout master
$ git pull nsnam master
$ git merge [your_branch_name]
$ git push nsnam master
Please note that if you want to keep track of your branch, you can use as command ``git merge --no-ff [your_branch_name]``. It is always recommended to rebase your branch before merging, to have a clean history. That is not a requirement, though: git perfectly handles a master with parallel merged branches.
Review and merge someone else's work
************************************
Gitlab.com has a plenty of documentation on how to handle merge requests. Please take a look here: https://docs.gitlab.com/ee/user/project/merge_requests/index.html.
If you are committing a patch from someone else, and it is not coming through a Merge Request process, you can use the --author='' argument to 'git commit' to assign authorship to another email address (such as we have done in the past with the Mercurial -u option).
+651
View File
@@ -0,0 +1,651 @@
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 \
source/traffic-control.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)/mesh/doc/source/mesh-user.rst \
$(SRC)/mesh/doc/source/mesh-design.rst \
$(SRC)/mesh/doc/source/mesh-references.rst \
$(SRC)/mesh/doc/source/mesh-testing.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)/network/doc/queue-limits.rst \
$(SRC)/nix-vector-routing/doc/nix-vector-routing.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/udp.rst \
$(SRC)/internet-apps/doc/internet-apps.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)/traffic-control/doc/traffic-control-layer.rst \
$(SRC)/traffic-control/doc/queue-discs.rst \
$(SRC)/traffic-control/doc/pfifo-fast.rst \
$(SRC)/traffic-control/doc/fifo.rst \
$(SRC)/traffic-control/doc/prio.rst \
$(SRC)/traffic-control/doc/tbf.rst \
$(SRC)/traffic-control/doc/red.rst \
$(SRC)/traffic-control/doc/codel.rst \
$(SRC)/traffic-control/doc/fq-codel.rst \
$(SRC)/traffic-control/doc/pie.rst \
$(SRC)/traffic-control/doc/mq.rst \
$(SRC)/spectrum/doc/spectrum.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)/applications/doc/http-embedded-object-size.png \
$(SRC)/applications/doc/http-main-object-size.png \
$(SRC)/applications/doc/http-num-of-embedded-objects.png \
$(SRC)/applications/doc/http-parsing-time.png \
$(SRC)/applications/doc/http-reading-time.png \
$(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)/internet/doc/figures/tcp-state-machine.png \
$(SRC)/wifi/doc/source/figures/WifiArchitecture.dia \
$(SRC)/wifi/doc/source/figures/snir.dia \
$(SRC)/wifi/doc/source/figures/clear-channel.eps \
$(SRC)/wifi/doc/source/figures/nist-frame-success-rate.eps \
$(SRC)/wifi/doc/source/figures/nist-frame-success-rate-n.eps \
$(SRC)/wifi/doc/source/figures/nist-frame-success-rate-ac.eps \
$(SRC)/wifi/doc/source/figures/nist-frame-success-rate-ax.eps \
$(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-dl-with-split.dia \
$(SRC)/lte/doc/source/figures/epc-data-flow-ul.dia \
$(SRC)/lte/doc/source/figures/epc-data-flow-ul-with-split.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-with-split.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-epc-e2e-data-protocol-stack-with-split.dia \
$(SRC)/lte/doc/source/figures/lte-epc-e2e-control-protocol-stack-with-split.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-test-rlf-one-enb.dia \
$(SRC)/lte/doc/source/figures/lte-test-rlf-two-enb.dia \
$(SRC)/lte/doc/source/figures/lena-radio-link-failure-one-enb.dia \
$(SRC)/lte/doc/source/figures/lena-radio-link-failure-two-enb.dia \
$(SRC)/lte/doc/source/figures/lte-epc-x2-entity-saps.dia \
$(SRC)/lte/doc/source/figures/ca-rrc-reconf.dia \
$(SRC)/lte/doc/source/figures/ca-lte-enb-net-device-changes.dia \
$(SRC)/lte/doc/source/figures/ca-lte-ue-net-device-changes.dia \
$(SRC)/lte/doc/source/figures/ca-enb-data-plane.dia \
$(SRC)/lte/doc/source/figures/ca-enb-ctrl-plane.dia \
$(SRC)/lte/doc/source/figures/ca-ue-data-plane.dia \
$(SRC)/lte/doc/source/figures/ca-ue-ctrl-plane.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/ca-downlink-bsr.pdf \
$(SRC)/lte/doc/source/figures/ca-downlink-bsr.png \
$(SRC)/lte/doc/source/figures/ca-setup-radio-bearer.pdf \
$(SRC)/lte/doc/source/figures/ca-setup-radio-bearer.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/carrier-aggregation-impact.pdf \
$(SRC)/lte/doc/source/figures/carrier-aggregation-impact.png \
$(SRC)/lte/doc/source/figures/carrier-aggregation-mac-impact.pdf \
$(SRC)/lte/doc/source/figures/carrier-aggregation-mac-impact.png \
$(SRC)/lte/doc/source/figures/ca-uplink-bsr.pdf \
$(SRC)/lte/doc/source/figures/ca-uplink-bsr.png \
$(SRC)/lte/doc/source/figures/ca-test-example-ul.pdf \
$(SRC)/lte/doc/source/figures/ca-test-example-ul.png \
$(SRC)/lte/doc/source/figures/ca-test-example-dl.pdf \
$(SRC)/lte/doc/source/figures/ca-test-example-dl.png \
$(SRC)/lte/doc/source/figures/lena-radio-link-failure-one-enb-thrput.png \
$(SRC)/lte/doc/source/figures/lena-radio-link-failure-two-enb-thrput.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)/lte/doc/source/figures/lte-ue-context-removal-from-epc.pdf \
$(SRC)/lte/doc/source/figures/lte-ue-context-removal-from-epc.png \
$(SRC)/lte/doc/source/figures/lte-ue-context-removal-from-enb-stack.pdf \
$(SRC)/lte/doc/source/figures/lte-ue-context-removal-from-enb-stack.png \
$(SRC)/lte/doc/source/figures/lte-ue-procedures-after-rlf.pdf \
$(SRC)/lte/doc/source/figures/lte-ue-procedures-after-rlf.png \
$(SRC)/uan/doc/auvmobility-classes.dia \
$(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)/traffic-control/doc/classful-queue-disc.dia \
$(SRC)/traffic-control/doc/multi-queue-aware-queue-disc.dia \
$(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)/epc-ctrl-arch.eps \
$(FIGURES)/epc-data-flow-dl.eps \
$(FIGURES)/epc-data-flow-dl-with-split.eps \
$(FIGURES)/epc-data-flow-ul.eps \
$(FIGURES)/epc-data-flow-ul-with-split.eps \
$(FIGURES)/epc-profiling-scenario.eps \
$(FIGURES)/epc-topology.eps \
$(FIGURES)/epc-topology-with-split.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-epc-e2e-data-protocol-stack-with-split.eps \
$(FIGURES)/lte-epc-e2e-control-protocol-stack-with-split.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)/classful-queue-disc.eps \
$(FIGURES)/multi-queue-aware-queue-disc.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 \
$(FIGURES)/clear-channel.eps \
$(FIGURES)/nist-frame-success-rate.eps \
$(FIGURES)/nist-frame-success-rate-n.eps \
$(FIGURES)/nist-frame-success-rate-ac.eps \
$(FIGURES)/nist-frame-success-rate-ax.eps \
$(FIGURES)/ca-rrc-reconf.eps \
$(FIGURES)/ca-lte-enb-net-device-changes.eps \
$(FIGURES)/ca-lte-ue-net-device-changes.eps \
$(FIGURES)/ca-enb-data-plane.eps \
$(FIGURES)/ca-enb-ctrl-plane.eps \
$(FIGURES)/ca-ue-data-plane.eps \
$(FIGURES)/ca-ue-ctrl-plane.eps \
$(FIGURES)/lte-test-rlf-one-enb.eps \
$(FIGURES)/lte-test-rlf-two-enb.eps \
$(FIGURES)/lena-radio-link-failure-one-enb.eps \
$(FIGURES)/lena-radio-link-failure-two-enb.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)/lena-radio-link-failure-one-enb.pdf_width = 10cm
$(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
$(FIGURES)/clear-channel.pdf_width = 12cm
$(FIGURES)/nist-frame-success-rate.pdf_width = 12cm
$(FIGURES)/nist-frame-success-rate-n.pdf_width = 12cm
$(FIGURES)/nist-frame-success-rate-ac.pdf_width = 12cm
$(FIGURES)/nist-frame-success-rate-ax.pdf_width = 12cm
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."
Binary file not shown.
Binary file not shown.
+2
View File
@@ -0,0 +1,2 @@
This directory stores .rst files that are used to build the model library
documentation but that are not stored with a particular module.
Whitespace-only changes.
+216
View File
@@ -0,0 +1,216 @@
# -*- 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.imgmath']
# 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.30'
# The full version, including alpha/beta/rc tags.
release = 'ns-3.30'
# 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)
]
@@ -0,0 +1,87 @@
.. 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 ``TapBridge``
``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.
For more details:
* :doc:`FdNetDevice <fd-net-device>` chapter.
* :doc:`TapBridge <tap>` chapter.
+55
View File
@@ -0,0 +1,55 @@
.. 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
dsdv
dsr
emulation-overview
energy
fd-net-device
flow-monitor
internet-models
lr-wpan
lte
mesh
distributed
mobility
network
nix-vector-routing
olsr
openflow-switch
point-to-point
propagation
spectrum
sixlowpan
tap
topology
traffic-control
uan
wave
wifi
wimax
@@ -0,0 +1,12 @@
Internet Models (IP, TCP, Routing, UDP, Internet Applications)
---------------------------------------------------------------------
.. toctree::
internet-stack
ipv4
ipv6
routing-overview
tcp
udp
internet-apps
+12
View File
@@ -0,0 +1,12 @@
Network Module
--------------
.. toctree::
packets
error-model
network-overview
sockets-api
simple
queue
queue-limits
+53
View File
@@ -0,0 +1,53 @@
.. 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.
+3
View File
@@ -0,0 +1,3 @@
.. |ns3| replace:: *ns-3*
.. |ns2| replace:: *ns-2*
@@ -0,0 +1,16 @@
Traffic Control Layer
---------------------------------------------------------------------
.. toctree::
traffic-control-layer
queue-discs
fifo
pfifo-fast
prio
tbf
red
codel
fq-codel
pie
mq
+22
View File
@@ -0,0 +1,22 @@
/**
* @file
* @ingroup utils
* @brief Definition of support modules Constants, Utils, Example programs and
* Test suites.
*/
/**
* @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 examples Example programs
* @brief All example programs
*
* @defgroup tests Test suites
* @brief All test suites
*/
File diff suppressed because it is too large. Load diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

+85
View File
@@ -0,0 +1,85 @@
.. 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.
+220
View File
@@ -0,0 +1,220 @@
#!/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
# 'git describe HEAD --tags' will return a string such as either
# 'ns-3.29-159-xxx' (if we are at a distance from the last release tag) or
# 'ns-3.29' if we are at the tag.
distance=`git describe HEAD --tags | cut -c 9- | cut -d '-' -f 1`
if [ $distance -eq 1 ]; then
version=`git describe HEAD --tags | cut -d '-' -f 2`
say "at version $version"
elif [ $tag -eq 1 ]; then
distance=1
# version previously set
vers_href=
else
version=`git describe HEAD --tags | cut -c 9- | cut -d '-' -f 2 | cut -c 2-`
# Check for uncommitted changes
changes=0
if [ "$(git status --porcelain)" ]; then
changes=1
fi
if [ $changes ] ; 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="https://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
vers_href="https://gitlab.com/nsnam/ns-3-dev/commits/$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.
+121
View File
@@ -0,0 +1,121 @@
{#
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">&nbsp;</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/"
>&nbsp;&nbsp;Home</a>
</li>
<li><span
onmouseover="mopen('mTuts')"
onmouseout="mclosetime()"
>Tutorials &nbsp;&#x25BC;</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 &nbsp;&nbsp;&nbsp;&#x25BC;</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 &#x25BC;</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">&nbsp;</span></li>
{{ super() }}
{% endblock %}
{% if theme_collapsiblesidebar|tobool %}
{% set script_files = script_files + ['_static/sidebar.js'] %}
{% endif %}
@@ -0,0 +1,24 @@
<!-- 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 &#160;<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>
@@ -0,0 +1,110 @@
<!-- 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/"
>&nbsp;&nbsp;Home</a>
</li>
<li><span
onmouseover="mopen('mTuts')"
onmouseout="mclosetime()"
>Tutorials &nbsp;&#x25BC;</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 &nbsp;&nbsp;&nbsp;&#x25BC;</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 &#x25BC;</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 -->
Binary file not shown.

After

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 635 B

Loaded 100 of 5291 files, more files were not shown because too many files have changed in this diff. Show more