FEMTO-ST Institute / DISC /OMNI VisibleSim BlockCode tutorial September 2015

VisibleSim BlockCode tutorial

Structure of VisibleSim

VisibleSim is a behavior simulator for modular connected robots. Each robot executes the same code named BlockCode that manages exchanges of messages between connected modules, internal events (like changing color, moving, aso) and communication with the user interface of the simulator.

The structure of the source code is organized in 3 main directories:

In order to create a BlockCode for the simulation of a distributed program, we have to fill a subdirectory of applicationSrc. This tutorial will help you to generate the files used by the simulator. The name of the files are deduced from the name of the application, for example we create here a simple application that color blocks depending on the distance to the #1 block, we call it simpleColor. Three files are necessary to run the simulation, they will be automatically created by the following webApp:

First BlockCode

You can already generate a first code, just by filling the name of your program (for example simpleColor), then select a type of block and click on "Generate" button.

General information
Application name:
Robot type: Blinky Block Smart Block Robot Block 2D catom 3D catom
simpleColor.cpp
#include <iostream>
#include "blinkyBlocksSimulator.h"
#include "blinkyBlocksBlockCode.h"
#include "simpleColorCode.h"

using namespace std;
using namespace BlinkyBlocks;

int main(int argc, char **argv) {
	createSimulator(argc, argv, SimpleColorCode::buildNewBlockCode);
	Scheduler *scheduler = getScheduler();

	getSimulator()->printInfo();
	scheduler->printInfo();
	BaseSimulator::getWorld()->printInfo();
	deleteSimulator();
	return(0);
}
simpleColorCode.h
#ifndef simpleColorCode_H_
#define simpleColorCode_H_
#include "blinkyBlocksSimulator.h"
#include "blinkyBlocksBlockCode.h"


using namespace BlinkyBlocks;

class SimpleColorCode : public BlinkyBlocksBlockCode {
private:
	BlinkyBlocksBlock *module;
public :
	SimpleColorCode(BlinkyBlocksBlock *host):BlinkyBlocksBlockCode(host) { module=host; };
	~SimpleColorCode() {};

	void startup();

/*****************************************************************************/
/** needed to associate code to module                                      **/
	static BlockCode *buildNewBlockCode(BuildingBlock *host) {
	    return(new SimpleColorCode((BlinkyBlocksBlock*)host));
	};
/*****************************************************************************/
};


#endif /* simpleColorCode_H_ */
using namespace BlinkyBlocks;

class SimpleColorCode : public BlinkyBlocksBlockCode {
private:
	BlinkyBlocksBlock *module;
public :
	SimpleColorCode(BlinkyBlocksBlock *host):BlinkyBlocksBlockCode(host) { module=host; };
	~SimpleColorCode() {};

	void startup();

/*****************************************************************************/
/** needed to associate code to module                                      **/
	static BlinkyBlocksBlockCode *buildNewBlockCode(BlinkyBlocksBlock *host) {
	    return(new SimpleColorCode(host));
	};
/*****************************************************************************/
};


#endif /* simpleColorCode_H_ */
simpleColorCode.cpp
#include "simpleColorCode.h"

void SimpleColorCode::startup() {
	console << "start " << module->blockId << "\n";
	if (module->blockId==1) { // master id is 1

	}
}

The last step to compilate this BlockCode in the simulator consists in copying this Makefile in the applicationSrc/simpleColor) directory, then add the BlockCode directory in the list of compilation directories. To do that, edit the applicationSrc/Makefile and add the directory simpleColor to the SUBDIRS variable.

SUBDIRS = simpleColor

Compile codes from the VisibleSim root directory, go to applicationBin subdirectory and run the program:

VisibleSim$ make
VisibleSim$ cd applicationBin/simpleColor
VisibleSim/applicationBin/simpleColor$ ./simpleColor

This first code does not simulate any interesting behaviour anyway. It just runs the startup() function for each block and then stops the simulation.
The developper has to complete the code of the startup function called at the beginning of the simulation for each module.

In order to test this code, you have to download this example of config.xml in the applicationBin/simpleColor directory.

Messages

In order to make a distributed program, one module or more must send messages to one of its neighbors. For each kind of message, we associate a method in the main class, this method will be automatically called when a message of this kind is received by the module.

You can complete the previous code by adding events, for each event you must precise a unique name and a class (or simple) type. For example, you can create a message broadcast that sends an integer value int, representing the distance of the sender to the master module.

Messages
#NameType
1

Message template

A message is a data packed in a object with an id of message type. We propose a template class MessageOf<T> that manage data of type T, T being the type of the message content. This class inherits from the generic class Message. It is possible to use it with simple types (like in the Exemple 1) or more complex class objects. The method getData() returns a pointer to the data of the message.

class Point {
	public :
	float x,y,z;
};

MessageOf<Point*> mp = new MessageOf<Point*>(ID1,new Point());

Point *ptr = mp.getData();
delete ptr;

Send messages

We propose two functions that send messages from one modules to neighbors:

The two following functions do the same actions but add console message at the sending of each message:

Example 1: coloring

Each module gets an integer variable named distance. Then we propose to complete the code according to the following rules:

  1. Module #1 is elected as the master, it has distance=0 and is drawn in red.
  2. Other modules get distance=-1 that indicates that the distance to the master is not already defined for these modules and they are temporarly colored in light grey.
  3. Module #1 sends its distance to all of its neighbors using a message (BROADCAST_MSG(distance)).
  4. When a module receives a message BROADCAST_MSG(d):
    • If a module receives the message for the first time, (distance==-1), it can deduce that its distance to the master is the distance of the sender plus 1, then distance=d+1.
    • If a module receives the message and has already a distance (distance!=-1), it must compare its distance to the recieved one plus 1 in order to get the shortest.
    • And when a module changes its distance value, it must send it to its neighbors in order to propagate the correction.
    • Then if a module has distance==-1 or distance>d, it sets distance=d+1, change its color and sends its distance to the other neighbors (BROADCAST_MSG(distance)).

We complete the startup function in simpleColorCode.cpp code:

const Color tabColors[8]={RED,ORANGE,YELLOW,GREEN,CYAN,BLUE,MAGENTA,GREY};

void SimpleColorCode::startup() {
// link function to BROADCAST_MSG message
    addMessageEventFunc(BROADCAST_MSG,_myBroadcastFunc);
    console << "start\n";
// initialize random number generator
    srand(time(NULL));
    if (module->blockId==1) { // master id is 1
        setColor(RED); // initial color of master block
        distance=0; // distance to master block is 0
// send message to all neighbors
        sendMessageToAllNeighbors("Broadcast",new MessageOf<int>(BROADCAST_MSG,distance),100,200,0);
    } else {
// for other blocks
        distance=-1; // already unknown distance
        setColor(LIGHTGREY); // initial color
    }
}

and the SimpleColorCode::myBroadcastFunc function:

void SimpleColorCode::myBroadcastFunc(const MessageOf<int>*msg, P2PNetworkInterface*sender) {
// reference distance given by the message data
  int d = *msg->getData()+1;
  console << "receives d=" << d << " from " << sender->getConnectedBlockId() << "\n";
  if (distance==-1 || distance>d) {
    console << "update distance=" << d << "\n";
    distance = d; // update distance
    setColor(tabColors[distance%8]); // change color
// send update to all neighbors but the sender
    sendMessageToAllNeighbors("Broadcast",new MessageOf<int>(BROADCAST_MSG,distance),100,200,1,sender);
  }
};

Figure 1 : Result of the running of the simulation with a configuration of 2240 robot blocks placed in a tube (8≤ r < 10 and h=20). Figure 2 : Result of the running of the simulation with a configuration of 2520 3Dcatoms placed in a tube (8≤ r < 10 and h=20).

Actuators and sensor events

Some of the modules admits actuators to move or sensors to detect 'tap' of the user on the module.

Sensors first are associated to an event function that is run when the detection occurs. For example, blinky blocks admit tap sensors, then if we define the onTap method in our codeblock class, it automatically runs when you use the "tap" menu on the blinky block in the visual interface of VisibleSim.

To move a module (that is able to move, like smartblocks, robotblocks, 2D catoms or 3D catoms) you can schedule an event with of adapted type to the module system. For example, considering a smart block, you can schedule a TranslationStartEvent event precising the time of starting, the module that must be affected and a final position of the module. At the end of the motion, the onMotionEnd function will be automatically ran.

void SimpleMotionCode::startup() {
	if (module->blockId==1) { // if id is 1, it moves
		Vector3D finalPosition;
		finalPosition.set(module->position.pt[0]+1,module->position.pt[1],0);
		scheduler->schedule(new TranslationStartEvent(scheduler->now()+1000,module,finalPosition));
	}
}

void SimpleMotionCode::onMotionEnd() {
    module->setColor(RED);
}

Communication with VisibleSim interface

Output to the simulator console

VisibleSim interface has a console panel on the right that can be used to write some execution information allowing to follow the execution of programs.

This panel shows the global list of outputs of all the running modules or only outputs that concerne the selected block.

In order to add a line in the console from a BlockCode, we can use the console object like a classical output streambuffer. For example, the following code will be used to draw a text and the content of the variable d. In the interface, the entry will precise the time and the id of the block that generate the message.

    console << "update distance=" << d << "\n";

Be carefull, use "\n" end line caracter instead of "endl".

Debugging

VisibleSim proposes to make some debugging actions like :

Benoît Piranda