In this section we describe how to create and manage the Cross Layer Message, one of the most innovative advantages provided by MIRACLE framework. With these messages we are able to extablish a communication among modules of the node. They could be a request of some statistics from a layer (``give me the channel condition'') or a request to perform a particular action in another layer (``turn off radio!'' or ``Start handover!'').
In our framework we are able to manage two kind of messages. These are Synchronous and Asynchronous messages. Both of them are created from the ClMessage
class but they differ in the way they are propagated within the node. Asynchronous messages are very similar to ordinary packet: a copy of the message is sent to each of the target module/plugIn. Syncronous messages are a bit different because the -same- message is sent to all target modules. This means that the message is temporarily shared with the sender and the receivers and therefore the instructions flow directly returns to the source when all the destinations have finished.
The two message types acquire the sinchronous or asynchronous property by the method used to propagate them among the node: you have at disposal the sendAsyncClMsg
(Up/Down) and the sendSyncClMsg
(Up/Down) PlugIn and Module classes methods. The former are more general methods, in fact they have to be used by PlugIn where there is no gerarchy information about the node, while the latter, in the brackets, are those of Module class and involve you choose a direction for your message
The message we are going to describe in more detail is the synchronous message, often used to retreive informations among different layers. Synchronous means that interrogation and reply are in the same message and therefoer the answer is given sinchronously by the receiver(s).
To send a cross layer message we exploit a reference class named ClMessage
. Extending this class we create methods to manage our own messages.
In this example we consider an application layer and we'd like to collect some informations stored at layer 3.
We define the class name of this particular kind of message. We'll call it GetStats()
and it will manage the information retrieval. The sending starategy will be managed by another class.
We also define the name of this particular cross layer message as EXAMPLE_GETSTATS
. By convention, names of packet types and cross layer messages are always written in capital letters and spaces are replaced with underscores (_).
We define a structure where informations will be stored. This structure is named Stats
. It constitutes the payload of the cross layer message.
Going into detail, we expose the code of the ClMessage
constructor, which indicates how a ClMessage
must be formed (from clmessage.h).
/** * Standard ClMessage constructor. * * This constructor allows to set all the relevant * parameters of ClMessage, and therefore can be used to * create all types of messages (unicast, broadcast, layercast). * * @param verbosity define the level of verbosity of the cross- * layer message * @param type define the type of the cross-layer message (this * parameter is given by the simulator during the initialization * phase, i.e., it is the value returned by the <i>addClMessage</i> * method) * @param dtype define the type of the destination (UNICAST or * BROADCAST) * @param value its intepretation is function of the <i>dtype</i> * paramter: * <ul> * <li><i>dtype</i>=UNICAST: * the id of the destination recipient</li> * <li><i>dtype</i>=BROADCAST: * the number of the layer to which the message has to be sent, * or <i>CLBROADCASTADDR</i> if the message is directed * to all the modules of the whole architecture</li> * </ul> * **/ ClMessage(int verbosity, ClMessage_t type, DestinationType dtype, int value);
To form a message we need to properly set each field. Verbosity indicates how ``noisy'' the passage of the message will be across SAPs, then message will be identificated dinamically in each simulation by a type
. In our specific case we call it EXAMPLE_GETSTATS
. This is an important field cause it univocally identifies the new messages during each simulation run and therefore it allows their correct recognition at the receiving modules in order to make the appropriate procedures associated, in our case, the retrieval of particular statistics Stats
. Next field is the destination type, it could be BROADCAST
or UNICAST
and refers to the possibility of sending a message to a specific module or to every module of a layer. This aspect is perfectioned by the last parameter, value
, whose meaning depends by dtype
. If destination is BROADCAST
, this value indicates the layer to which message is addressed, if the destination type is UNICAST
the value denotes the identifier of the module which will receive the message. Be sure to not overwrite messages! In fact, in case you send a unique message in broadcast and there are many modules replying, they will write their answers on the same message!
In clmessage
{.h,.cc} code you can find also another constructor, which semplifies the operations of sending a message to the whole MIRACLE architecture. It it a method that automatically sets the dtype
parameter to BROADCAST
and also it sets the value
parameter to CLBROADCAST
, which allows the message to be received by all modules in all layers. In this constructor only message type and verbosity must be set.
Let's now create our own cross layer message class. We start defining the header file (*.h) for first. We'll call these files getstats{.cc,.h}. We define the struct that will house statistics and then it will characterize the packet.
typedef struct Stats { // MAC level statistics double foo1_; double foo2_; // ... };
Suppose we want to retreive the values of two double variables from one of lower layers. We'll refer to them calling foo1_
and foo2_
.
Now we describe the GetStats
class, defining it as a class that inherits from ClMessage
, as seen before.
class GetStats : public ClMessage { public: GetStats(); GetStats(GetStats* m); GetStats(int verbosity, DestinationType dtype, int value); ClMessage* copy(); // copies the message Stats getStats(); void setStats(Stats s); private: Stats stats_; };
We can see three constructors, a copy()
method and two other methods to manipulate data. At the end we declare a Stats
object which will house statistics. It is private so only this class (or friends) can modify its contents.
Now we'll describe each method, defining how it works.
// define message type ClMessage_t EXAMPLE_GETSTATS = 0; GetStats::GetStats() : ClMessage(STARTCAVERBOSITY, EXAMPLE_GETSTATS) {} GetStats::GetStats(GetStats *m) : ClMessage(m) { } GetStats::GetStats(int verbosity, DestinationType dtype, int value) : ClMessage(verbosity, EXAMPLE_GETSTATS, dtype, value) { } ClMessage *GetStats::copy() { ClMessage *temp = new GetStats(this); ((GetStats*)temp)->stats_ = this->stats_; return (temp); } Stats GetStats::getStats() { return (stats_); } void GetStats::setStats(Stats s) { stats_ = s; }Constructors are responsible of the creation of the message, using the constructor mentioned before. Let's focus on next methods. The
copy()
is fundamental, it is invoked every time your message crosses a SAP as an asynchronous message, so if you don't define this method, your message will not be able to correctly propagate within the node, since the value of the new added attributes are not copied. As previously discussed, when you call the sendAsyncClMsg
methods you send a copy of the same message to the adjacent SAPs.
Following methods are the core of the class and they define how to manipulate datas. In this case they are very simple, getStats
returns the structure with the data and setStats
assigns a particular set of value to the structure.
The last part of code regards the addition of the new cross layer message type to the list of known (and interpretable) cross layer messages. We previously declared EXAMPLE_GETSTATS
as a ClMessage_t
(in .cc file). To complete the process we have to add (usually) in the initlib.cc file the following code:
#include <tclcl.h> #include <getstats.h> extern EmbeddedTcl tclSupp; extern ClMessage_t EXAMPLE_GETSTATS; extern "C" int Miracle_Init() { /* Put here all the commands which must be execute when the library is loaded (i.e. TCL script execution) Remember to ruturn 0 if all is OK, otherwise return 1 */ EXAMPLE_GETSTATS = ClMessage::addClMessage(); tclSupp.load(); return 0; }where we call the function
addClMessage
to add the new message type.
Let's now show how it is possible to send synchronous messages over the node. On the sender side (the one who requests info) we have:
//message forming, note that is a GetStats message GetStats* m = new GetStats(DEFAULT_VERBOSITY, BROADCAST, 3); //Sending message and synchronous reply (plugin.cc method) sendSyncClMsg(m); //Now we have the reply and in m there is our structure stats_ //Reading the answer double FOO = m->getStats().foo1_;
First thing to do is create an appropriate message, so we define m
as a GetStas
message directed to layer 3. Then we call the method sendSyncClMsg
, which is defined in plugin.cc. We point that each module inherits from Module class and Module class inherits from PlugIn class. This method is responsible of sending message from the module to the Node Core and then to the receiving module(s). Notice that, being message synchronous, the end of sendSyncClMsg
procedure means that we have a reply written on m
. At the end, we just recover the wanted info, and store, for example foo1_
in FOO
variable.
Now we explain what happens at the receiver side (the one who provides data requested), in this case a layer 3 module. If this destination module doesn't exist, the packet will be dropped.
int Layer3Module::recvSyncClMsg(ClMessage* m) { if (m->type()==EXAMPLE_GETSTATS) { // Synchronous message -> answer directly in the message received // get the structure housed in the message Stats s = ((GetStats*)m)->getStats(); ... //fill required data fields (I'm writing on the message just arrived!) if (condition){ s.foo1_ = statistic1; s.foo2_ = statistic2; } //rewrite message content ((GetStats*)m)->setStats(s); return 0; }
As sendSyncClMsg
, method recvSyncClMsg
is a method derived from PlugIn class. It has the function of receiving synchronous ClMessages. To decide which actions to do and how to form response, we introduced an if
cycle where we evaluate message type. If it is an EXAMPLE_GETSTATS
message we know what variables the sender needs (in this case statistic1
and statistic2
) and in which format to send back datas (a Stats
structure). Every layer can potentially receive more than one cross layer message type from different layers, each one requiring particular informations and data format.
The next operation to do after the if
check is to get the structure contained in the incoming message, fill the correct fields and set the structure of the message with the newly created structure. Notice that we write the content of s
directly into m
, the incoming message. In this moment, the sender has statistics at its disposal as sender and receiver in this moment share the m
message.
The last note is about the two casts of m
. They are needed because recvSyncClMsg
accepts ClMessages to maintain generalization, in order to accept several kind of messages. So m
is interpreted as a ClMessage and a cast to GetStats
is needed.