We divide the simulator along two directions. At the higher level, the simulator is divided between the executable harness and dynamically loadable modules. This makes the harness (which is this package) easier to write and maintain. It also provides for greater flexibility, since detailed functionality is pushed to modules, which can potentially be combined into increasingly complex models.
At the lower level, the simulator is also divided between mechanism and interaction. The mechanism portion of the simulator is written in C++, and all modules should similarly be written (at least partially) in C++. For control of the simulation, which includes both interactive control and batch scripting, a separate interpreted language is used. The interpreter is embedded within the harness executable, so that running the simulator also starts the interpreter. Code that is specific to a particular interpretive language is isolated into a single file, in order to simplify additional implementations in other languages.
When running the simulator, the first thing that must be done is to configure the various elements that will make up the network. At least one event should also be scheduled. When the simulator is told to run, it will process events until either there are no more remaining in its queue or an event triggers a breakpoint. Using breakpoints rather than terminating the simulation allows us to examine and potentially modify the system before deciding whether to exit or continue running from where we stopped.
An Entity produces and consumes Events. Nearly every "thing" in a simulation will be an Entity, such as nodes, links, or data analyzers. A specific type of Entity will be able to handle certain types of Events, and a specific type of Event will generally only be able to take certain types of Entitys as destinations. The Entity base class only knows about DataEvents, since those are sufficiently generic that we can reasonably expect all Entitys to handle them (though a few might return without doing anything). One concrete Entity subclass is defined: Terminator. The only purpose of this class is to produce and consume Events that cause the simulation to stop. As noted above, these are effectively breakpoints, and the internal state of the simulator is otherwise left unchanged. Most Entitys will need to be entered into the Registry, so that they can be created from the interpreter. Terminator is an exception to this, and some other Entitys might also not be made visible to the interface.
class GeosynchronousSatellite : public Entity { public: // ... std::string identifier() const { return "geosynchronous satellite"; } // ... };
argc and argv. The Entity is responsible for ensuring that its arguments are correct in number and type. Entitys referenced within the interpreter are passed as string tokens that may be passed to resolve_symbol(). X_main.cc are taken to be interpreter glue, where X is the language of the interpreter. The interpreter hooks should be defined here. These glue files also include the main routine.The general pattern for an interpreter interface is that modules are loaded, the network is configured, and some number of events are scheduled. The simulator is then run, and if not told to exit control is returned to the interpreter in interactive mode.
The sim.Entity class has the following members and methods:
An example of a simple simulation might look as follows:
import mysim node1 = sim.Entity("node") node2 = sim.Entity("node") link = sim.Entity("link") node1.config("link",link,node2) node2.config("link",link,node1) link.config("bandwidth",100) link.config("latency",30) d = "hello world" node1.emit([0,1],"data",d,link) sim.stopAt(sim.time() + [0,1000]) sim.run()
We might run this code by executing
python_main
sim.py, we can also run it with python_main sim.py
sim.py as follows: import mysim import sys node1 = sim.Entity("node") node2 = sim.Entity("node") link = sim.Entity("link") node1.config("link",link,node2) node2.config("link",link,node1) link.config("bandwidth",100) link.config("latency",30) d = "hello world" node1.emit([0,1],"data",d,link) sim.stopAt(sim.time() + [0,1000]) sim.run() sys.exit()
python_main can take any number of Python script files on the command line. It can also take arguments of the form
python_main -i <module>
python_main -i mysim -i sys sim.py
In order to import a module, it must be located in Python's search path. python_main adds the current directory (".") to its path automatically, but it might be more convenient to add other directories to the path. There are two ways to do this. The first is to preface your scripts with
sys.path.append('/path/to/your/modules')
python_main command-line option -L, which behaves like the C compiler flag in that it adds its argument to the search path for libraries (which is what modules are). The equivalent command-line version of the above would be: python_main -L /path/to/your/modules
python_main does not take any other command line arguments at present. In particular, it cannot pass any of the standard Python flags to the interpreter.
Because the example module provides a detailed discussion of writing a module, we will focus here on using the wrapper.
Nodes are shown with their network addresses, and links with their prefix matches. We want to set up this network and send a DTN::Bundle from 01 to 11. This is done with the following script using the python interpreter.
# Copyright 2008 Michael Marsh, University of Maryland. # # This file is part of pydtn. # # pydtn 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 3 of the License, or # (at your option) any later version. # # pydtn 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 pydtn. If not, see <http:#www.gnu.org/licenses/>. # # The views and conclusions contained in the software and documentation # are those of the authors and should not be interpreted as representing # official policies, either expressed or implied, of the University # of Maryland. # # pydtn extends and embeds the Python interpreter, which is # Copyright 2001-2006 Python Software Foundation, All Rights Reserved, # and is released under the PSF License Agreement. # # RANLUX random number generation uses the Boost library, # Copyright 1994-2006 by various authors (details in individual files), # which is released under the Boost Software License, Version 1.0. import pydtn import pydtn.namtrace import pydtn.network import pydtn.flowtrace import pydtn.storetrace import pydtn.storeprofile import pydtn.sampleapp pydtn.namtrace.setup("example.nam") pydtn.flowtrace.setup("example.flow") pydtn.storetrace.setup("example.stor") pydtn.storeprofile.setup("example.storprof") pydtn.config("bundle_lifetime",[0,9000]); pydtn.stats( "stats.out", 1000 ) pydtn.network.algorithm("forwarding:routed:explicit") # this is the default # Create three nodes. node1 = pydtn.network.addNode("\0\1",capacity=4096,stable=10000) node2 = pydtn.network.addNode("\1\0",capacity=4096,stable=10000) node3 = pydtn.network.addNode("\1\1",capacity=4096) node2.config("custody","spaceavail") node2.config("resend",[0,5000]) # Create links 1 <--> 2 <--> 3 link12 = pydtn.network.addLink( node1, node2, latency=0.003, bandwidth=100 ) link21 = pydtn.network.addLink( node2, node1, latency=0.003, bandwidth=100 ) link23 = pydtn.network.addLink( node2, node3, latency=0.003, bandwidth=100 ) link32 = pydtn.network.addLink( node3, node2, latency=0.003, bandwidth=100 ) # Set up routing. pydtn.network.route() pydtn.network.applyToNodes(pydtn.storeprofile.collect) pydtn.network.applyToNodes(pydtn.sampleapp.attach) # Schedule a data event. node1.emit([0,1],"data",node3,"hello world") node1.emit([0,2],"data",node3,"hello world") node1.emit([0,3],"data",node3,"hello world") # Send a message from an application. pydtn.sampleapp.send(node1,node3,"app data",[0,50]) for i in range(1000): node1.emit([0,100],"data",node3,"hello world") for i in range(20): node1.emit([0,100000],"datafake",node3,10240) def td( now ): t = now t[0] += 0 t[1] += 100000 return t def gen(): node1.emit(sim.time(),"datafake",node3,10) tg = sim.Entity("trafficgen") tg.config("time_dist",td) tg.config("generator",gen) tg.emit([0,5000]) sim.stopAt([0,600000]) # Start the simulation. sim.run() # Stop the interpreter. sys.exit()
This script should be mostly self-explanatory. The empty string in
link.config("prefix","")
Specifies that this link should be added to the routing table with a 0-length prefix. That is, if nothing else matches for a destination, this will.
When run, the following output is produced:
0001 adding route for prefix of length 0
received data at 00 01
sending data from 00 01
looking for a route from 0001 to 0101
received data at 01 01
consuming data at 01 01
11 bytes
68 65 6C 6C 6F 20 77 6F 72 6C 64
You can match up the route configuration and the subsequent path taken by the DTN::Bundle through the network. Output is generated each time a DTN::Node's recv or send method is called, and again when the DTN::Bundle is finally "consumed" at its destination.
Once again, we want to send a DTN::Bundle from 01 to 11, but this time there is another node, 10, between them. Note that 10 has two routes, one for addresses beginning with 0 and one for addresses beginning with 1.
The script that sets up this network and sends the data is:
# Copyright 2008 Michael Marsh, University of Maryland. # # This file is part of pydtn. # # pydtn 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 3 of the License, or # (at your option) any later version. # # pydtn 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 pydtn. If not, see <http:#www.gnu.org/licenses/>. # # The views and conclusions contained in the software and documentation # are those of the authors and should not be interpreted as representing # official policies, either expressed or implied, of the University # of Maryland. # # pydtn extends and embeds the Python interpreter, which is # Copyright 2001-2006 Python Software Foundation, All Rights Reserved, # and is released under the PSF License Agreement. # # RANLUX random number generation uses the Boost library, # Copyright 1994-2006 by various authors (details in individual files), # which is released under the Boost Software License, Version 1.0. import pydtn import pydtn.network pydtn.network.algorithm("forwarding:routed:explicit") # this is the default # Create three nodes. node1 = pydtn.network.addNode("\0\1") node2 = pydtn.network.addNode("\1\0") node3 = pydtn.network.addNode("\1\1") # Create links 1 <--> 2 <--> 3 link12 = pydtn.network.addLink( node1, node2, latency=3000, bandwidth=100 ) link21 = pydtn.network.addLink( node2, node1, latency=3000, bandwidth=100 ) link23 = pydtn.network.addLink( node2, node3, latency=3000, bandwidth=100 ) link32 = pydtn.network.addLink( node3, node2, latency=3000, bandwidth=100 ) # Set up routing. pydtn.network.route() # Schedule a data event. node1.emit([0,1],"data",node3,"hello world") # Start the simulation. sim.run() # Stop the interpreter. sys.exit()
Aside from the increased number of Entitys, there are two things to note in this example. The first is that we are now specifying non-empty prefixes for the links link21 and link23. In particular, 10 has no default route. The other thing to note is that 01 is now specifying a destination to which it has no direct link.
The output for this script is:
0001 adding route for prefix of length 0
0100 adding route for prefix of length 1
0100 adding route for prefix of length 1
0101 adding route for prefix of length 0
received data at 00 01
sending data from 00 01
looking for a route from 0001 to 0101
received data at 01 00
sending data from 01 00
looking for a route from 0100 to 0101
received data at 01 01
consuming data at 01 01
11 bytes
68 65 6C 6C 6F 20 77 6F 72 6C 64
From this, we can see how the data has travelled through the network from 01 to 10 to 11.
1.5.4