Академический Документы
Профессиональный Документы
Культура Документы
This page is for those who are somewhat familiar with network simulators, and would like to find out in a few minutes what OMNeT++ is about. It is probably a good idea to keep the User Manual and the API open in other browser tabs, so that you can search for more info there when you find something interesting in this text.
How do those simulation models and frameworks mentioned on omnetpp.org relate to OMNeT++?
OMNeT++ provides the basic machinery and tools to write simulations, but itself it does not provide any components specifically for computer network simulations, queueing network simulations, system architecture simulations or any other area. Instead, these application areas are supported by various simulation models and frameworks such as INET/INETMANET, MiXiM or Castalia. These models are developed completely independent of OMNeT++, and follow their own release cycles.
modules. Well-
Modules can be connected with each other via gates (other systems would call them ports), and combined to form compound modules. Connections are created within a single level of module hierarchy: a submodule can be connected with another, or with the containing compound module. Every simulation model is an instance of a compound module type. This level (components and topology) is dealt with in NED files. To give you an idea, a component named EtherMAC would be described in NED like this:
// // Ethernet CSMA/CD MAC // simple EtherMAC { parameters: string address; gates: input phyIn; output phyOut; input llcIn; output llcOut; }
// others omitted for brevity // // // // to to to to physical physical EtherLLC EtherLLC layer or the network layer or the network or higher layer or higher layer
llc.macIn <-- mac.llcOut; llc.macOout --> mac.llcIn; mac.phyIn <-- in; mac.phyOut --> out; }
Comments are useful for generated documentation; see an example here). Simple modules which, like EtherMAC above, don't have further submodules and are backed up with C++ code that provides their active behaviour, are declared with the simple keyword; compound modules are declared with the module keyword. To simulate an Ethernet LAN, you'd create a compound module EtherLAN and announce that it can run by itself with the network keyword:
network EtherLAN { ... (submodules of type EtherStation, etc) ... }
NED files can be edited both graphically and in text mode in the Simulation IDE. NED only defines the model structure (topology), and leaves behaviour and a subset of module parameters open. As mentioned above, behaviour is added via C++ code behind simple modules, and module parameters which are left unassigned in NED files will get their values from ini files -- we'll cover these topics later. The OMNeT++ manual has a chapter about the NED language.
opp_makemake creates a makefile with the appropriate settings, so you don't have to do anything else.
If you have sources in several directories, such as when you use some model framework like the Mobility Framework or the INET Framework, then this simple method of generating the makefile won't work, because the simulation will need to link with code from other directories as well. Then you'll need to pass additional options to opp_makemake such as -I; it is best to check the given framework's documentation or existing makefile system for a hint. To run the executable, you need an omnetpp.ini file. Without it you get the following error:
$ ./etherlan OMNeT++/OMNEST Discrete Event Simulation (C) 1992-2005 Andras Varga <!> Error during startup: Cannot open ini file `omnetpp.ini'
[....]
One function of the ini file is to tell which network to simulate (there might be more than one network definitions in the NED files). You can also specify where to load NED files from, assign module parameters, specify how long the simulation should run, what seeds to use for random number generation, how much results to collect, set up several experiments with different parameter settings, etc. An example omnetpp.ini (hopefully self-explaining):
[General] network = etherLAN *.numStations = 20 **.frameLength = normal(200,1400) **.station[0].numFramesToSend = 5000 **.station[1-5].numFramesToSend = 1000 **.station[*].numFramesToSend = 0
As you can see you can use wildcards when assigning module parameters. Parameter assignments in the NED file take place first, and those left unassigned can be assigned in the ini file. (That is, parameter values in NED cannot be overwritten from the ini file.) If there are still parameters without a value, those ones will be asked interactively at runtime. Why do we have separate NED and ini files, why isn't everything contained in a single model file? Simulation is about creating a model, experimenting with it, and drawing conclusions. In OMNeT++, C++ and the NED files represent the model, and experiments are described in ini files: parameter values, results to collect, seeds etc. Ini files let you keep the model unchanged while you're exploring the parameter space. (Note that parameters can also affect the topology, e.g. can denote the number of nodes in the network). To load an ini file with a different name, pass the file name as command-line argument. More than one ini file can also be loaded, with the effect of their contents getting merged.
$ ./etherlan common-settings.ini params15.ini seeds3.ini
Ini files also support inclusion, which is useful if you have common settings to factor out. The manual describes the ini file facility in detail, including a complete list of ini file options supported. By default, the simulation executable builds with the graphical user interface, Tkenv. To run the simulation under the command-line (batch) user interface Cmdenv, specify the -u Cmdenv option:
$ ./etherlan -u Cmdenv
It is also very easy to link the simulation with only Tkenv or Cmdenv. Since it's this easy to switch to a different UI, you could create your own GUI as well if you wanted. This is easier than you think -reading src/cmdenv/cmdenv.cc would give you an idea how to do it. Embedding OMNeT++ into another application (e.g. some design or analysis tool that employs simulation) would go in a very similar way, and it has already been done by some commercial companies using OMNeT++/OMNEST. How to use the Tkenv GUI? Here we don't go into details -- please explore it by clicking and right-clicking everywhere, looking into the menus, etc. There are also resources in the Wiki's Omnetpp4 section.
modelling; you can configure this RNG mapping using wildcards in omnetpp.ini. Seeding can be automatic or manual; manual seeds also come from the ini file. Several distributions are supported (in the 3.2 version 14 continuous and 6 discrete distributions, see the API doc), and they are available from both NED and C++. Non-const module parameters can be assigned random variates like exponential(0.2), which means that the C++ code will get a different number each time it reads the parameter; this is a very convenient way of specifying parameters for random traffic sources. (const parameters can also be assigned expressions like exponential(0.2), but there it will be evaluated only once and then stuck with it).
Can I do MRIP, parallel distributed simulation, network emulation, or feature X with OMNeT++?
Yes. OMNeT++ is very extensible, plus you have all of the source code, so the sky is the limit. Several features are supported out of the box, and it is easier and cleaner to implement others than you think. Ask for guidance on the mailing list if you have such plans. MRIP stands for multiple replications in parallel, and Akaroa is an excellent tools for that. You'll need to download and install it separately, then configure and recompile OMNeT++ with Akaroa support enabled (see configure.user). AFAIK Akaroa is available on Linux (*nix) only. Search for Akaroa in the OMNeT++ manual to learn more. Very large simulations may benefit from the parallel distributed simulation (PDES) feature, either by getting speedup, or distributing memory requirements. If your simulation requires a few Gigabytes of memory, distributing it over a cluster may be the only way to run it. For getting speedup (and not actually slowdown, which is also easily possible), the hardware or cluster should have low latency and the model should have inherent parallelism. Partitioning and other configuration can be configured in omnetpp.ini, the simulation model itself doesn't need to be changed (unless, of course, it contains global variables and the like that prevents distributed execution in the first place.) The communication layer is MPI, but it's actually configurable, so if you don't have MPI you can still run some basic tests over named pipes, or (really for testing or debugging!) file-based message exchange. Or, if you care, you can add new ones by implementing the abstract cParsimCommunications interface. Network emulation, together with real-time simulation and hardware-in-the-loop like functionality, is available because the event scheduler in the simulation kernel is pluggable. The OMNeT++ distribution contains a demo of real-time simulation and a simplistic example of network emulation, enough to give you hints if you're into this area. Real network emulation with the INET Framework is in the queue. Interfacing OMNeT++ with other simulators (hybrid operation) or HLA is also largely a matter of implementing one's own scheduler class. Once you get the idea you'll find that it's much easier to do than you would have thought. It is possible to replace omnetpp.ini with a database as the source of configuration data, and to redirect results (output vectors and scalars) into a database. The standard OMNeT++ distribution also contains demo of that; or you can check out how others have done it. And again, since you have the full source code, and it's structured and documented, you can implement pretty much all your ideas. If you contact me on the mailing list or directly, I'll be happy to give you some initial directions.
without having connections set up to them in the NED file; this can be done with sendDirect(cMessage *msg,double delay, cModule *targetModule, const char *inGateName). Self-messages can be sent (that is, timers can be scheduled) viascheduleAt(simtime_t time, cMessage *msg), and before they expire they can be cancelled via cancelEvent(cMessage *msg). Like many others, these functions are all members of the cSimpleModule class; look it up in the API-doc to see further variants and other functions. The basic cMessage class contains several data members, the practically most important ones being name, length, message "kind" (an int member). Other data members store information about the most recent sending/scheduling of the message: arrival time, arrival gate, etc. To facilitate protocol simulations, cMessage can encapsulate one other cMessage object, see its encapsulate(cMessage *msg), decapsulate() methods; these methods modify the length field accordingly as well. If you need to carry more data in the message, other data members can be added via subclassing. However, you don't need to write the new C++ class by hand, it is more convenient to define it in a .msg file instead, and let OMNeT++ (the opp_msgc tool) generate the C++ class for you. Generated C++ files will have the _m.h, _m.cc suffix. .msg files support further inheritance, composition, array members etc, and have syntax that allows you to customize the class in C++ as well. An example message file:
message NetworkPacket { fields: int srcAddr; int destAddr; }
OMNeT++ is often used to simulate network protocol stacks, and cMessage has a field called control info that carries auxiliary information to facilitate communication between protocol layers. For example, when the application layer sends data (a message object) to TCP for transmission, it can attach a piece of control info (an object) to the message which contains the socket identifier. Or, when TCP sends a TCP segment down to IP for transmission, it attaches control info to it that carries the destination IP address, and possibly other options like TTL. Control info is sent in the other direction (upwards) as well, to indicate source IP address or TCP connection to upper layers. Control info is handled via cMessage'ssetControlInfo(cPolymorpic *ctrl) and removeControlInfo() methods. Other cSimpleModule virtual member fuctions you may need to redefine are initialize() (you'll want to do most initialization here, since during the constructor call the model is still being built), initialize(int stage) and int numInitStages() const for multi-stage initialization (that's useful if one module's initialization code depends on another module already having been initialized), and finish() for recording summary results (the destructor is not suitable for that). If you want the module to take notice of runtime changes in its parameters (parameters can be changed interactively from the GUI, or programmatically by another module -- and you may want the module to re-read the parameters then), redefine the handleParameterChange(const char *paramName) method. A simple module's NED parameters can be read using the par(const char *paramName) method; typically you'll want to do this in initialize(), and store the values in data members of the module class. par() returns a reference to a cPar object which you can cast to the appropriate type (long, double, const char *, etc) with the C/C++ cast syntax or by calling the object's doubleValue() etc. methods. In addition to basic types such as long, bool, double and string, there is XML as well: parameters can be assigned small (or not so small) XML documents or document fragments, which are presented to the C++ code as a DOM-like object tree. Message exchange is not always the most suitable way of interaction between modules. For example, if you have a designated module for statistics collection (very much preferred over global variables!!!), then instead of sending the statistics updates to it via messages, it is more convenient to regard the statistics module as a C++ object and call a public member function of it created for this purpose (e.g. updateStatistics(...)). Direct method calls have a few tricky details. First you have to find the other module: the parentModule(), submodule(const char *name) methods of cModule let you navigate relative to the current module, and simulation.moduleByPath(const char *path) lets you find a module globally, by an absolute path name. Once you have the pointer of the other module, you need to cast it to the actual type (i.e. to StatisticsCollector* from cModule*); this is done by check_and_cast which has the same syntax as C++'s dynamic_cast but throws an error if the cast unsuccessful or the pointer is NULL. The public method should have Enter_Method(...) orEnter_Method_Silent(...) at the top -- this enables animation of the call on the GUI, and also does something slightly obscure called temporary context switch (we don't want to go into details here, but it's especially needed if you do message handling in the method). The INET Framework extensively uses method calls to access modules likeRoutingTable, InterfaceTable, NotificationBoard, etc.
INET's NotificationBoard is actually a module that can be useful in other nontrivial simulations as well: it supports sharing information among several modules by decoupling producers and consumers of information, by relaying change notifications or event notifications among them. (NotificationBoard is loosely based on the blackboard idea, but in practice it is a lot easier, simpler and more efficient to just relay notifications than storing published information on the blackboard; moreover, notifications may contain a copy of the actual data or a pointer to them.) For debugging, it is essential that modules print some info about what they're doing. Instead of printf() and cout<<, in OMNeT++ you'd use ev<< -- that works much the same way, but the output goes to GUI windows where it can be filtered etc. It also helps to reduce debugging time when modules display their status in the icon color or in text labels or tooltips; this can be achieved by changing the display string during execution (see cModule's displayString() method for a start). By calling thebubble( const char *text) method, you can get a transient "bubble" or "balloon" displayed above the module, which can also be helpful. Another debugging aid is theWATCH(variableName) macro and variants (WATCH_VECTOR, etc), which let you inspect the value of a variable on the Tkenv GUI (you'll find watched variables in the "Contents" tab of the module's inspector). To record output vectors, you'd add a cOutVector object to the class as a data member (or create it with new), set its name string which becomes the name of the output vector, then keep calling its record(double value) method to record numbers. Output scalars are best written from the finish() function of the module (see therecordScalar(const char *name, double value) method of cModule), based on counters etc you keep in the module class as data members. It is also possible to calculate basic statistics and histograms; check the cStdDev, cDoubleHistogram and cLongHistogram classes. NED files describe static topology, but it is also possible to create modules and connections dynamically. This can be useful when you have the network topology in some other form than a NED file (plain text file, Excel sheet, database etc), or you truly want to create and delete modules dynamically during runtime. In the former case, often it is an easier solution to convert the data into NED by using Awk, Perl, Python, Ruby, Tcl or a series of regex find/replace operations in a text editor; however, if you're deciding for using dynamic module creation from C++, then invoking nedtool on a sample NED file and looking at the generated _n.cc file can be very helpful.