From a597aa43922c4518f91c6bc8fcd01afa61ddb003 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 29 Sep 2012 14:12:57 +0000 Subject: [PATCH] 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 --- .../src/tests/network_simulator/MainLoop.cpp | 24 + .../tests/network_simulator/MonitoredTurtle.h | 15 + .../src/tests/network_simulator/Network.cpp | 161 ++++ .../src/tests/network_simulator/Network.h | 71 ++ .../network_simulator/NetworkSimulatorGUI.cpp | 55 ++ .../network_simulator/NetworkSimulatorGUI.h | 26 + .../network_simulator/NetworkSimulatorGUI.ui | 106 +++ .../tests/network_simulator/NetworkViewer.cpp | 410 +++++++++ .../tests/network_simulator/NetworkViewer.h | 68 ++ .../src/tests/network_simulator/README.txt | 38 + .../network_simulator/RsAutoUpdatePage.cpp | 47 + .../network_simulator/RsAutoUpdatePage.h | 40 + .../TurtleRouterStatistics.cpp | 324 +++++++ .../TurtleRouterStatistics.h | 74 ++ .../TurtleRouterStatistics.ui | 55 ++ .../src/tests/network_simulator/argstream.h | 814 ++++++++++++++++++ .../src/tests/network_simulator/main.cpp | 51 ++ .../src/tests/network_simulator/network.pro | 14 + 18 files changed, 2393 insertions(+) create mode 100644 libretroshare/src/tests/network_simulator/MainLoop.cpp create mode 100644 libretroshare/src/tests/network_simulator/MonitoredTurtle.h create mode 100644 libretroshare/src/tests/network_simulator/Network.cpp create mode 100644 libretroshare/src/tests/network_simulator/Network.h create mode 100644 libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.cpp create mode 100644 libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.h create mode 100644 libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.ui create mode 100644 libretroshare/src/tests/network_simulator/NetworkViewer.cpp create mode 100644 libretroshare/src/tests/network_simulator/NetworkViewer.h create mode 100644 libretroshare/src/tests/network_simulator/README.txt create mode 100644 libretroshare/src/tests/network_simulator/RsAutoUpdatePage.cpp create mode 100644 libretroshare/src/tests/network_simulator/RsAutoUpdatePage.h create mode 100644 libretroshare/src/tests/network_simulator/TurtleRouterStatistics.cpp create mode 100644 libretroshare/src/tests/network_simulator/TurtleRouterStatistics.h create mode 100644 libretroshare/src/tests/network_simulator/TurtleRouterStatistics.ui create mode 100644 libretroshare/src/tests/network_simulator/argstream.h create mode 100644 libretroshare/src/tests/network_simulator/main.cpp create mode 100644 libretroshare/src/tests/network_simulator/network.pro diff --git a/libretroshare/src/tests/network_simulator/MainLoop.cpp b/libretroshare/src/tests/network_simulator/MainLoop.cpp new file mode 100644 index 000000000..22e89127e --- /dev/null +++ b/libretroshare/src/tests/network_simulator/MainLoop.cpp @@ -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;in_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) ; + } +} diff --git a/libretroshare/src/tests/network_simulator/MonitoredTurtle.h b/libretroshare/src/tests/network_simulator/MonitoredTurtle.h new file mode 100644 index 000000000..07e5b5310 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/MonitoredTurtle.h @@ -0,0 +1,15 @@ +#include + +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 ;} +}; diff --git a/libretroshare/src/tests/network_simulator/Network.cpp b/libretroshare/src/tests/network_simulator/Network.cpp new file mode 100644 index 000000000..034dd95e5 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/Network.cpp @@ -0,0 +1,161 @@ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include "Network.h" +#include "MonitoredTurtle.h" + +bool Network::initRandom(uint32_t nb_nodes,float connexion_probability) +{ + _nodes.clear() ; + _neighbors.clear() ; + + std::vector ids ; + + _neighbors.resize(nb_nodes) ; + ids.resize(nb_nodes) ; + + for(uint32_t i=0;i(bytes).toStdString(false) ; + _node_ids[ids[i]] = i ; + } + + // Each node has an exponential law of connectivity to friends. + // + for(uint32_t i=0;i friends ; + for(std::set::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;iPeerId()) ; + 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::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& friends) + : p3LinkMgrIMPL(NULL,NULL),_own_id(own_id),_friends(friends) + { + } + + virtual std::string getOwnId() const { return _own_id ; } + virtual void getOnlineList(std::list& 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 _friends ; +}; + +const RsTurtle *PeerNode::turtle_service() const +{ + return _turtle ; +} + +PeerNode::PeerNode(const std::string& id,const std::list& 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() ; +} diff --git a/libretroshare/src/tests/network_simulator/Network.h b/libretroshare/src/tests/network_simulator/Network.h new file mode 100644 index 000000000..14d10d650 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/Network.h @@ -0,0 +1,71 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +class MonitoredTurtleRouter ; +class RsTurtle ; +class RsRawItem ; +class p3ServiceServer ; + +class PeerNode +{ + public: + PeerNode(const std::string& id,const std::list& 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 Graph +{ + public: + typedef uint32_t NodeId ; + + void symmetric_connect(uint32_t n1,uint32_t n2) ; + const std::set& 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 _nodes ; + std::vector > _neighbors ; +}; + +class Network: public Graph +{ + 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 _node_ids ; +}; + diff --git a/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.cpp b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.cpp new file mode 100644 index 000000000..aa3cd0072 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.cpp @@ -0,0 +1,55 @@ +#include +#include + +#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() ; + +} + diff --git a/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.h b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.h new file mode 100644 index 000000000..09a453e7f --- /dev/null +++ b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.h @@ -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 ; +}; + diff --git a/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.ui b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.ui new file mode 100644 index 000000000..6ac45d8bf --- /dev/null +++ b/libretroshare/src/tests/network_simulator/NetworkSimulatorGUI.ui @@ -0,0 +1,106 @@ + + + NetworkSimulatorGUI + + + + 0 + 0 + 901 + 600 + + + + MainWindow + + + + + + + + + + 0 + 0 + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + flow + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + + + 0 + 0 + 901 + 25 + + + + + File + + + + + + + + + + Load network + + + + + Save network + + + + + + diff --git a/libretroshare/src/tests/network_simulator/NetworkViewer.cpp b/libretroshare/src/tests/network_simulator/NetworkViewer.cpp new file mode 100644 index 000000000..78ccfadc9 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/NetworkViewer.cpp @@ -0,0 +1,410 @@ +#include + +#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& neighs( _network.neighbors(i) ) ; + + for(std::set::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 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=0 && i=0 && j 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& neighbs(_network.neighbors(node_id)) ; + double weight = neighbs.size() + 1 ; + + for(std::set::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); +} + diff --git a/libretroshare/src/tests/network_simulator/NetworkViewer.h b/libretroshare/src/tests/network_simulator/NetworkViewer.h new file mode 100644 index 000000000..98beb8f9a --- /dev/null +++ b/libretroshare/src/tests/network_simulator/NetworkViewer.h @@ -0,0 +1,68 @@ +#include + +// 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 _node_coords ; + std::vector _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 ; +}; diff --git a/libretroshare/src/tests/network_simulator/README.txt b/libretroshare/src/tests/network_simulator/README.txt new file mode 100644 index 000000000..7aaad52e6 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/README.txt @@ -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) + diff --git a/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.cpp b/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.cpp new file mode 100644 index 000000000..9cb849e7f --- /dev/null +++ b/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.cpp @@ -0,0 +1,47 @@ +#include +#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 ; } diff --git a/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.h b/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.h new file mode 100644 index 000000000..ea1b0c8c8 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/RsAutoUpdatePage.h @@ -0,0 +1,40 @@ +#pragma once + +#include +#include + +// 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 ; +}; + diff --git a/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.cpp b/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.cpp new file mode 100644 index 000000000..1b04a0dc2 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.cpp @@ -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 +#include +#include + +#include +#include + +#include +#include +#include "TurtleRouterStatistics.h" + +static const int MAX_TUNNEL_REQUESTS_DISPLAY = 10 ; + +class TRHistogram +{ + public: + TRHistogram(const std::vector& 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 > hits ; + std::map > depths ; + std::map >::iterator it ; + + int max_hits = 1; + int max_depth = 1; + + for(uint32_t i=0;i<_infos.size();++i) + { + std::vector& h(hits[_infos[i].source_peer_id]) ; + std::vector& 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;ifillRect(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;idrawText(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;ifillRect(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;idrawText(ox+i*cellx,oy+(p+1)*celly+4,QString::number(i)) ; + for(int i=0;idrawText(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& _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 > hashes_info ; + std::vector > tunnels_info ; + std::vector search_reqs_info ; + std::vector 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 names ; + + std::map::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 >& /*hashes_info*/, + const std::vector >& /*tunnels_info*/, + const std::vector& search_reqs_info, + const std::vector& 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 +#include +#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 > >&, + const std::vector > >&, + const std::vector&, + const std::vector&, + const RsTurtle *turtle) ; + + private: + static QString speedString(float f) ; + + QPixmap pixmap ; + int maxWidth,maxHeight ; +}; + diff --git a/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.ui b/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.ui new file mode 100644 index 000000000..365d3f362 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/TurtleRouterStatistics.ui @@ -0,0 +1,55 @@ + + + TurtleRouterStatistics + + + + 0 + 0 + 611 + 408 + + + + Router Statistics + + + + :/images/rstray3.png:/images/rstray3.png + + + + + + Qt::Vertical + + + + QFrame::NoFrame + + + Qt::ScrollBarAlwaysOff + + + true + + + + + 0 + 0 + 593 + 390 + + + + + + + + + + + + + diff --git a/libretroshare/src/tests/network_simulator/argstream.h b/libretroshare/src/tests/network_simulator/argstream.h new file mode 100644 index 000000000..137856794 --- /dev/null +++ b/libretroshare/src/tests/network_simulator/argstream.h @@ -0,0 +1,814 @@ +/* Copyright (C) 2004 Xavier Décoret +* + * 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 +#include +#include +#include +#include +#include +#include +#include + +namespace +{ + class argstream; + + template + class ValueHolder; + + template + argstream& operator>> (argstream&, const ValueHolder&); + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of ValueHolder + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + template + 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& 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 + inline ValueHolder + parameter(char s, + const char* l, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(s,l,b,desc,mandatory); + } + template + inline ValueHolder + parameter(char s, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(s,b,desc,mandatory); + } + template + inline ValueHolder + parameter(const char* l, + T& b, + const char* desc="", + bool mandatory = true) + { + return ValueHolder(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 ValuesHolder + { + public: + ValuesHolder(const O& o, + const char* desc, + int len); + template friend argstream& operator>>(argstream& s,const ValuesHolder& 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 + inline ValuesHolder + values(const O& o, + const char* desc="", + int len=-1) + { + return ValuesHolder(o,desc,len); + } + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // Interface of ValueParser + //++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + template + 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 + { + 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 + friend argstream& operator>>(argstream& s,const ValueHolder& v); + friend inline argstream& operator>>(argstream& s,const OptionHolder& v); + template + friend argstream& operator>>(argstream& s,const ValuesHolder& 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::iterator value_iterator; + typedef std::pair help_entry; + std::string progName_; + std::map options_; + std::list values_; + bool minusActive_; + bool isOk_; + std::deque argHelps_; + std::string cmdLine_; + std::deque errors_; + bool helpRequested_; + }; + //************************************************************ + // Implementation of ValueHolder + //************************************************************ + template + ValueHolder::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 + ValueHolder::ValueHolder(const char* l, + T& v, + const char* desc, + bool mandatory) + : longName_(l), + value_(&v), + initialValue_(v), + description_(desc), + mandatory_(mandatory) + { + } + template + ValueHolder::ValueHolder(char s, + T& v, + const char* desc, + bool mandatory) + : shortName_(1,s), + value_(&v), + initialValue_(v), + description_(desc), + mandatory_(mandatory) + { + } + template + std::string + ValueHolder::name() const + { + std::ostringstream os; + if (!shortName_.empty()) os<<'-'< + std::string + ValueHolder::description() const + { + std::ostringstream os; + os< + //************************************************************ + template + ValuesHolder::ValuesHolder(const O& o, + const char* desc, + int len) + : value_(o), + description_(desc), + len_(len) + { + letter_ = argstream::uniqueLetter(); + } + template + std::string + ValuesHolder::name() const + { + std::ostringstream os; + os< + std::string + ValuesHolder::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 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::const_iterator + iter = args.begin(); + iter != args.end();++iter) + { + *p++ = const_cast(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 "<::const_iterator + iter = options_.begin();iter != options_.end();++iter) + { + std::cout<<"DEBUG: option "<first; + if (iter->second != values_.end()) + { + std::cout<<" -> "<<*(iter->second); + } + std::cout<::const_iterator + iter = values_.begin();iter != values_.end();++iter) + { + std::cout<<"DEBUG: value "<<*iter<::const_iterator + iter = argHelps_.begin();iter != argHelps_.end();++iter) + { + if (lmaxfirst.size()) lmax = iter->first.size(); + } + for (std::deque::const_iterator + iter = argHelps_.begin();iter != argHelps_.end();++iter) + { + os<<'\t'<first<first.size(),' ') + <<" : "<second<<'\n'; + } + return os.str(); + } + inline std::string + argstream::errorLog() const + { + std::string s; + for(std::deque::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 + argstream& + operator>>(argstream& s,const ValueHolder& 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 "<::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)< 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::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 "<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<<'-'<>(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 "<::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 + argstream& + operator>>(argstream& s,const ValuesHolder& v) + { + s.argHelps_.push_back(argstream::help_entry(v.name(),v.description())); + { + std::ostringstream os; + os<<' '<::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 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::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 "< + +#include "Network.h" +#include "NetworkSimulatorGUI.h" +#include +#include + +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 +} + diff --git a/libretroshare/src/tests/network_simulator/network.pro b/libretroshare/src/tests/network_simulator/network.pro new file mode 100644 index 000000000..693d880cb --- /dev/null +++ b/libretroshare/src/tests/network_simulator/network.pro @@ -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