added a network simulator. Basically exchanges data between peers and visualize the stats of each peer. The basics is here (network draw, data exchange, visualization of turtle router data). The real experiments must now be implemented: adding file requests to create TR, etc

git-svn-id: http://svn.code.sf.net/p/retroshare/code/trunk@5619 b45a01b8-16f6-495d-af2f-9b41ad6348cc
This commit is contained in:
csoler 2012-09-29 14:12:57 +00:00
parent e1181f7b04
commit a597aa4392
18 changed files with 2393 additions and 0 deletions

View File

@ -0,0 +1,24 @@
void NetworkLoop()
{
while(true)
{
std::cerr<< "network loop: tick()" << std::endl;
// Get items for each components and send them to their destination.
//
for(int i=0;i<network->n_nodes();++i)
{
RsRawItem *item ;
while(item = network->node(i).outgoing())
{
PeerNode& node = network->node_by_id(item->peerId())
item->peerId(network->node(i)->id()) ;
node.received(item) ;
}
}
usleep(500000) ;
}
}

View File

@ -0,0 +1,15 @@
#include <turtle/p3turtle.h>
class MonitoredTurtleRouter: public p3turtle
{
public:
MonitoredTurtleRouter(p3LinkMgr *lmgr,ftServer *fts)
: p3turtle(lmgr,fts)
{
}
// Overload functions that I don't want to be called for real!
virtual bool loadConfiguration(std::string& loadHash) { return true ;}
virtual bool saveConfiguration() { return true ;}
};

View File

@ -0,0 +1,161 @@
#include <stdlib.h>
#include <math.h>
#include <iostream>
#include <vector>
#include <list>
#include <string.h>
#include <util/rsid.h>
#include <retroshare/rspeers.h>
#include <turtle/p3turtle.h>
#include <serialiser/rsserial.h>
#include <pqi/p3linkmgr.h>
#include <ft/ftserver.h>
#include <ft/ftcontroller.h>
#include <services/p3service.h>
#include "Network.h"
#include "MonitoredTurtle.h"
bool Network::initRandom(uint32_t nb_nodes,float connexion_probability)
{
_nodes.clear() ;
_neighbors.clear() ;
std::vector<std::string> ids ;
_neighbors.resize(nb_nodes) ;
ids.resize(nb_nodes) ;
for(uint32_t i=0;i<nb_nodes;++i)
{
unsigned char bytes[20] ;
for(int k=0;k<20;++k)
bytes[k] = lrand48() & 0xff ;
ids[i] = t_RsGenericIdType<20>(bytes).toStdString(false) ;
_node_ids[ids[i]] = i ;
}
// Each node has an exponential law of connectivity to friends.
//
for(uint32_t i=0;i<nb_nodes;++i)
{
int nb_friends = (int)ceil(-log(1-drand48())) ;
for(uint32_t j=0;j<nb_friends;++j)
{
int f = i ;
while(f==i)
f = lrand48()%nb_nodes ;
_neighbors[i].insert(f) ;
_neighbors[f].insert(i) ;
}
}
for(uint32_t i=0;i<nb_nodes;++i)
{
std::cerr << "Added new node with id " << ids[i] << std::endl;
std::list<std::string> friends ;
for(std::set<uint32_t>::const_iterator it(_neighbors[i].begin());it!=_neighbors[i].end();++it)
friends.push_back( ids[*it] ) ;
_nodes.push_back( new PeerNode( ids[i], friends ));
}
return true ;
}
void Network::tick()
{
std::cerr<< "network loop: tick()" << std::endl;
// Tick all nodes.
for(uint32_t i=0;i<n_nodes();++i)
node(i).tick() ;
// Get items for each components and send them to their destination.
//
for(uint32_t i=0;i<n_nodes();++i)
{
RsRawItem *item ;
while( (item = node(i).outgoing()) != NULL)
{
PeerNode& node = node_by_id(item->PeerId()) ;
item->PeerId(Network::node(i).id()) ;
node.incoming(item) ;
std::cerr << "Tick: send item from " << item->PeerId() << " to " << Network::node(i).id() << std::endl;
}
}
}
PeerNode& Network::node_by_id(const std::string& id)
{
std::map<std::string,uint32_t>::const_iterator it = _node_ids.find(id) ;
if(it == _node_ids.end())
throw std::runtime_error("Error. Bad id passed to node_by_id ("+id+")") ;
return node(it->second) ;
}
class FakeLinkMgr: public p3LinkMgrIMPL
{
public:
FakeLinkMgr(const std::string& own_id,const std::list<std::string>& friends)
: p3LinkMgrIMPL(NULL,NULL),_own_id(own_id),_friends(friends)
{
}
virtual std::string getOwnId() const { return _own_id ; }
virtual void getOnlineList(std::list<std::string>& lst) const { lst = _friends ; }
virtual uint32_t getLinkType(const std::string&) const { return RS_NET_CONN_TCP_ALL | RS_NET_CONN_SPEED_NORMAL; }
private:
std::string _own_id ;
std::list<std::string> _friends ;
};
const RsTurtle *PeerNode::turtle_service() const
{
return _turtle ;
}
PeerNode::PeerNode(const std::string& id,const std::list<std::string>& friends)
: _id(id)
{
// add a service server.
_service_server = new p3ServiceServer ;
p3LinkMgr *link_mgr = new FakeLinkMgr(id, friends) ;
ftServer *ft_server = new ftServer(NULL,link_mgr) ;
_service_server->addService(_turtle = new MonitoredTurtleRouter(link_mgr,ft_server)) ;
// add a turtle router.
//
}
PeerNode::~PeerNode()
{
delete _service_server ;
}
void PeerNode::tick()
{
_service_server->tick() ;
}
void PeerNode::incoming(RsRawItem *item)
{
_service_server->incoming(item) ;
}
RsRawItem *PeerNode::outgoing()
{
return _service_server->outgoing() ;
}

View File

@ -0,0 +1,71 @@
#pragma once
#include <string>
#include <map>
#include <set>
#include <list>
#include <vector>
#include <stdint.h>
class MonitoredTurtleRouter ;
class RsTurtle ;
class RsRawItem ;
class p3ServiceServer ;
class PeerNode
{
public:
PeerNode(const std::string& id,const std::list<std::string>& friends) ;
~PeerNode() ;
RsRawItem *outgoing() ;
void incoming(RsRawItem *) ;
const std::string& id() const { return _id ;}
void tick() ;
const RsTurtle *turtle_service() const ;
private:
p3ServiceServer *_service_server ;
MonitoredTurtleRouter *_turtle ;
std::string _id ;
};
template<class NODE_TYPE> class Graph
{
public:
typedef uint32_t NodeId ;
void symmetric_connect(uint32_t n1,uint32_t n2) ;
const std::set<NodeId>& neighbors(const NodeId& n) const { return _neighbors[n] ; }
// number of nodes.
//
uint32_t n_nodes() const { return _nodes.size() ;}
const NODE_TYPE& node(int i) const { return *_nodes[i] ; }
NODE_TYPE& node(int i) { return *_nodes[i] ; }
protected:
std::vector<NODE_TYPE *> _nodes ;
std::vector<std::set<uint32_t> > _neighbors ;
};
class Network: public Graph<PeerNode>
{
public:
// inits the graph as random. Returns true if connected, false otherwise.
//
bool initRandom(uint32_t n_nodes, float connexion_probability) ;
// ticks all services of all nodes.
//
void tick() ;
PeerNode& node_by_id(const std::string& node_id) ;
private:
std::map<std::string,uint32_t> _node_ids ;
};

View File

@ -0,0 +1,55 @@
#include <QFrame>
#include <QObject>
#include "NetworkSimulatorGUI.h"
#include "NetworkViewer.h"
#include "TurtleRouterStatistics.h"
NetworkSimulatorGUI::NetworkSimulatorGUI(Network& net)
{
setupUi(this) ;
tickTimerId = 0 ;
QVBoxLayout *layout = new QVBoxLayout(networkViewFrame) ;
layout->addWidget(_viewer = new NetworkViewer(networkViewFrame,net)) ;
QObject::connect(_viewer,SIGNAL(nodeSelected(int)),this,SLOT(updateSelectedNode(int))) ;
QObject::connect(flow_CB,SIGNAL(toggled(bool)),this,SLOT(toggleNetworkTraffic(bool))) ;
QVBoxLayout *layout2 = new QVBoxLayout(inspectorFrame) ;
layout2->addWidget(_turtle_router_statistics = new TurtleRouterStatistics() ) ;
}
void NetworkSimulatorGUI::updateSelectedNode(int node_id)
{
_turtle_router_statistics->setTurtleRouter( _viewer->network().node(node_id).turtle_service() ) ;
}
void NetworkSimulatorGUI::toggleNetworkTraffic(bool b)
{
if(!b && tickTimerId > 0)
{
killTimer(tickTimerId) ;
tickTimerId = 0 ;
return ;
}
if(b && tickTimerId == 0)
{
tickTimerId = startTimer(1000) ;
return ;
}
std::cerr << "ERROR !!" << std::endl;
}
void NetworkSimulatorGUI::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event) ;
std::cerr << "timer event!" << std::endl;
_viewer->network().tick() ;
}

View File

@ -0,0 +1,26 @@
#include "ui_NetworkSimulatorGUI.h"
class TurtleRouterStatistics ;
class NetworkViewer ;
class Network ;
class NetworkSimulatorGUI: public QMainWindow, public Ui::NetworkSimulatorGUI
{
Q_OBJECT
public:
NetworkSimulatorGUI(Network& net) ;
public slots:
void updateSelectedNode(int) ;
void toggleNetworkTraffic(bool) ;
virtual void timerEvent(QTimerEvent *e) ;
private:
NetworkViewer *_viewer ;
TurtleRouterStatistics *_turtle_router_statistics ;
int tickTimerId ;
};

View File

@ -0,0 +1,106 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>NetworkSimulatorGUI</class>
<widget class="QMainWindow" name="NetworkSimulatorGUI">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>901</width>
<height>600</height>
</rect>
</property>
<property name="windowTitle">
<string>MainWindow</string>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QFrame" name="networkViewFrame">
<property name="sizePolicy">
<sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="flow_CB">
<property name="text">
<string>flow</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</item>
<item>
<widget class="QFrame" name="inspectorFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>901</width>
<height>25</height>
</rect>
</property>
<widget class="QMenu" name="menuFile">
<property name="title">
<string>File</string>
</property>
<addaction name="actionLoad_network"/>
<addaction name="actionSave_network"/>
</widget>
<addaction name="menuFile"/>
</widget>
<widget class="QStatusBar" name="statusbar"/>
<action name="actionLoad_network">
<property name="text">
<string>Load network</string>
</property>
</action>
<action name="actionSave_network">
<property name="text">
<string>Save network</string>
</property>
</action>
</widget>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,410 @@
#include <QMouseEvent>
#include "Network.h"
#include "NetworkViewer.h"
NetworkViewer::NetworkViewer(QWidget *parent,Network&net)
: QGLViewer(parent),_network(net) , timerId(0)
{
_current_selected_node = -1 ;
_dragging = false ;
_node_coords.resize(net.n_nodes()) ;
_node_speeds.resize(net.n_nodes()) ;
for(int i=0;i<_node_coords.size();++i)
{
_node_coords[i].x = drand48()*width() ;
_node_coords[i].y = drand48()*height() ;
_node_speeds[i].x = 0 ;
_node_speeds[i].y = 0 ;
}
timerId = startTimer(1000/25) ;
}
void NetworkViewer::draw()
{
glDisable(GL_DEPTH_TEST) ;
glClear(GL_COLOR_BUFFER_BIT) ;
// for now, view is fixed.
glMatrixMode(GL_MODELVIEW) ;
glPushMatrix() ;
glLoadIdentity() ;
glMatrixMode(GL_PROJECTION) ;
glPushMatrix() ;
glLoadIdentity() ;
glOrtho(0,width(),0,height(),1,-1) ;
glEnable(GL_BLEND) ;
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA) ;
// Now, draw all edges
glEnable(GL_LINE_SMOOTH) ;
glColor3f(0.4f,0.4f,0.4f) ;
glBegin(GL_LINES) ;
for(uint32_t i=0;i<_network.n_nodes();++i)
{
const std::set<uint32_t>& neighs( _network.neighbors(i) ) ;
for(std::set<uint32_t>::const_iterator it(neighs.begin());it!=neighs.end();++it)
if( i < *it )
{
glVertex2f(_node_coords[ i].x, _node_coords[ i].y) ;
glVertex2f(_node_coords[*it].x, _node_coords[*it].y) ;
}
}
glEnd() ;
// Draw all nodes.
//
glEnable(GL_POINT_SMOOTH) ;
glPointSize(10.0f) ;
glBegin(GL_POINTS) ;
for(uint32_t i=0;i<_network.n_nodes();++i)
{
if((int)i == _current_selected_node)
glColor3f(1.0f,0.2f,0.1f) ;
else
glColor3f(0.8f,0.8f,0.8f) ;
glVertex2f(_node_coords[i].x, _node_coords[i].y) ;
}
glEnd() ;
glMatrixMode(GL_MODELVIEW) ;
glPopMatrix() ;
glMatrixMode(GL_PROJECTION) ;
glPopMatrix() ;
}
#define SWAP(a,b) tempr=(a);(a)=(b);(b)=tempr
void fourn(double data[],unsigned long nn[],unsigned long ndim,int isign)
{
int i1,i2,i3,i2rev,i3rev,ip1,ip2,ip3,ifp1,ifp2;
int ibit,idim,k1,k2,n,nprev,nrem,ntot;
double tempi,tempr;
double theta,wi,wpi,wpr,wr,wtemp;
ntot=1;
for (idim=1;idim<=(long)ndim;idim++)
ntot *= nn[idim];
nprev=1;
for (idim=ndim;idim>=1;idim--) {
n=nn[idim];
nrem=ntot/(n*nprev);
ip1=nprev << 1;
ip2=ip1*n;
ip3=ip2*nrem;
i2rev=1;
for (i2=1;i2<=ip2;i2+=ip1) {
if (i2 < i2rev) {
for (i1=i2;i1<=i2+ip1-2;i1+=2) {
for (i3=i1;i3<=ip3;i3+=ip2) {
i3rev=i2rev+i3-i2;
SWAP(data[i3],data[i3rev]);
SWAP(data[i3+1],data[i3rev+1]);
}
}
}
ibit=ip2 >> 1;
while (ibit >= ip1 && i2rev > ibit) {
i2rev -= ibit;
ibit >>= 1;
}
i2rev += ibit;
}
ifp1=ip1;
while (ifp1 < ip2) {
ifp2=ifp1 << 1;
theta=isign*6.28318530717959/(ifp2/ip1);
wtemp=sin(0.5*theta);
wpr = -2.0*wtemp*wtemp;
wpi=sin(theta);
wr=1.0;
wi=0.0;
for (i3=1;i3<=ifp1;i3+=ip1) {
for (i1=i3;i1<=i3+ip1-2;i1+=2) {
for (i2=i1;i2<=ip3;i2+=ifp2) {
k1=i2;
k2=k1+ifp1;
tempr=wr*data[k2]-wi*data[k2+1];
tempi=wr*data[k2+1]+wi*data[k2];
data[k2]=data[k1]-tempr;
data[k2+1]=data[k1+1]-tempi;
data[k1] += tempr;
data[k1+1] += tempi;
}
}
wr=(wtemp=wr)*wpr-wi*wpi+wr;
wi=wi*wpr+wtemp*wpi+wi;
}
ifp1=ifp2;
}
nprev *= n;
}
}
#undef SWAP
static void convolveWithGaussian(double *forceMap,int S,int /*s*/)
{
static double *bf = NULL ;
if(bf == NULL)
{
bf = new double[S*S*2] ;
for(int i=0;i<S;++i)
for(int j=0;j<S;++j)
{
int x = (i<S/2)?i:(S-i) ;
int y = (j<S/2)?j:(S-j) ;
// int l=2*(x*x+y*y);
bf[2*(i+S*j)] = log(sqrtf(0.1 + x*x+y*y)); // linear -> derivative is constant
bf[2*(i+S*j)+1] = 0 ;
}
unsigned long nn[2] = {S,S};
fourn(&bf[-1],&nn[-1],2,1) ;
}
unsigned long nn[2] = {S,S};
fourn(&forceMap[-1],&nn[-1],2,1) ;
for(int i=0;i<S;++i)
for(int j=0;j<S;++j)
{
float a = forceMap[2*(i+S*j)]*bf[2*(i+S*j)] - forceMap[2*(i+S*j)+1]*bf[2*(i+S*j)+1] ;
float b = forceMap[2*(i+S*j)]*bf[2*(i+S*j)+1] + forceMap[2*(i+S*j)+1]*bf[2*(i+S*j)] ;
forceMap[2*(i+S*j)] = a ;
forceMap[2*(i+S*j)+1] = b ;
}
fourn(&forceMap[-1],&nn[-1],2,-1) ;
for(int i=0;i<S*S*2;++i)
forceMap[i] /= S*S;
}
void NetworkViewer::timerEvent(QTimerEvent *event)
{
Q_UNUSED(event);
if(!isVisible())
return ;
static const int S = 256 ;
static double *forceMap = new double[2*S*S] ;
memset(forceMap,0,2*S*S*sizeof(double)) ;
for(uint32_t i=0;i<_network.n_nodes();++i)
{
float x = S*_node_coords[i].x/width() ;
float y = S*_node_coords[i].y/height() ;
int i=(int)floor(x) ;
int j=(int)floor(y) ;
float di = x-i ;
float dj = y-j ;
if( i>=0 && i<S-1 && j>=0 && j<S-1)
{
forceMap[2*(i +S*(j ))] += (1-di)*(1-dj) ;
forceMap[2*(i+1+S*(j ))] += di *(1-dj) ;
forceMap[2*(i +S*(j+1))] += (1-di)*dj ;
forceMap[2*(i+1+S*(j+1))] += di *dj ;
}
}
// compute convolution with 1/omega kernel.
//
convolveWithGaussian(forceMap,S,20) ;
static float speedf=1.0f;
std::vector<NodeCoord> new_coords(_node_coords) ;
for(uint32_t i=0;i<_network.n_nodes();++i)
if(i != _current_selected_node || !_dragging)
{
float x = _node_coords[i].x ;
float y = _node_coords[i].y ;
calculateForces(i,forceMap,S,S,x,y,speedf,new_coords[i].x,new_coords[i].y);
}
bool itemsMoved = false;
for(uint32_t i=0;i<_node_coords.size();++i)
{
if( fabsf(_node_coords[i].x - new_coords[i].x) > 0.2 || fabsf(_node_coords[i].y - new_coords[i].y) > 0.2)
itemsMoved = true;
//std::cerr << "Old i = " << _node_coords[i].x << ", new = " << new_coords[i].x << std::endl;
_node_coords[i] = new_coords[i] ;
}
if (!itemsMoved) {
killTimer(timerId);
#ifdef DEBUG_ELASTIC
std::cerr << "Killing timr" << std::endl ;
#endif
timerId = 0;
}
else
{
updateGL() ;
usleep(2000) ;
}
}
void NetworkViewer::mouseMoveEvent(QMouseEvent *e)
{
if(_dragging && _current_selected_node >= 0)
{
_node_coords[_current_selected_node].x = e->x() ;
_node_coords[_current_selected_node].y = height() - e->y() ;
if(timerId == 0)
timerId = startTimer(1000/25) ;
updateGL() ;
}
}
void NetworkViewer::mouseReleaseEvent(QMouseEvent *e)
{
_dragging = false ;
}
void NetworkViewer::mousePressEvent(QMouseEvent *e)
{
float x = e->x() ;
float y = height() - e->y() ;
// find which node is selected
for(uint32_t i=0;i<_node_coords.size();++i)
if( pow(_node_coords[i].x - x,2)+pow(_node_coords[i].y - y,2) < 10*10 )
{
_current_selected_node = i ;
_dragging = true ;
updateGL() ;
emit nodeSelected(i) ;
return ;
}
_dragging = false ;
_current_selected_node = -1 ;
}
void NetworkViewer::calculateForces(const Network::NodeId& node_id,const double *map,int W,int H,float x,float y,float /*speedf*/,float& new_x, float& new_y)
{
#ifdef A_FAIRE
if (mouseGrabberItem() == this)
{
new_x = x ;
new_y = y ;
return;
}
#endif
// Sum up all forces pushing this item away
qreal xforce = 0;
qreal yforce = 0;
float dei=0.0f ;
float dej=0.0f ;
static float *e = NULL ;
static const int KS = 5 ;
if(e == NULL)
{
e = new float[(2*KS+1)*(2*KS+1)] ;
for(int i=-KS;i<=KS;++i)
for(int j=-KS;j<=KS;++j)
e[i+KS+(2*KS+1)*(j+KS)] = exp( -(i*i+j*j)/30.0 ) ; // can be precomputed
}
for(int i=-KS;i<=KS;++i)
for(int j=-KS;j<=KS;++j)
{
int X = std::min(W-1,std::max(0,(int)rint(x/(float)width()*W))) ;
int Y = std::min(H-1,std::max(0,(int)rint(y/(float)height()*H))) ;
float val = map[2*((i+X+W)%W + W*((j+Y+H)%H))] ;
dei += i * e[i+KS+(2*KS+1)*(j+KS)] * val ;
dej += j * e[i+KS+(2*KS+1)*(j+KS)] * val ;
}
xforce = REPULSION_FACTOR * dei/25.0;
yforce = REPULSION_FACTOR * dej/25.0;
// Now subtract all forces pulling items together
//
const std::set<Network::NodeId>& neighbs(_network.neighbors(node_id)) ;
double weight = neighbs.size() + 1 ;
for(std::set<Network::NodeId>::const_iterator it(neighbs.begin());it!=neighbs.end();++it)
{
NodeCoord pos;
double w2 ; // This factor makes the edge length depend on connectivity, so clusters of friends tend to stay in the
// same location.
//
pos.x = _node_coords[*it].x - x ; //mapFromItem(edge->destNode(), 0, 0);
pos.y = _node_coords[*it].y - y ; //mapFromItem(edge->destNode(), 0, 0);
w2 = sqrtf(std::min(neighbs.size(),_network.neighbors(*it).size())) ;
float dist = sqrtf(pos.x*pos.x + pos.y*pos.y) ;
float val = dist - NODE_DISTANCE * w2 ;
xforce += 0.01*pos.x * val / weight;
yforce += 0.01*pos.y * val / weight;
}
xforce -= FRICTION_FACTOR * _node_speeds[node_id].x ;
yforce -= FRICTION_FACTOR * _node_speeds[node_id].y ;
// This term drags nodes away from the sides.
//
if(x < 15) xforce += 100.0/(x+0.1) ;
if(y < 15) yforce += 100.0/(y+0.1) ;
if(x > width()-15) xforce -= 100.0/(width()-x+0.1) ;
if(y > height()-15) yforce -= 100.0/(height()-y+0.1) ;
// now time filter:
_node_speeds[node_id].x += xforce / MASS_FACTOR;
_node_speeds[node_id].y += yforce / MASS_FACTOR;
if(_node_speeds[node_id].x > 10) _node_speeds[node_id].x = 10.0f ;
if(_node_speeds[node_id].y > 10) _node_speeds[node_id].y = 10.0f ;
if(_node_speeds[node_id].x <-10) _node_speeds[node_id].x =-10.0f ;
if(_node_speeds[node_id].y <-10) _node_speeds[node_id].y =-10.0f ;
new_x = x + _node_speeds[node_id].x ;
new_y = y + _node_speeds[node_id].y ;
new_x = std::min(std::max(new_x, 10.0f), width() - 10.0f);
new_y = std::min(std::max(new_y, 10.0f), height() - 10.0f);
}

View File

@ -0,0 +1,68 @@
#include <QGLViewer/qglviewer.h>
// The network simulator GUI has the following functionalities:
//
// 1 - show the network graph
// * the graph should spread in space automatically. We should use the code from the NetworkView for that.
// * each edge will be drawn with a color that displays used bandwidth along the edge, or TR sent to the edge, etc.
// * individual tunnels should be shown, in order to see what shape they have
//
// 2 - show info about each node. One needs a node widget that gets updated with whatever is to be read from the current node
//
// 3 - buttons to re-initiate a new network, or reset the network.
//
// 4 - buttons to inject information into the network:
// * shared files in some nodes. Should be handled by derivign the component that the turtle router accesses for local searches.
// * file requests in other nodes. Eazy: one just needs to ask the turtle router of the node to handle the hash.
//
// 5 - perturbate the network
// * change the load of each node, and the delay when forwarding requests.
//
#include "Network.h"
class NetworkViewer: public QGLViewer
{
Q_OBJECT
public:
NetworkViewer(QWidget *parent,Network& network) ;
virtual void draw() ;
virtual void keyPressEvent(QKeyEvent *) {}
virtual void mousePressEvent(QMouseEvent *) ;
virtual void mouseReleaseEvent(QMouseEvent *) ;
virtual void mouseMoveEvent(QMouseEvent *) ;
const Network& network() const { return _network ; }
Network& network() { return _network ; }
signals:
void nodeSelected(int) ;
public slots:
void timerEvent(QTimerEvent *) ;
private:
void calculateForces(const Network::NodeId& node_id,const double *map,int W,int H,float x,float y,float /*speedf*/,float& new_x, float& new_y) ;
typedef struct
{
float x ;
float y ;
} NodeCoord ;
Network& _network ;
std::vector<NodeCoord> _node_coords ;
std::vector<NodeCoord> _node_speeds ;
static const float MASS_FACTOR = 7 ;
static const float FRICTION_FACTOR = 9.8 ;
static const float REPULSION_FACTOR = 8 ;
static const float NODE_DISTANCE = 30.0 ;
int timerId ;
int _current_selected_node ;
bool _dragging ;
};

View File

@ -0,0 +1,38 @@
The purpose of this directory is to write a Network simulator, that can have multiple turtle routers interact
together. The routers will talk to a fake link manager, which reports the peers for each node of a network graph.
Required components:
===================
NetworkGraph: a set of friends, with connexions. Should be able to be saved to a file for debugging.
GraphNode: a RS peer, represented by a random SSL id, a link manager, and possibly components such as file transfer, etc.
Main loop: a loop calling tick() on all turtle routers.
Functions:
* gather statistics over network load. See if tunnels are ok, improve bandwidth allocation strategy, see request broadcast.
GUI:
* visualization of the graph. OpenGL + qglviewer window. Show tunnels, data flow as colors, etc.
* give quantitative information under mouse
* the user can trigger behaviors, execute tunnel handling orders, cause file transfer, etc.
Implementation constraints
==========================
* sendItem() and recvItems() should come from above. The class p3Service thus needs to be re-implemented to get/send the
data properly.
=> define subclass of p3turtle , where send() and recv() are redefined.
* LinkMgr:
getOwnId(), getLinkType(), getOnlineList()
used by turtle
* turtle needs LinkMgr and ftServer. The ftServer can be contructed from PeerMgr (not called except in ftServer::setupFtServer. not needed here.
Complilation
============
* needs QGLViewer-dev library (standard on ubuntu, package name is libqglviewer-qt4-dev)

View File

@ -0,0 +1,47 @@
#include <QTimer>
#include "RsAutoUpdatePage.h"
bool RsAutoUpdatePage::_locked = false ;
RsAutoUpdatePage::RsAutoUpdatePage(int ms_update_period, QWidget *parent, Qt::WindowFlags flags)
: QWidget(parent, flags)
{
_timer = new QTimer ;
_timer->setInterval(ms_update_period);
_timer->setSingleShot(true);
QObject::connect(_timer,SIGNAL(timeout()),this,SLOT(timerUpdate())) ;
_timer->start() ;
}
RsAutoUpdatePage::~RsAutoUpdatePage()
{
if(_timer != NULL)
delete _timer ;
_timer = NULL ;
}
void RsAutoUpdatePage::showEvent(QShowEvent */*event*/)
{
//std::cout << "RsAutoUpdatePage::showEvent() In show event !!" << std::endl ;
if(!_locked)
updateDisplay();
}
void RsAutoUpdatePage::timerUpdate()
{
// only update when the widget is visible.
//
if(_locked == false && isVisible()) {
updateDisplay();
update() ; // Qt flush
}
_timer->start() ;
}
void RsAutoUpdatePage::lockAllEvents() { _locked = true ; }
void RsAutoUpdatePage::unlockAllEvents() { _locked = false ; }
bool RsAutoUpdatePage::eventsLocked() { return _locked ; }

View File

@ -0,0 +1,40 @@
#pragma once
#include <QApplication>
#include <QWidget>
// This class implement a basic RS functionality which is that widgets displayign info
// should update regularly. They also should update only when visible, to save CPU time.
//
// Using this class simply needs to derive your widget from RsAutoUpdateWidget
// and oveload the update() function with the actual code that updates the
// widget.
//
class QTimer ;
class RsAutoUpdatePage: public QWidget
{
Q_OBJECT
public:
RsAutoUpdatePage(int ms_update_period = 1000, QWidget *parent = NULL, Qt::WindowFlags flags = 0) ;
virtual ~RsAutoUpdatePage() ;
virtual void updateDisplay() {}
static void lockAllEvents() ;
static void unlockAllEvents() ;
static bool eventsLocked() ;
protected:
virtual void showEvent(QShowEvent *e) ;
private slots:
void timerUpdate() ;
private:
QTimer *_timer ;
static bool _locked ;
};

View File

@ -0,0 +1,324 @@
/****************************************************************
* RetroShare is distributed under the following license:
*
* Copyright (C) 20011, RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#include <iostream>
#include <QTimer>
#include <QObject>
#include <QPainter>
#include <QStylePainter>
#include <retroshare/rsturtle.h>
#include <retroshare/rspeers.h>
#include "TurtleRouterStatistics.h"
static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ;
class TRHistogram
{
public:
TRHistogram(const std::vector<TurtleRequestDisplayInfo >& info) :_infos(info) {}
QColor colorScale(float f)
{
if(f == 0)
return QColor::fromHsv(0,0,192) ;
else
return QColor::fromHsv((int)((1.0-f)*280),200,255) ;
}
virtual void draw(QPainter *painter,int& ox,int& oy,const QString& title)
{
static const int MaxTime = 61 ;
static const int MaxDepth = 8 ;
static const int cellx = 7 ;
static const int celly = 12 ;
int save_ox = ox ;
painter->setPen(QColor::fromRgb(0,0,0)) ;
painter->drawText(2+ox,celly+oy,title) ;
oy+=2+2*celly ;
if(_infos.empty())
return ;
ox += 10 ;
std::map<std::string,std::vector<int> > hits ;
std::map<std::string,std::vector<int> > depths ;
std::map<std::string,std::vector<int> >::iterator it ;
int max_hits = 1;
int max_depth = 1;
for(uint32_t i=0;i<_infos.size();++i)
{
std::vector<int>& h(hits[_infos[i].source_peer_id]) ;
std::vector<int>& g(depths[_infos[i].source_peer_id]) ;
if(h.size() <= _infos[i].age)
h.resize(MaxTime,0) ;
if(g.empty())
g.resize(MaxDepth,0) ;
if(_infos[i].age < h.size())
{
h[_infos[i].age]++ ;
if(h[_infos[i].age] > max_hits)
max_hits = h[_infos[i].age] ;
}
if(_infos[i].depth < g.size())
{
g[_infos[i].depth]++ ;
if(g[_infos[i].depth] > max_depth)
max_depth = g[_infos[i].depth] ;
}
}
int max_bi = std::max(max_hits,max_depth) ;
int p=0 ;
for(it=depths.begin();it!=depths.end();++it,++p)
for(int i=0;i<MaxDepth;++i)
painter->fillRect(ox+MaxTime*cellx+20+i*cellx,oy+p*celly,cellx,celly,colorScale(it->second[i]/(float)max_bi)) ;
painter->setPen(QColor::fromRgb(0,0,0)) ;
painter->drawRect(ox+MaxTime*cellx+20,oy,MaxDepth*cellx,p*celly) ;
for(int i=0;i<MaxTime;i+=5)
painter->drawText(ox+i*cellx,oy+(p+1)*celly+4,QString::number(i)) ;
p=0 ;
int great_total = 0 ;
for(it=hits.begin();it!=hits.end();++it,++p)
{
int total = 0 ;
for(int i=0;i<MaxTime;++i)
{
painter->fillRect(ox+i*cellx,oy+p*celly,cellx,celly,colorScale(it->second[i]/(float)max_bi)) ;
total += it->second[i] ;
}
painter->setPen(QColor::fromRgb(0,0,0)) ;
painter->drawText(ox+MaxDepth*cellx+30+(MaxTime+1)*cellx,oy+(p+1)*celly,TurtleRouterStatistics::getPeerName(it->first)) ;
painter->drawText(ox+MaxDepth*cellx+30+(MaxTime+1)*cellx+120,oy+(p+1)*celly,"("+QString::number(total)+")") ;
great_total += total ;
}
painter->drawRect(ox,oy,MaxTime*cellx,p*celly) ;
for(int i=0;i<MaxTime;i+=5)
painter->drawText(ox+i*cellx,oy+(p+1)*celly+4,QString::number(i)) ;
for(int i=0;i<MaxDepth;i++)
painter->drawText(ox+MaxTime*cellx+20+i*cellx,oy+(p+1)*celly+4,QString::number(i)) ;
painter->setPen(QColor::fromRgb(255,130,80)) ;
painter->drawText(ox+MaxDepth*cellx+30+(MaxTime+1)*cellx+120,oy+(p+1)*celly+4,"("+QString::number(great_total)+")");
oy += (p+1)*celly+6 ;
painter->setPen(QColor::fromRgb(0,0,0)) ;
painter->drawText(ox,oy+celly,"("+QApplication::translate("TurtleRouterStatistics", "Age in seconds")+")");
painter->drawText(ox+MaxTime*cellx+20,oy+celly,"("+QApplication::translate("TurtleRouterStatistics", "Depth")+")");
painter->drawText(ox+MaxDepth*cellx+30+(MaxTime+1)*cellx+120,oy+celly,"("+QApplication::translate("TurtleRouterStatistics", "total")+")");
oy += 3*celly ;
// now, draw a scale
int last_hts = -1 ;
int cellid = 0 ;
for(int i=0;i<=10;++i)
{
int hts = (int)(max_bi*i/10.0) ;
if(hts > last_hts)
{
painter->fillRect(ox+cellid*(cellx+22),oy,cellx,celly,colorScale(i/10.0f)) ;
painter->setPen(QColor::fromRgb(0,0,0)) ;
painter->drawRect(ox+cellid*(cellx+22),oy,cellx,celly) ;
painter->drawText(ox+cellid*(cellx+22)+cellx+4,oy+celly,QString::number(hts)) ;
last_hts = hts ;
++cellid ;
}
}
oy += celly*2 ;
ox = save_ox ;
}
private:
const std::vector<TurtleRequestDisplayInfo>& _infos ;
};
TurtleRouterStatistics::TurtleRouterStatistics(QWidget *parent)
: RsAutoUpdatePage(2000,parent)
{
setupUi(this) ;
_turtle = NULL ;
_tunnel_statistics_F->setWidget( _tst_CW = new TurtleRouterStatisticsWidget() ) ;
_tunnel_statistics_F->setWidgetResizable(true);
_tunnel_statistics_F->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
_tunnel_statistics_F->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
_tunnel_statistics_F->viewport()->setBackgroundRole(QPalette::NoRole);
_tunnel_statistics_F->setFrameStyle(QFrame::NoFrame);
_tunnel_statistics_F->setFocusPolicy(Qt::NoFocus);
}
TurtleRouterStatistics::~TurtleRouterStatistics()
{
}
void TurtleRouterStatistics::updateDisplay()
{
if(_turtle == NULL)
return ;
std::vector<std::vector<std::string> > hashes_info ;
std::vector<std::vector<std::string> > tunnels_info ;
std::vector<TurtleRequestDisplayInfo > search_reqs_info ;
std::vector<TurtleRequestDisplayInfo > tunnel_reqs_info ;
_turtle->getInfo(hashes_info,tunnels_info,search_reqs_info,tunnel_reqs_info) ;
//updateTunnelRequests(hashes_info,tunnels_info,search_reqs_info,tunnel_reqs_info) ;
_tst_CW->updateTunnelStatistics(hashes_info,tunnels_info,search_reqs_info,tunnel_reqs_info,_turtle) ;
_tst_CW->update();
}
QString TurtleRouterStatistics::getPeerName(const std::string& peer_id)
{
static std::map<std::string, QString> names ;
std::map<std::string,QString>::const_iterator it = names.find(peer_id) ;
if( it != names.end())
return it->second ;
else
{
RsPeerDetails detail ;
if(!rsPeers->getPeerDetails(peer_id,detail))
return tr("Unknown Peer");
return (names[peer_id] = QString::fromUtf8(detail.name.c_str())) ;
}
}
TurtleRouterStatisticsWidget::TurtleRouterStatisticsWidget(QWidget *parent)
: QWidget(parent)
{
maxWidth = 200 ;
maxHeight = 0 ;
}
void TurtleRouterStatisticsWidget::updateTunnelStatistics(const std::vector<std::vector<std::string> >& /*hashes_info*/,
const std::vector<std::vector<std::string> >& /*tunnels_info*/,
const std::vector<TurtleRequestDisplayInfo >& search_reqs_info,
const std::vector<TurtleRequestDisplayInfo >& tunnel_reqs_info,
const RsTurtle *turtle)
{
static const int cellx = 6 ;
static const int celly = 10+4 ;
QPixmap tmppixmap(maxWidth, maxHeight);
tmppixmap.fill(this, 0, 0);
setFixedHeight(maxHeight);
QPainter painter(&tmppixmap);
painter.initFrom(this);
maxHeight = 500 ;
// std::cerr << "Drawing into pixmap of size " << maxWidth << "x" << maxHeight << std::endl;
// draw...
int ox=5,oy=5 ;
TRHistogram(search_reqs_info).draw(&painter,ox,oy,tr("Search requests repartition") + ":") ;
painter.setPen(QColor::fromRgb(70,70,70)) ;
painter.drawLine(0,oy,maxWidth,oy) ;
oy += celly ;
TRHistogram(tunnel_reqs_info).draw(&painter,ox,oy,tr("Tunnel requests repartition") + ":") ;
// now give information about turtle traffic.
//
TurtleTrafficStatisticsInfo info ;
turtle->getTrafficStatistics(info) ;
painter.setPen(QColor::fromRgb(70,70,70)) ;
painter.drawLine(0,oy,maxWidth,oy) ;
oy += celly ;
painter.drawText(ox,oy+celly,tr("Turtle router traffic")+":") ; oy += celly*2 ;
painter.drawText(ox+2*cellx,oy+celly,tr("Tunnel requests Up")+"\t: " + speedString(info.tr_up_Bps) ) ; oy += celly ;
painter.drawText(ox+2*cellx,oy+celly,tr("Tunnel requests Dn")+"\t: " + speedString(info.tr_dn_Bps) ) ; oy += celly ;
painter.drawText(ox+2*cellx,oy+celly,tr("Incoming file data")+"\t: " + speedString(info.data_dn_Bps) ) ; oy += celly ;
painter.drawText(ox+2*cellx,oy+celly,tr("Outgoing file data")+"\t: " + speedString(info.data_up_Bps) ) ; oy += celly ;
painter.drawText(ox+2*cellx,oy+celly,tr("Forwarded data ")+"\t: " + speedString(info.unknown_updn_Bps) ) ; oy += celly ;
QString prob_string ;
for(uint i=0;i<info.forward_probabilities.size();++i)
prob_string += QString::number(info.forward_probabilities[i],'g',2) + " (" + QString::number(i) + ") " ;
painter.drawText(ox+2*cellx,oy+celly,tr("TR Forward probabilities")+"\t: " + prob_string ) ;
oy += celly ;
oy += celly ;
// update the pixmap
//
pixmap = tmppixmap;
maxHeight = oy ;
}
QString TurtleRouterStatisticsWidget::speedString(float f)
{
if(f < 1.0f)
return QString("0 B/s") ;
if(f < 1024.0f)
return QString::number((int)f)+" B/s" ;
return QString::number(f/1024.0,'f',2) + " KB/s";
}
void TurtleRouterStatisticsWidget::paintEvent(QPaintEvent */*event*/)
{
QStylePainter(this).drawPixmap(0, 0, pixmap);
}
void TurtleRouterStatisticsWidget::resizeEvent(QResizeEvent *event)
{
QRect TaskGraphRect = geometry();
maxWidth = TaskGraphRect.width();
maxHeight = TaskGraphRect.height() ;
QWidget::resizeEvent(event);
update();
}

View File

@ -0,0 +1,74 @@
/****************************************************************
* RetroShare is distributed under the following license:
*
* Copyright (C) 20011, RetroShare Team
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
****************************************************************/
#pragma once
#include <QPoint>
#include <retroshare/rsturtle.h>
#include "ui_TurtleRouterStatistics.h"
#include "RsAutoUpdatePage.h"
class TurtleRouterStatisticsWidget ;
class TurtleRouterStatistics: public RsAutoUpdatePage, public Ui::TurtleRouterStatistics
{
Q_OBJECT
public:
TurtleRouterStatistics(QWidget *parent = NULL) ;
~TurtleRouterStatistics();
// Cache for peer names.
static QString getPeerName(const std::string& peer_id) ;
void setTurtleRouter(const RsTurtle *turtle) { _turtle = turtle ; }
private:
virtual void updateDisplay() ;
TurtleRouterStatisticsWidget *_tst_CW ;
const RsTurtle *_turtle ;
} ;
class TurtleRouterStatisticsWidget: public QWidget
{
Q_OBJECT
public:
TurtleRouterStatisticsWidget(QWidget *parent = NULL) ;
virtual void paintEvent(QPaintEvent *event) ;
virtual void resizeEvent(QResizeEvent *event);
void updateTunnelStatistics( const std::vector<std::vector<std::basic_string<char> > >&,
const std::vector<std::vector<std::basic_string<char> > >&,
const std::vector<TurtleRequestDisplayInfo >&,
const std::vector<TurtleRequestDisplayInfo >&,
const RsTurtle *turtle) ;
private:
static QString speedString(float f) ;
QPixmap pixmap ;
int maxWidth,maxHeight ;
};

View File

@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>TurtleRouterStatistics</class>
<widget class="QWidget" name="TurtleRouterStatistics">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>611</width>
<height>408</height>
</rect>
</property>
<property name="windowTitle">
<string>Router Statistics</string>
</property>
<property name="windowIcon">
<iconset resource="images.qrc">
<normaloff>:/images/rstray3.png</normaloff>:/images/rstray3.png</iconset>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QSplitter" name="splitter">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<widget class="QScrollArea" name="_tunnel_statistics_F">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="horizontalScrollBarPolicy">
<enum>Qt::ScrollBarAlwaysOff</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>593</width>
<height>390</height>
</rect>
</property>
</widget>
</widget>
</widget>
</item>
</layout>
</widget>
<resources>
<include location="images.qrc"/>
</resources>
<connections/>
</ui>

View File

@ -0,0 +1,814 @@
/* Copyright (C) 2004 Xavier Décoret <Xavier.Decoret@imag.fr>
*
* argsteam is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* Foobar is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Foobar; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#ifndef ARGSTREAM_H
#define ARGSTREAM_H
#include <stdlib.h>
#include <string>
#include <list>
#include <deque>
#include <map>
#include <stdexcept>
#include <sstream>
#include <iostream>
namespace
{
class argstream;
template<class T>
class ValueHolder;
template <typename T>
argstream& operator>> (argstream&, const ValueHolder<T>&);
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueHolder<T>
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template<class T>
class ValueHolder
{
public:
ValueHolder(char s,
const char* l,
T& b,
const char* desc,
bool mandatory);
ValueHolder(const char* l,
T& b,
const char* desc,
bool mandatory);
ValueHolder(char s,
T& b,
const char* desc,
bool mandatory);
friend argstream& operator>><>(argstream& s,const ValueHolder<T>& v);
std::string name() const;
std::string description() const;
private:
std::string shortName_;
std::string longName_;
T* value_;
T initialValue_;
std::string description_;
bool mandatory_;
};
template <class T>
inline ValueHolder<T>
parameter(char s,
const char* l,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,l,b,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(char s,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(s,b,desc,mandatory);
}
template <class T>
inline ValueHolder<T>
parameter(const char* l,
T& b,
const char* desc="",
bool mandatory = true)
{
return ValueHolder<T>(l,b,desc,mandatory);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of OptionHolder
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class OptionHolder
{
public:
inline OptionHolder(char s,
const char* l,
bool& b,
const char* desc);
inline OptionHolder(const char* l,
bool& b,
const char* desc);
inline OptionHolder(char s,
bool& b,
const char* desc);
friend argstream& operator>>(argstream& s,const OptionHolder& v);
inline std::string name() const;
inline std::string description() const;
protected:
inline OptionHolder(char s,
const char* l,
const char* desc);
friend OptionHolder help(char s='h',
const char* l="help",
const char* desc="Display this help");
private:
std::string shortName_;
std::string longName_;
bool* value_;
std::string description_;
};
inline OptionHolder
option(char s,
const char* l,
bool& b,
const char* desc="")
{
return OptionHolder(s,l,b,desc);
}
inline OptionHolder
option(char s,
bool& b,
const char* desc="")
{
return OptionHolder(s,b,desc);
}
inline OptionHolder
option(const char* l,
bool& b,
const char* desc="")
{
return OptionHolder(l,b,desc);
}
inline OptionHolder
help(char s,
const char* l,
const char* desc)
{
return OptionHolder(s,l,desc);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValuesHolder
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template<class T,class O>
class ValuesHolder
{
public:
ValuesHolder(const O& o,
const char* desc,
int len);
template<class A,class B> friend argstream& operator>>(argstream& s,const ValuesHolder<A,B>& v);
std::string name() const;
std::string description() const;
typedef T value_type;
private:
mutable O value_;
std::string description_;
int len_;
char letter_;
};
template<class T,class O>
inline ValuesHolder<T,O>
values(const O& o,
const char* desc="",
int len=-1)
{
return ValuesHolder<T,O>(o,desc,len);
}
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of ValueParser
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
template <class T>
class ValueParser
{
public:
inline T operator()(const std::string& s) const
{
std::istringstream is(s);
T t;
is>>t;
return t;
}
};
// We need to specialize for string otherwise parsing of a value that
// contains space (for example a string with space passed in quotes on the
// command line) would parse only the first element of the value!!!
template <>
class ValueParser<std::string>
{
public:
inline std::string operator()(const std::string& s) const
{
return s;
}
};
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// Interface of argstream
//++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
class argstream
{
public:
inline argstream(int argc,char** argv);
inline argstream(const char* c);
template<class T>
friend argstream& operator>>(argstream& s,const ValueHolder<T>& v);
friend inline argstream& operator>>(argstream& s,const OptionHolder& v);
template<class T,class O>
friend argstream& operator>>(argstream& s,const ValuesHolder<T,O>& v);
inline bool helpRequested() const;
inline bool isOk() const;
inline std::string errorLog() const;
inline std::string usage() const;
inline void defaultErrorHandling(bool ignoreUnused=false) const;
static inline char uniqueLetter();
protected:
void parse(int argc,char** argv);
private:
typedef std::list<std::string>::iterator value_iterator;
typedef std::pair<std::string,std::string> help_entry;
std::string progName_;
std::map<std::string,value_iterator> options_;
std::list<std::string> values_;
bool minusActive_;
bool isOk_;
std::deque<help_entry> argHelps_;
std::string cmdLine_;
std::deque<std::string> errors_;
bool helpRequested_;
};
//************************************************************
// Implementation of ValueHolder<T>
//************************************************************
template<class T>
ValueHolder<T>::ValueHolder(char s,
const char* l,
T& v,
const char* desc,
bool mandatory)
: shortName_(1,s),
longName_(l),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
ValueHolder<T>::ValueHolder(const char* l,
T& v,
const char* desc,
bool mandatory)
: longName_(l),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
ValueHolder<T>::ValueHolder(char s,
T& v,
const char* desc,
bool mandatory)
: shortName_(1,s),
value_(&v),
initialValue_(v),
description_(desc),
mandatory_(mandatory)
{
}
template<class T>
std::string
ValueHolder<T>::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty()) {
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
template<class T>
std::string
ValueHolder<T>::description() const
{
std::ostringstream os;
os<<description_;
if (mandatory_)
{
os<<"(mandatory)";
}
else
{
os<<"(default="<<initialValue_<<")";
}
return os.str();
}
//************************************************************
// Implementation of OptionHolder
//************************************************************
inline OptionHolder::OptionHolder(char s,
const char* l,
bool& b,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(const char* l,
bool& b,
const char* desc)
: longName_(l),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
bool& b,
const char* desc)
: shortName_(1,s),
value_(&b),
description_(desc)
{
}
inline OptionHolder::OptionHolder(char s,
const char* l,
const char* desc)
: shortName_(1,s),
longName_(l),
value_(NULL),
description_(desc)
{
}
inline std::string
OptionHolder::name() const
{
std::ostringstream os;
if (!shortName_.empty()) os<<'-'<<shortName_;
if (!longName_.empty())
{
if (!shortName_.empty()) os<<'/';
os<<"--"<<longName_;
}
return os.str();
}
inline std::string
OptionHolder::description() const
{
return description_;
}
//************************************************************
// Implementation of ValuesHolder<T,O>
//************************************************************
template<class T,class O>
ValuesHolder<T,O>::ValuesHolder(const O& o,
const char* desc,
int len)
: value_(o),
description_(desc),
len_(len)
{
letter_ = argstream::uniqueLetter();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::name() const
{
std::ostringstream os;
os<<letter_<<"i";
return os.str();
}
template <class T,class O>
std::string
ValuesHolder<T,O>::description() const
{
return description_;
}
//************************************************************
// Implementation of argstream
//************************************************************
inline
argstream::argstream(int argc,char** argv)
: progName_(argv[0]),
minusActive_(true),
isOk_(true)
{
parse(argc,argv);
}
inline
argstream::argstream(const char* c)
: progName_(""),
minusActive_(true),
isOk_(true)
{
std::string s(c);
// Build argc, argv from s. We must add a dummy first element for
// progName because parse() expects it!!
std::deque<std::string> args;
args.push_back("");
std::istringstream is(s);
while (is.good())
{
std::string t;
is>>t;
args.push_back(t);
}
char* pargs[args.size()];
char** p = pargs;
for (std::deque<std::string>::const_iterator
iter = args.begin();
iter != args.end();++iter)
{
*p++ = const_cast<char*>(iter->c_str());
}
parse(args.size(),pargs);
}
inline void
argstream::parse(int argc,char** argv)
{
// Run thru all arguments.
// * it has -- in front : it is a long name option, if remainder is empty,
// it is an error
// * it has - in front : it is a sequence of short name options, if
// remainder is empty, deactivates option (- will
// now be considered a char).
// * if any other char, or if option was deactivated
// : it is a value. Values are split in parameters
// (immediately follow an option) and pure values.
// Each time a value is parsed, if the previously parsed argument was an
// option, then the option is linked to the value in case of it is a
// option with parameter. The subtle point is that when several options
// are given with short names (ex: -abc equivalent to -a -b -c), the last
// parsed option is -c).
// Since we use map for option, any successive call overides the previous
// one: foo -a -b -a hello is equivalent to foo -b -a hello
// For values it is not true since we might have several times the same
// value.
value_iterator* lastOption = NULL;
for (char** a = argv,**astop=a+argc;++a!=astop;)
{
std::string s(*a);
if (minusActive_ && s[0] == '-')
{
if (s.size() > 1 && s[1] == '-')
{
if (s.size() == 2)
{
minusActive_ = false;
continue;
}
lastOption = &(options_[s.substr(2)] = values_.end());
}
else
{
if (s.size() > 1)
{
// Parse all chars, if it is a minus we have an error
for (std::string::const_iterator cter = s.begin();
++cter != s.end();)
{
if (*cter == '-')
{
isOk_ = false;
std::ostringstream os;
os<<"- in the middle of a switch "<<a;
errors_.push_back(os.str());
break;
}
lastOption = &(options_[std::string(1,*cter)] = values_.end());
}
}
else
{
isOk_ = false;
errors_.push_back("Invalid argument -");
break;
}
}
}
else
{
values_.push_back(s);
if (lastOption != NULL)
{
*lastOption = --values_.end();
}
lastOption = NULL;
}
}
#ifdef ARGSTREAM_DEBUG
for (std::map<std::string,value_iterator>::const_iterator
iter = options_.begin();iter != options_.end();++iter)
{
std::cout<<"DEBUG: option "<<iter->first;
if (iter->second != values_.end())
{
std::cout<<" -> "<<*(iter->second);
}
std::cout<<std::endl;
}
for (std::list<std::string>::const_iterator
iter = values_.begin();iter != values_.end();++iter)
{
std::cout<<"DEBUG: value "<<*iter<<std::endl;
}
#endif // ARGSTREAM_DEBUG
}
inline bool
argstream::isOk() const
{
return isOk_;
}
inline bool
argstream::helpRequested() const
{
return helpRequested_;
}
inline std::string
argstream::usage() const
{
std::ostringstream os;
os<<"usage: "<<progName_<<cmdLine_<<'\n';
unsigned int lmax = 0;
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
if (lmax<iter->first.size()) lmax = iter->first.size();
}
for (std::deque<help_entry>::const_iterator
iter = argHelps_.begin();iter != argHelps_.end();++iter)
{
os<<'\t'<<iter->first<<std::string(lmax-iter->first.size(),' ')
<<" : "<<iter->second<<'\n';
}
return os.str();
}
inline std::string
argstream::errorLog() const
{
std::string s;
for(std::deque<std::string>::const_iterator iter = errors_.begin();
iter != errors_.end();++iter)
{
s += *iter;
s += '\n';
}
return s;
}
inline char
argstream::uniqueLetter()
{
static unsigned int c = 'a';
return c++;
}
template<class T>
argstream&
operator>>(argstream& s,const ValueHolder<T>& v)
{
// Search in the options if there is any such option defined either with a
// short name or a long name. If both are found, only the last one is
// used.
#ifdef ARGSTREAM_DEBUG
std::cout<<"DEBUG: searching "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
if (v.mandatory_)
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " -";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " --";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " value";
}
else
{
if (!v.shortName_.empty())
{
s.cmdLine_ += " [-";
s.cmdLine_ += v.shortName_;
}
else
{
s.cmdLine_ += " [--";
s.cmdLine_ += v.longName_;
}
s.cmdLine_ += " value]";
}
std::map<std::string,argstream::value_iterator>::iterator iter =
s.options_.find(v.shortName_);
if (iter == s.options_.end())
{
iter = s.options_.find(v.longName_);
}
if (iter != s.options_.end())
{
// If we find counterpart for value holder on command line, either it
// has an associated value in which case we assign it, or it has not, in
// which case we have an error.
if (iter->second != s.values_.end())
{
#ifdef ARGSTREAM_DEBUG
std::cout<<"DEBUG: found value "<<*(iter->second)<<std::endl;
#endif
ValueParser<T> p;
*(v.value_) = p(*(iter->second));
// The option and its associated value are removed, the subtle thing
// is that someother options might have this associated value too,
// which we must invalidate.
s.values_.erase(iter->second);
for (std::map<std::string,argstream::value_iterator>::iterator
jter = s.options_.begin();jter != s.options_.end();++jter)
{
if (jter->second == iter->second)
{
jter->second = s.values_.end();
}
}
s.options_.erase(iter);
}
else
{
s.isOk_ = false;
std::ostringstream os;
os<<"No value following switch "<<iter->first
<<" on command line";
s.errors_.push_back(os.str());
}
}
else
{
if (v.mandatory_)
{
s.isOk_ = false;
std::ostringstream os;
os<<"Mandatory parameter ";
if (!v.shortName_.empty()) os<<'-'<<v.shortName_;
if (!v.longName_.empty())
{
if (!v.shortName_.empty()) os<<'/';
os<<"--"<<v.longName_;
}
os<<" missing";
s.errors_.push_back(os.str());
}
}
return s;
}
inline argstream&
operator>>(argstream& s,const OptionHolder& v)
{
// Search in the options if there is any such option defined either with a
// short name or a long name. If both are found, only the last one is
// used.
#ifdef ARGSTREAM_DEBUG
std::cout<<"DEBUG: searching "<<v.shortName_<<" "<<v.longName_<<std::endl;
#endif
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::string c;
if (!v.shortName_.empty())
{
c += " [-";
c += v.shortName_;
}
else
{
c += " [--";
c += v.longName_;
}
c += "]";
s.cmdLine_ = c+s.cmdLine_;
}
std::map<std::string,argstream::value_iterator>::iterator iter =
s.options_.find(v.shortName_);
if (iter == s.options_.end())
{
iter = s.options_.find(v.longName_);
}
if (iter != s.options_.end())
{
// If we find counterpart for value holder on command line then the
// option is true and if an associated value was found, it is ignored
if (v.value_ != NULL)
{
*(v.value_) = true;
}
else
{
s.helpRequested_ = true;
}
// The option only is removed
s.options_.erase(iter);
}
else
{
if (v.value_ != NULL)
{
*(v.value_) = false;
}
else
{
s.helpRequested_ = false;
}
}
return s;
}
template<class T,class O>
argstream&
operator>>(argstream& s,const ValuesHolder<T,O>& v)
{
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
{
std::ostringstream os;
os<<' '<<v.letter_<<'1';
switch (v.len_)
{
case -1:
os<<"...";
break;
case 1:
break;
default:
os<<"..."<<v.letter_<<v.len_;
break;
}
s.cmdLine_ += os.str();
}
std::list<std::string>::iterator first = s.values_.begin();
// We add to the iterator as much values as we can, limited to the length
// specified (if different of -1)
int n = v.len_ != -1?v.len_:s.values_.size();
while (first != s.values_.end() && n-->0)
{
// Read the value from the string *first
ValueParser<T> p;
*(v.value_++) = p(*first );
s.argHelps_.push_back(argstream::help_entry(v.name(),v.description()));
// The value we just removed was maybe "remembered" by an option so we
// remove it now.
for (std::map<std::string,argstream::value_iterator>::iterator
jter = s.options_.begin();jter != s.options_.end();++jter)
{
if (jter->second == first)
{
jter->second = s.values_.end();
}
}
++first;
}
// Check if we have enough values
if (n != 0)
{
s.isOk_ = false;
std::ostringstream os;
os<<"Expecting "<<v.len_<<" values";
s.errors_.push_back(os.str());
}
// Erase the values parsed
s.values_.erase(s.values_.begin(),first);
return s;
}
inline void
argstream::defaultErrorHandling(bool ignoreUnused) const
{
if (helpRequested_)
{
std::cout<<usage();
exit(1);
}
if (!isOk_)
{
std::cerr<<errorLog();
exit(1);
}
if (!ignoreUnused &&
(!values_.empty() || !options_.empty()))
{
std::cerr<<"Unused arguments"<<std::endl;
exit(1);
}
}
};
#endif // ARGSTREAM_H

View File

@ -0,0 +1,51 @@
#include <fenv.h>
#include "Network.h"
#include "NetworkSimulatorGUI.h"
#include <QApplication>
#include <argstream.h>
int main(int argc, char *argv[])
{
feenableexcept(FE_INVALID) ;
feenableexcept(FE_DIVBYZERO) ;
#ifndef DEBUG
try
{
#endif
argstream as(argc,argv) ;
bool show_gui = false;
as >> option('i',"gui",show_gui,"show gui (vs. do the pipeline automatically)")
>> help() ;
as.defaultErrorHandling() ;
// 2 - call the full pipeline
Network network ;
network.initRandom(20,0.2) ;
if(show_gui)
{
QApplication app(argc,argv) ;
NetworkSimulatorGUI pgui(network);
pgui.show() ;
return app.exec() ;
}
return 0 ;
#ifndef DEBUG
}
catch(std::exception& e)
{
std::cerr << "Unhandled exception: " << e.what() << std::endl;
return 1 ;
}
#endif
}

View File

@ -0,0 +1,14 @@
TEMPLATE = app
CONFIG *= qt qglviewer
QT *= xml opengl
INCLUDEPATH *= ../..
TARGET = NetworkSim
SOURCES = Network.cpp main.cpp NetworkViewer.cpp NetworkSimulatorGUI.cpp TurtleRouterStatistics.cpp RsAutoUpdatePage.cpp
HEADERS = Network.h MonitoredTurtle.h NetworkViewer.h NetworkSimulatorGUI.h TurtleRouterStatistics.h RsAutoUpdatePage.h
FORMS = NetworkSimulatorGUI.ui TurtleRouterStatistics.ui
LIBS *= ../../lib/libretroshare.a ../../../../libbitdht/src/lib/libbitdht.a ../../../../openpgpsdk/src/lib/libops.a -lgnome-keyring -lupnp -lssl -lcrypto -lbz2