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