Overview
OverSwarm is a framework that aims at easing the development and evaluation of swarm-based peer-to-peer protocols. It is intended to run alongside with OverSim, so that swarm protocols can be compared existing “traditional” protocols, extend existing algorithms, or run on top of an existing peer-to-peer overlay.
The idea behind OverSwarm is to separate the implementation of swarm agents' behaviors from that of nodes. Moreover, agents' behaviors are written using a lisp-like language that enables faster prototyping and supports strong, transparent migration (i.e. agents can migrate to another node at any point during their execution).
Toolchain
The following figure depicts the OverSwarm toolchain:
The agents' behavior description is compiled into three header files by means of the OverSwarm compiler. These headers files contain the C++ methods that enable agent execution, as well as the required prototypes and header file declarations. All source files are then compiled using the standard C++ compiler (typically gcc), in order to produce a working OverSim/OMNet++ module.
A simple module: the main class
Let's see how a simple module can be implemented using OverSwarm… we consider the overswarmtemplate.zip example (also used in the installation tutorial).
The main module is described in the *OverSwarmTemplate.h* header.
The first lines include the necessary header files:
#ifndef __OVERSWARMTEMPLATE_H_ #define __OVERSWARMTEMPLATE_H_ #include <OverSwarm.h> #include <BaseOverSwarmOverlay.h>
The OverSwarm.h file is required for the declaration of OverSwarm's specific types, whereas BaseOverSwarmOverlay.h represent a minimal class for the implementation of OverSwarm modules.
Then an autogenerated header file is include:
/* Overswarm generated headers go here*/ #include "OverSwarmTemplate_Headers.h"
This provides the necessary headers files, as required by the agent behavior. Then comes the class declaration:
/* Additional headers go here */ #include "OvSwTemplateSampleLib.h" namespace oversim { class OverSwarmTemplate : public overswarm::BaseOverSwarmOverlay { public: OverSwarmTemplate(); virtual ~OverSwarmTemplate(); virtual void initializeOverlay(int stage); virtual void handleTimerEvent(cMessage* msg); friend class OvSwTemplateSampleLib; protected: // TODO
The second autogenerated header file is included in the private section of the class:
private: /* Method prototypes go here */ #include "OverSwarmTemplate_MethodPrototypes.h"
Finally, the private fields are also declared
/* Other private fields and methods go here */ OvSwTemplateSampleLib* utils; cMessage *internalTimer; bool isMaster; }; } #endif
The main module file, *OverSwarmTemplate.cc* contains the implementation of the basic functionalities of the module:
#include <BootstrapList.h> #include <UnderlayConfigurator.h> #include <OverSwarm.h> #include <BaseOverSwarmOverlay.h> #include "OverSwarmTemplate.h" namespace oversim { Define_Module(OverSwarmTemplate);
Within the file, include the OverSwarm agents' behavior implementation:
/* Implementation goes there */ #include "OverSwarmTemplate_Implementation.h"
What follows is a “standard” OverSim/OMNet++ module. The constructor just initializes some fields:
OverSwarmTemplate::OverSwarmTemplate() { this->utils = NULL; this->internalTimer = NULL; this->isMaster = false; }
The initializeOverlay method, derived from BaseOverlay (provided by OverSim) is called at different stages during the initialization of the overlay, each time with an increasing value for the parameter “stage”. Here we are interested in the MIN_STAGE_OVERLAY stage, which represents the earliest possibility to initialize overlay stuff:
void OverSwarmTemplate::initializeOverlay(int stage) { if (stage != MIN_STAGE_OVERLAY) return;
For practical reasons we save a pointer to the “utils” submodule:
/* Setup submodules pointers */ utils = check_and_cast<OvSwTemplateSampleLib*> (getParentModule()->getSubmodule("utils"));
Subsequently we setup a periodic timer that will send a “self” message every 2 seconds. We schedule the first message to be sent at current time (simTime()) plus 2 seconds:
/* Periodic events */ internalTimer = new cMessage("Event timer"); scheduleAt(simTime() + 2, internalTimer);
Finally we initialize the utils module, set the current node as Master if it's the first one to be created (in that case the bootstrap node list will be empty), and flag the overlay node as ready:
utils->initialize(stage); if (bootstrapList->getBootstrapNode().isUnspecified()) { this->isMaster = true; } /* We are ready */ setOverlayReady(true); }
Upon destruction we must delete the internal timer:
OverSwarmTemplate::~OverSwarmTemplate() { cancelAndDelete(internalTimer); }
When a “self” message is received (a timer) the handleTimerEvent method is called. Here we check the message type and if we are ready, and we are the master node, we execute a SayHello ant. We pass a OvSwString parameter to the ant, that will be bound to the first “virtual” variable. Notice that we must put NULL as the last argument (because the method is a variadic function):
void OverSwarmTemplate::handleTimerEvent(cMessage* msg) { if (msg == internalTimer) { /* Reschedule */ scheduleAt(simTime() + 2, internalTimer); /* Wait until the underlay is ready */ if (underlayConfigurator->isInInitPhase()) return; /* Do what is needed */ if (this->isMaster) { executeSayHello(new OvSwString("Joe"), NULL); } } } }
The SayHello agent
The SayHello agent is defined in the OverSwarmTemplate.osw file. The first statement defines the protocol name, in this case “OverSwarmTemplate”. It is important that this name matches the main class name!
/* Template Algorithm */ protocol "OverSwarmTemplate" {
Next, the list of imported methods is defined:
import for "oversim" { "utils->say" as "say", /* Says something on a node */ "utils->getRandomNodes" as "getRandomNodes", /* Return a list with random nodes */ "utils->nodeId" as "nodeId", /* Return nodeId */ }
Each import line defines a C++ method and the corresponding “lisp” name. For example, when an agent calls the “say” function, the “say” method in the object pointed by “utils” will be called.
The behavior of the SayHello agent is defined within the “behavior” part of the “ant” block:
ant "SayHello" { behavior { (virtual $name) (var steps 0) (define (doSay ... something) (say (concat something))) (while 1 (begin (doSay "Hello world, my name is " $name " and I've traveled " steps " hops: now I'm on " (nodeId)) (if (>= steps 10) (end)) (var nextStep (choose (getRandomNodes))) (set! steps (+ steps 1)) (migrate nextStep))) } } }
Here we see how the “virtual” statement is used, where the value passed to “executeSayHello” is associated to the variable $name. The function doSay employs a variable argument list: everything that is passed as argument is added to the list “something”.
Utils: functions accessible by the agent
In OvSwSampleLib.cc we define the methods that are exported as functions to the agent:
OvSwValue::Ptr OvSwTemplateSampleLib::say(void* owner, OvSwStack* st, OvSwValue::Ptr msg) { cout << *msg << endl; return OVSWFALSE; }
Each call to “say” from the agent will result in a call to this method. The parameter passed to the function within the agent is available in the msg parameter. Please note that each function MUST return a value. In this case we return OVSWFALSE, which is the boolean false equivalent for our agent framework.
It is also possible to use the antFunction
macro:
osvPtr antFunction(OvSwTemplateSampleLib::say, osvPtr msg) { cout << *msg << endl; return OVSWFALSE; }
osvPtr is an “easy to write” macro for OvSwValue::Ptr. The antFunction macro can also be used to “clean up” the header file“:
osvPtr antFunction(say, osvPtr msg);