close Warning: BrowserModule failed with ConfigurationError: Look in the Trac log for more information.

Changes between Version 18 and Version 19 of OverSimDevelop


Ignore:
Timestamp:
Apr 28, 2009, 12:35:31 PM (16 years ago)
Author:
heep
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • OverSimDevelop

    v18 v19  
    3535For module declarations, !OverSim uses the NED language, a topology description language. Modules should be declared in their own NED files (with extension .ned), and the file name should match the module name (minus the extension!).
    3636
    37 First, we start with our overlay module. All overlay module files should be located in <OverSim>/src/overlay, so we'll create a folder called myoverlay, and inside, we'll create a file called MyOverlay.ned.
    38 
    39 Following is the declaration for MyOverlay:
     37First, we start with our overlay module. All overlay module files should be located in !OverSim/src/overlay, so we'll create a folder called myoverlay, and inside, we'll create a file called !MyOverlay.ned.
     38
     39Following is the declaration for !MyOverlay:
    4040
    4141{{{
     
    5858The parameters subsection is a list of module properties. These parameters must be set before the simulation (or the simulator will stop and ask you for a value each time a module is created!) and will be read-only while the simulation is running. As parameter types we can use booleans (bool), strings, integers, or doubles; custom types (either module declarations or C++ types) can also be used.
    5959
    60 Notice that, since we're writing an overlay module, we'll declare our module as inheriting from the parent class BaseOverlay. Therefore we'll use "extends BaseOverlay" in the module definition. Since we want to use our own code for our module, we'll add "@class(MyOverlay)" in the parameters section, to tell the simulator that MyOverlay is a class of its own.
    61 
    62 Now, we'll declare our application module in a similar way. Application files should be located in <OverSim>/src/applications, so we'll create a folder called myapplication (folder names are in lower case by convention) and there a file called MyApplication.ned.
     60Notice that, since we're writing an overlay module, we'll declare our module as inheriting from the parent class BaseOverlay. Therefore we'll use "extends BaseOverlay" in the module definition. Since we want to use our own code for our module, we'll add "@class(!MyOverlay)" in the parameters section, to tell the simulator that !MyOverlay is a class of its own.
     61
     62Now, we'll declare our application module in a similar way. Application files should be located in !OverSim/src/applications, so we'll create a folder called !myapplication (folder names are in lower case by convention) and there a file called !MyApplication.ned.
    6363
    6464{{{
     
    8585=== 2.1.1 Simple and Compound Modules ===
    8686
    87 MyOverlay and MyApplication are examples of what is called a 'simple' module, because they don't contain any other modules inside them. Simple modules are declared using the keyword "simple" (for example, "simple MyOverlay"). Only simple modules can have its behaviour customized with C++ (see Section 4).
     87!MyOverlay and !MyApplication are examples of what is called a 'simple' module, because they don't contain any other modules inside them. Simple modules are declared using the keyword "simple" (for example, "simple !MyOverlay"). Only simple modules can have its behaviour customized with C++ (see Section 4).
    8888
    8989On the other hand, modules with inner nested modules are called compound modules, and act only as containers. They are declared with the keyword "module". These are useful when we want to organize all the modules related to your overlay in a single place.
     
    109109
    110110    connections allowunconnected:
    111         udpIn --> myOverlay.udpIn++;
    112         udpOut <-- myOverlay.udpOut++;
     111        udpIn --> myOverlay.udpIn;
     112        udpOut <-- myOverlay.udpOut;
    113113        appIn --> myOverlay.appIn;
    114114        appOut <-- myOverlay.appOut;
     
    116116}}}
    117117
    118 As you can see, the inner module "myOverlay" of type MyOverlay is nested inside MyOverlayModules. However, when we use MyOverlayModules, all messages that need to be sent to myOverlay will be sent to the wrapper instead. Therefore, we need to set up which gates we're going to use (input and output to the overlay and the UDP module), and then connect them to the overlay gates. There is no need to change the default entries in the sections "gates" and "connections" for other overlays (but keep the order! gates - submodules - connections).
     118As you can see, the inner module "myOverlay" of type !MyOverlay is nested inside !MyOverlayModules. However, when we use !MyOverlayModules, all messages that need to be sent to myOverlay will be sent to the wrapper instead. Therefore, we need to set up which gates we're going to use (input and output to the overlay and the UDP module), and then connect them to the overlay gates. There is no need to change the default entries in the sections "gates" and "connections" for other overlays (but keep the order! gates - submodules - connections).
    119119
    120120Notice that we used "like" instead of "extends". This is because IOverlay is simply an interface, not a parent class; we don't want to inherit from it. Additionaly, no "@class" is needed, since compound modules can't be extended with C++ code.
     
    153153== 3. Messages ==
    154154
    155 For sending messages, we can use the basic cMessage class. However, we'll be needing a few custom fields for our application, so we'll use a customized message instead. Custom message declarations are like those of modules, but without sections: simply include the parameters directly. The framework will automatically create getter- and setter-functions for each of them.
     155For sending messages, we can use the basic cPacket class (which inherits from the generic cMessage class). However, we'll be needing a few custom fields for our application. Thus, we'll use a customized message instead. Custom message declarations are like those of modules, but without sections: simply include the parameters directly. The framework will automatically create getter- and setter-functions for each of them.
    156156
    157157And like modules, messages should be declared in their own (.msg) files.
    158158
    159 In this case, let's see MyMessage.msg:
     159In this case, let's see !MyMessage.msg:
    160160
    161161{{{
     
    186186In order to route packets in a realistic way, the "length" parameter of the message should be set to an appropiate value. This is done manually by using the setBitLength(bits) or setByteLength(bytes) function when sending the message. Forgetting to set this value will default the length to a size of 0, potentially making measurements such as latencies less meaningful.
    187187
    188 All messages can encapsulate any other message (just avoid recursivity!) by using the encapsulate() function. To decapsulate the message, use decapsulate(). To peek the encapsulated message without decapsulating it, use getEncapsulatedMsg(). The length of the message is automatically changed accordingly.
    189 
    190 Additionaly, each message that is created must be deleted exactly once. Messages that aren't deleted will cause memory leaks, and those that are deleted twice can cause a segmentation fault. It is the responsability of the programmer to know when a message should be deleted.
     188All messages can encapsulate any other message (just avoid recursivity!) by using the encapsulate() function. To decapsulate the message, use decapsulate(). To peek the encapsulated message without decapsulating it, use getEncapsulatedMsg(). The length of the message is automatically updated.
     189
     190Additionaly, each message that is created must be deleted exactly once. Messages that aren't deleted will cause memory leaks, and those that are deleted twice can cause a segmentation fault. It is the responsibility of the programmer to know when a message should be deleted.
    191191
    192192== 4. Implementing the overlay and application modules ==
     
    210210IPvXAddress:        Generic IP address, can contain either an IPv4 address (IPAddress) or an IPv6 address (IPv6Address).[[BR]]
    211211TransportAddress:   A structure containing bot an IPvXAddress (field "ip"), and a port (field "port").[[BR]]
    212 NodeHandle:         A child class of TransportAddress, additionaly contains an OverlayKey (field "key"). In overlay modules, the NodeHandle value representing the node in which the module is can be retrieved with "thisNode". In application modules, thisNode also exists, but the "key" field may not be set![[BR]]
     212NodeHandle:         A child class of TransportAddress, additionaly contains an OverlayKey (field "key"). In overlay and application modules, the variable "thisNode" contains the NodeHandle value representing the host. However, in application modules the "key" field may not be set.[[BR]]
    213213
    214214Now, let's continue with the implementation.
     
    216216== 4.1 Implementing the Application ==
    217217
    218 First, we'll start with MyApplication, since the application interface is easier to understand. Our application will implement a simple PING-type program, in which a packet is sent to another node, and that node sends the packet back. When we start, we'll wait <sendPeriod> seconds, then we'll route <numToSend> packets to random keys through the overlay. When a node receives a packet, it will send it back to the owner through UDP. The process is repeated until the simulation ends.
    219 
    220 This is the declaration of MyApplication from MyApplication.h:
     218First, we'll start with !MyApplication, since the application interface is easier to understand. Our application will implement a simple PING-type program, in which a packet is sent to another node, and that node sends the packet back. When we start, we'll wait <sendPeriod> seconds, then we'll route <numToSend> packets to random keys through the overlay. When a node receives a packet, it will send it back to the owner through UDP. The process is repeated until the simulation ends.
     219
     220If you use UDP and expect to receive packets, notice that you first must bind a port for your application to use. This can be done with bindToPort(port), and you can bind as many ports as you like. Note however that, when receiving a packet, the port in which the packet came won't be passed up to the application. Don't use port numbers 1024-1034, as they are reserved for overlay use. The overlay module reserves its ports automatically.
     221
     222This is the declaration of !MyApplication from !MyApplication.h:
    221223
    222224{{{
     
    245247}}}
    246248
    247 Now comes the implementation of MyApplication from MyApplication.cc:
     249Now comes the implementation of !MyApplication from !MyApplication.cc:
    248250
    249251{{{
     
    284286    timerMsg = new cMessage("MyApplication Timer");
    285287    scheduleAt(simTime() + sendPeriod, timerMsg);
     288
     289    // bind our port to receive UDP packets
     290    // the value for thisNode.port will be updated accordingly
     291    bindToPort(2048);
    286292}
    287293
     
    310316{
    311317    if (msg == timerMsg) {    // is this our timer?
    312 
    313318        scheduleAt(simTime() + sendPeriod, timerMsg); // reschedule our message
    314319
     
    330335        }
    331336    } else {
    332         delete msg; // who knows what this packet is?
     337        delete msg; // unknown packet
    333338    }
    334339}
     
    340345{
    341346    // we are only expecting messages of type MyMessage, throw away any other
    342 
    343347    MyMessage *myMsg = dynamic_cast<MyMessage*>(msg);
    344348    if (myMsg == 0) {
     
    348352
    349353    // are we a PING? send a PONG!
    350 
    351354    if (myMsg->getType() == MYMSG_PING) {
    352 
    353355       myMsg->setType(MYMSG_PONG);                         // change type
    354356       sendMessageToUDP(myMsg->getSenderAddress(), myMsg); // send it back to its owner
     
    359361
    360362// handleUDPMessage is called when we receive a message from UDP.
     363// Parameter msg is actually of type cPacket*, set to the more generic cMessage* for legacy purposes.
    361364// Unhandled or unknown packets can be safely deleted here.
    362365
     
    364367{
    365368    // we are only expecting messages of type MyMessage
    366 
    367369    MyMessage *myMsg = dynamic_cast<MyMessage*>(msg);
    368370
     
    372374
    373375    // Whatever msg was, we won't need it anymore.
    374 
    375376    delete msg;
    376377}
     
    382383Now, we'll implement our overlay module. Our overlay will be rather simple, and based on only two concepts. First, an overlay key maps directly to the IP address of its holder (for example, key 16 will be held by 1.0.0.16). And second, routing a packet from key A to key B will consist of sending that packet to all nodes with keys between A and B (for example, sending a node from key 4 to key 1 will create a route of 4 - 3 - 2 - 1).
    383384
    384 This is the declaration of MyOverlay in MyOverlay.h
     385This is the declaration of !MyOverlay in !MyOverlay.h
    385386
    386387{{{
     
    407408    // obligatory: called when we need the next hop to route a packet to the given key
    408409    NodeVector* findNode(const OverlayKey& key,             // key to route to
    409                          int numRedundantNodes,             // how many candidates for next hop we want (see getMaxNumSiblings)
    410                          int numSiblings,                   // how many siblings we'll return (?) (see getMaxRedundantNodes)
    411                          BaseOverlayMessage* msg);          // message being routed
    412 
    413     // obligatory: In general, called when we need to know whether node is amongst numSiblings closest nodes to key.
    414     // But normally it is called with node set to this node, and asking whether this node is responsible for key.
     410                         int numRedundantNodes,     // next hop candidates to return if we're not responsible for "key"
     411                         int numSiblings,           // how many siblings to return if we're responsible for "key"
     412                         BaseOverlayMessage* msg);  // message being routed
     413
     414    // obligatory: In general, called when we need to know whether "node" is amongst numSiblings closest nodes to "key".
     415    // But normally it is called with "node" set to "thisNode", and asking whether we are responsible for "key"
    415416    bool isSiblingFor(const NodeHandle& node,               // which node (usually thisNode) we're referring to
    416417                      const OverlayKey& key,                // key in question
     
    418419                      bool* err);                           // set to false when we couldn't determine the range
    419420
    420     // obligatory: Set the maximum number of siblings that can be queried about (usually 1)
     421    // obligatory: Set the maximum number of siblings that can be queried about in isSiblingFor(usually 1)
    421422    int getMaxNumSiblings();
    422423
    423     // obligatory: Set the maximum number of redundant nodes that can be queried about (usually 1)
     424    // obligatory: Set the maximum number of redundant nodes that can be queried about in isSiblingFor (usually 1)
    424425    int getMaxNumRedundantNodes();
    425426
     
    427428}}}
    428429
    429 Now comes the implementation of MyOverlay from MyOverlay.cc:
     430Now comes the implementation of !MyOverlay from !MyOverlay.cc:
    430431
    431432{{{
     
    502503    }
    503504
    504     nextHops = new NodeVector(1);              // else, set the response vector with one node
     505    // numRedundantNodes and numSiblings are both maximal 1   
     506    nextHops = new NodeVector(1);             
    505507
    506508    if (key == thisNode.key) {                 // are we responsible? next step is this node
     
    550552}}}
    551553
    552 In this example, we are working with the network SimpleOverlay. From there, we need node number 5, and then its overlay module. For that module, we'll use the submodule myOverlay, and set the parameter enableDrops to true.
     554In this example, we are working with the network SimpleOverlay. From there, we retrieve the overlayTerminal array, get index 5, and then its overlay module. For that module, we'll use the submodule myOverlay, and set the parameter enableDrops to true.
    553555
    554556This case, however, is too specific. We may not always work with the SimpleOverlay network. Or we may need that parameter to be set for all nodes. For those cases the wildcards * and ** are of use. For example:
     
    563565
    564566Should a module parameter not be set in either configuration file, or match any wildcard, !OverSim will prompt the user to enter a value for each instance of the module. For simulations with a big amount of nodes, setting each parameter individually quickly becomes overwhelming. Therefore, it is recommended that every module parameter be assigned a default value in default.ini.
     567
     568Notice that these are parameter paths, not to be confused with module paths from NED declarations (see Section 2.1). In this case, the parameter paths describe a set of modules and submodules to follow to reach the inner parameter we want. On the other hand, the module paths describe the folder under <OverSim>/src/ in which the declaration of the NED module will be found.
    565569
    566570== 5.2 Creating our Configuration File ==
     
    595599The churn type tells OverSim how often it should add or delete nodes from the network. There are many churn types (e.g. NoChurn for a static network, LifetimeChurn for creating nodes with a given lifetime distribution, and so on); for a complete list see the ned files in <OverSim>/src/common. In our example, we'll use the less interesting NoChurn, which disables churn in our network. There are plenty of examples in <OverSim>/simulations/omnetpp.ini for all churns.
    596600
    597 The second part are the node parameters. Two node parameters are obligatory: tier1Type (to specify the application module), and overlayType (for the overlay module). Further parameters can be specified for these application and overlay modules. For this example, we'll use the module MyOverlayModules, which we declared in a previous chapter.
     601The second part are the node parameters. Two node parameters are obligatory: tier1Type (to specify the application module), and overlayType (for the overlay module). Further parameters can be specified for these application and overlay modules. For this example, we'll use the module !MyOverlayModules, which we declared in a previous chapter.
    598602
    599603{{{
     
    696700== 7. Remote Procedure Calls ==
    697701
    698 Remote Procedure Calls (RPCs) are a useful paradigm for when an application wants to execute a function, but where the context and / or the code are in another host, yet in a transparent way. Continuing our MyOverlay example, let's create an RPC function that retrieves the neighbors from another node.
    699 
    700 The MyOverlay header would look this way:
     702Remote Procedure Calls (RPCs) are a useful paradigm for when an application wants to execute a function, but where the context and / or the code are in another host, yet in a transparent way. Continuing our !MyOverlay example, let's create an RPC function that retrieves the neighbors from another node.
     703
     704The !MyOverlay header would look this way:
    701705
    702706{{{
     
    869873
    870874== Have fun! ==
    871