MSP.cpp
Go to the documentation of this file.
00001 /*********************************************************************
00002 *
00003 * Software License Agreement (BSD License)
00004 *
00005 *  Copyright (c) 2011, ISR University of Coimbra.
00006 *  All rights reserved.
00007 *
00008 *  Redistribution and use in source and binary forms, with or without
00009 *  modification, are permitted provided that the following conditions
00010 *  are met:
00011 *
00012 *   * Redistributions of source code must retain the above copyright
00013 *     notice, this list of conditions and the following disclaimer.
00014 *   * Redistributions in binary form must reproduce the above
00015 *     copyright notice, this list of conditions and the following
00016 *     disclaimer in the documentation and/or other materials provided
00017 *     with the distribution.
00018 *   * Neither the name of the ISR University of Coimbra nor the names of its
00019 *     contributors may be used to endorse or promote products derived
00020 *     from this software without specific prior written permission.
00021 *
00022 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
00023 *  "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
00024 *  LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
00025 *  FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
00026 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
00027 *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
00028 *  BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
00029 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
00030 *  CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
00031 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
00032 *  ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
00033 *  POSSIBILITY OF SUCH DAMAGE.
00034 *
00035 * Author: David Portugal, 2011
00036 *********************************************************************/
00037 
00038 #include <ros/ros.h>
00039 #include <move_base_msgs/MoveBaseAction.h>
00040 #include <actionlib/client/simple_action_client.h>
00041 #include <tf/transform_broadcaster.h>
00042 #include <nav_msgs/Odometry.h>
00043 
00044 #include "getgraph.h"
00045 #include "algorithms.h"
00046 
00047 #define NUM_MAX_ROBOTS 32
00048 
00049 typedef unsigned int uint;
00050 typedef actionlib::SimpleActionClient<move_base_msgs::MoveBaseAction> MoveBaseClient;
00051 
00052 bool ResendGoal; //Send the same goal again (if goal failed...)
00053 bool interference;
00054 bool goal_complete;
00055 bool initialize = true;
00056 bool end_simulation = false;
00057 uint goalvertex;
00058 uint backUpCounter;
00059 
00060 int TEAMSIZE;
00061 int ID_ROBOT;
00062 
00063 double xPos[NUM_MAX_ROBOTS]; //tabelas de posições (atençao ao index pro caso de 1 so robot)
00064 double yPos[NUM_MAX_ROBOTS]; //tabelas de posições (atençao ao index pro caso de 1 so robot)
00065 
00066 ros::Subscriber odom_sub;
00067 ros::Publisher odom_pub;
00068 ros::Subscriber results_sub;
00069 ros::Publisher results_pub;
00070 ros::Publisher cmd_vel_pub;
00071 
00072 
00073 void send_goal_result (uint gvertex){
00074   /*
00075   string frame_id       //robot ID
00076     float64 x           //Goal (X=vertex,0,0)  
00077 */      
00078         printf("Send Vertex %u [Goal] to Results: Robot %d\n",gvertex,ID_ROBOT);        
00079         
00080         geometry_msgs::PointStamped msg;        
00081         char id_robot_str[3];
00082         sprintf(id_robot_str, "%d", ID_ROBOT);  //integer to array
00083                 
00084         msg.header.frame_id = id_robot_str;
00085         msg.point.x = float (goalvertex);
00086         msg.point.y = 0.0; 
00087         msg.point.z = 0.0;
00088 
00089         results_pub.publish(msg);
00090         ros::spinOnce();
00091 }
00092 
00093 void backup(){
00094         
00095         while (backUpCounter<=100){
00096         
00097         if(backUpCounter==0){
00098                 ROS_INFO("The wall is too close! I need to do some backing up...");
00099                 // Move the robot back...
00100                 geometry_msgs::Twist cmd_vel;
00101                 cmd_vel.linear.x = -0.1;
00102                 cmd_vel.angular.z = 0.0;
00103                 cmd_vel_pub.publish(cmd_vel);
00104         }
00105                         
00106         if(backUpCounter==40){
00107                 // Turn the robot around...
00108                 geometry_msgs::Twist cmd_vel;
00109                 cmd_vel.linear.x = 0.0;
00110                 cmd_vel.angular.z = 0.5;
00111                 cmd_vel_pub.publish(cmd_vel);
00112         }
00113                         
00114         if(backUpCounter==100){
00115                 // Stop the robot...
00116                 geometry_msgs::Twist cmd_vel;
00117                 cmd_vel.linear.x = 0.0;
00118                 cmd_vel.angular.z = 0.0;
00119                 cmd_vel_pub.publish(cmd_vel);
00120                         
00121                 ROS_INFO("Done backing up, now on with my life!");              
00122         }
00123 
00124         backUpCounter++;
00125         }
00126         
00127 }
00128 
00129 
00130 void goalDoneCallback(const actionlib::SimpleClientGoalState &state, const move_base_msgs::MoveBaseResultConstPtr &result){ //goal terminado (completo ou cancelado)
00131 //      ROS_INFO("Goal is complete (suceeded, aborted or cancelled).");
00132         // If the goal succeeded send a new one!
00133         //if(state.state_ == actionlib::SimpleClientGoalState::SUCCEEDED) sendNewGoal = true;
00134         // If it was aborted time to back up!
00135         //if(state.state_ == actionlib::SimpleClientGoalState::ABORTED) needToBackUp = true;    
00136         
00137         if(state.state_ == actionlib::SimpleClientGoalState::SUCCEEDED){
00138                 ROS_INFO("SUCCESS");
00139                 goal_complete = true;
00140                 send_goal_result (goalvertex);
00141 //              printf("goal complete = %d\n",goal_complete);
00142         }else{
00143                 ROS_INFO("CANCELLED or ABORTED...Resend Goal!");        //tentar voltar a enviar goal...
00144                 backUpCounter = 0;
00145                 backup();               
00146                 ResendGoal = true;
00147         }
00148 }
00149 
00150 void goalActiveCallback(){      //enquanto o robot esta a andar para o goal...
00151         goal_complete = false;
00152 //      ROS_INFO("Goal is active.");
00153 }
00154 
00155 void odomCB(const nav_msgs::Odometry::ConstPtr& msg) { //colocar propria posicao na tabela
00156         
00157 //      printf("Colocar Propria posição na tabela, ID_ROBOT = %d\n",ID_ROBOT);
00158         int idx = ID_ROBOT;
00159         
00160         if (ID_ROBOT<=-1){
00161                 idx = 0;
00162         }
00163         
00164         xPos[idx]=msg->pose.pose.position.x;
00165         yPos[idx]=msg->pose.pose.position.y;
00166         
00167 //      printf("Posicao colocada em Pos[%d]\n",idx);
00168 }
00169 
00170 void positionsCB(const nav_msgs::Odometry::ConstPtr& msg) {     //construir tabelas de posições
00171         
00172 //     printf("Construir tabela de posicoes (receber posicoes), ID_ROBOT = %d\n",ID_ROBOT);    
00173         
00174     char id[20]; //identificador do robot q enviou a msg d posição...
00175     strcpy( id, msg->header.frame_id.c_str() );
00176     //int stamp = msg->header.seq;
00177 //     printf("robot q mandou msg = %s\n", id);
00178     
00179     // Build Positions Table
00180     
00181     if (ID_ROBOT>-1){
00182         //verify id "XX" of robot: (string: "robot_XX/odom")
00183         
00184         char str_idx[4];
00185         uint i;
00186         
00187         for (i=6; i<10; i++){
00188                 if (id[i]=='/'){
00189                         str_idx[i-6] = '\0';
00190                         break;
00191                 }else{
00192                         str_idx[i-6] = id[i];
00193                 }
00194         }
00195         
00196         int idx = atoi (str_idx);
00197 //      printf("id robot q mandou msg = %d\n",idx);
00198         
00199         if (idx >= TEAMSIZE && TEAMSIZE <= NUM_MAX_ROBOTS){
00200                 //update teamsize:
00201                 TEAMSIZE = idx+1;
00202         }
00203         
00204         if (ID_ROBOT != idx){  //Ignore own positions   
00205                 xPos[idx]=msg->pose.pose.position.x;
00206                 yPos[idx]=msg->pose.pose.position.y;            
00207         }       
00208 //      printf ("Position Table:\n frame.id = %s\n id_robot = %d\n xPos[%d] = %f\n yPos[%d] = %f\n\n", id, idx, idx, xPos[idx], idx, yPos[idx] );           
00209     }
00210 }
00211 
00212 
00213 void resultsCB(const geometry_msgs::PointStamped::ConstPtr& msg) { //
00214         
00215         if(initialize){ //em espera que o monitor a desbloquei e passe initialize para false
00216                 
00217 //              printf("Is it the monitor? frame_id = %s (%f,%f,%f)\n",msg->header.frame_id.c_str(),msg->point.x,msg->point.y,msg->point.z);
00218                 
00219                 if (msg->header.frame_id == "monitor"){
00220                         printf("Let's Patrol!\n");
00221                         initialize = false;
00222                 }
00223         }else {
00224                 if (msg->header.frame_id == "monitor" && msg->point.x == 1.0 && msg->point.y == 1.0 && msg->point.z == 1.0){
00225                         printf("The simulation is over. Let's leave\n");
00226                         end_simulation = true;
00227                 }               
00228         }
00229 }
00230 
00231 void initialize_node (){
00232   /*
00233   string frame_id       //robot ID
00234     float64 z           //initialization (0,0,Z=ID)  
00235 */      
00236         printf("Initialize Node: Robot %d\n",ID_ROBOT); 
00237         ros::Rate loop_rate(1); //1 segundo
00238         
00239         geometry_msgs::PointStamped msg;        
00240         
00241         char id_robot_str[3];
00242         sprintf(id_robot_str, "%d", ID_ROBOT);  //integer to array
00243                 
00244         msg.header.frame_id = id_robot_str;
00245         msg.point.x = 0.0;
00246         msg.point.y = 0.0; 
00247         msg.point.z = (float) ID_ROBOT; //Z = ID
00248         
00249         int count = 0;
00250         while (count<2){
00251                 results_pub.publish(msg);
00252                 ros::spinOnce();
00253                 //printf("publiquei msg.\n");
00254                 loop_rate.sleep();
00255                 count++;
00256         }       
00257 }
00258 
00259 void send_interference(){
00260   /*
00261   string frame_id       //robot ID
00262     float64 y           //Interference (0,Y=1,0)  
00263 */      
00264         printf("Send Interference: Robot %d\n",ID_ROBOT);       
00265         //ros::Rate loop_rate(1); //1 segundo
00266         
00267         geometry_msgs::PointStamped msg;        
00268         char id_robot_str[3];
00269         sprintf(id_robot_str, "%d", ID_ROBOT);  //integer to array
00270                 
00271         msg.header.frame_id = id_robot_str;
00272         msg.point.x = 0.0;
00273         msg.point.y = 1.0; 
00274         msg.point.z = 0.0;
00275 
00276         results_pub.publish(msg);
00277         ros::spinOnce();
00278 }
00279 
00280 bool check_interference (void){ //verificar se os robots estao proximos
00281         
00282         int i;
00283         double dist_quad;
00284         
00285         /* Poderei usar TEAMSIZE para afinar */
00286         for (i=0; i<ID_ROBOT; i++){     //percorrer vizinhos (assim asseguro q cada interferencia é so encontrada 1 vez)
00287                 
00288                 dist_quad = (xPos[i] - xPos[ID_ROBOT])*(xPos[i] - xPos[ID_ROBOT]) + (yPos[i] - yPos[ID_ROBOT])*(yPos[i] - yPos[ID_ROBOT]);
00289                 
00290                 if (dist_quad <= /*sqrt*/4){    //robots are 2 meter or less apart
00291 //                      ROS_INFO("Feedback: Robots are very close. INTERFERENCE! Dist_Quad = %f", dist_quad);
00292                         interference = true;
00293                         return interference;
00294                 }               
00295         }
00296         
00297         interference = false;
00298         return interference;
00299         
00300 }
00301 
00302 void goalFeedbackCallback(const move_base_msgs::MoveBaseFeedbackConstPtr &feedback){    //publicar posições
00303 
00304 //      printf("ID_ROBOT = %d [feedback callback]\n", ID_ROBOT);
00305         
00306         //Publish Position to common node:
00307         nav_msgs::Odometry msg; 
00308         
00309         int idx = ID_ROBOT;
00310                 
00311         if (ID_ROBOT <= -1){
00312                 msg.header.frame_id = "odom";    //identificador do robot q publicou
00313                 idx = 0;
00314         }else{
00315                 char string[20];
00316                 strcpy (string,"robot_"); 
00317                 char id[3];
00318                 itoa(ID_ROBOT, id, 10);  
00319                 strcat(string,id);
00320                 strcat(string,"/odom"); //string = "robot_X/odom"       
00321                 //strcpy( msg.header.frame_id, string );
00322                 msg.header.frame_id = string;
00323         }
00324         
00325         msg.pose.pose.position.x = xPos[idx]; //send odometry.x
00326         msg.pose.pose.position.y = yPos[idx]; //send odometry.y
00327         
00328         odom_pub.publish(msg);
00329         ros::spinOnce();
00330         
00331         //ROS_INFO("[POSITION PUBLISHED]: ID = %s, X = %f, Y = %f", msg.header.frame_id.c_str(), xPos[idx], yPos[idx]);
00332 //      printf("ID_ROBOT = %d\n", ID_ROBOT);
00333 //      printf("TEAMSIZE = %d\n", TEAMSIZE);    
00334         check_interference();   
00335 }
00336 
00337 int main(int argc, char** argv){        //pass the .graph file to open
00338  /*
00339   argc=3
00340   argv[0]=/.../patrolling_sim/bin/MSP
00341   argv[1]=__name:=XXXXXX
00342   argv[2]=maps/1r-5-map.graph
00343   argv[3]=MSP/1r5-map_1_0
00344   argv[4]=ID_ROBOT
00345   */
00346   
00347   //More than One robot (ID between 0 and 99)
00348   if ( atoi(argv[4])>NUM_MAX_ROBOTS || atoi(argv[4])<-1 ){
00349     ROS_INFO("The Robot's ID must be an integer number between 0 an 99"); //max 100 robots 
00350     return 0;
00351   }else{
00352     ID_ROBOT = atoi(argv[4]); 
00353     //printf("ID_ROBOT = %d\n",ID_ROBOT); //-1 in the case there is only 1 robot.
00354   }
00355 
00356   uint i = strlen( argv[2] );
00357   char graph_file[i];
00358   strcpy (graph_file,argv[2]);
00359   
00360   //Check Graph Dimension:
00361   uint dimension = GetGraphDimension(graph_file);
00362   
00363   //Create Structure to save the Graph Info;
00364   vertex vertex_web[dimension];
00365   
00366   //Get the Graph info from the Graph File
00367   GetGraphInfo(vertex_web, dimension, graph_file);
00368   
00369   uint j;
00370   
00371   
00372   i = strlen( argv[3] );
00373   char msp_file[i];
00374   strcpy (msp_file,argv[3]);
00375   
00376 //   printf("MSP_FILE = %s\n",msp_file);    
00377   
00378   
00379   /* Output Graph Data */
00380   for (i=0;i<dimension;i++){
00381     printf ("ID= %u\n", vertex_web[i].id);
00382     printf ("X= %f, Y= %f\n", vertex_web[i].x, vertex_web[i].y);
00383     printf ("#Neigh= %u\n", vertex_web[i].num_neigh);
00384         
00385     for (j=0;j<vertex_web[i].num_neigh; j++){
00386       printf("\tID = %u, DIR = %s, COST = %u\n", vertex_web[i].id_neigh[j], vertex_web[i].dir[j], vertex_web[i].cost[j]);
00387     }
00388     
00389     printf("\n");       
00390   }
00391   
00392   
00393   
00394   /* Define Starting Vertex/Position (Launch File Parameters) */
00395 
00396   ros::init(argc, argv, "msp");
00397   ros::NodeHandle nh;
00398   double initial_x, initial_y;
00399   
00400   XmlRpc::XmlRpcValue list;
00401   nh.getParam("initial_pos", list);
00402   ROS_ASSERT(list.getType() == XmlRpc::XmlRpcValue::TypeArray);
00403   
00404   int value = ID_ROBOT;
00405   if (value == -1){value = 0;}
00406   
00407   ROS_ASSERT(list[2*value].getType() == XmlRpc::XmlRpcValue::TypeDouble);
00408   initial_x = static_cast<double>(list[2*value]);
00409   
00410   ROS_ASSERT(list[2*value+1].getType() == XmlRpc::XmlRpcValue::TypeDouble);
00411   initial_y = static_cast<double>(list[2*value+1]);
00412  
00413 //    printf("x=%f, y=%f\n", initial_x, initial_y);
00414   uint current_vertex = IdentifyVertex(vertex_web, dimension, initial_x, initial_y);
00415 //    printf("v=%d\n",current_vertex);
00416   
00417   //Publicar dados de "odom" para nó de posições
00418   odom_pub = nh.advertise<nav_msgs::Odometry>("positions", 1); //only concerned about the most recent
00419         
00420   //Subscrever posições de outros robots
00421   odom_sub = nh.subscribe("positions", 10, positionsCB);  
00422   
00423   char string[20];
00424   char string2[20];
00425   
00426   if(ID_ROBOT==-1){ 
00427     strcpy (string,"odom"); //string = "odom"
00428     strcpy (string2,"cmd_vel"); //string = "cmd_vel"
00429     TEAMSIZE = 1;
00430   }else{ 
00431     strcpy (string,"robot_"); 
00432     strcpy (string2,"robot_"); 
00433     char id[3];
00434     itoa(ID_ROBOT, id, 10);  
00435     strcat(string,id);
00436     strcat(string2,id);
00437     strcat(string,"/odom"); //string = "robot_X/odom"  
00438     strcat(string2,"/cmd_vel"); //string = "robot_X/cmd_vel"
00439     TEAMSIZE = ID_ROBOT + 1;
00440   }       
00441   
00442 //   printf("string de publicação da odometria: %s\n",string);
00443   
00444    //Cmd_vel to backup:
00445    cmd_vel_pub  = nh.advertise<geometry_msgs::Twist>(string2, 1);  
00446   
00447   //Subscrever para obter dados de "odom" do robot corrente
00448   ros::Subscriber sub;
00449   sub = nh.subscribe(string, 1, odomCB); //size of the buffer = 1 (?)
00450   ros::spinOnce();    
00451     
00452   
00453   // Define Goal
00454   
00455   if(ID_ROBOT==-1){ 
00456     strcpy (string,"move_base"); //string = "move_base"  
00457   }else{ 
00458     strcpy (string,"robot_"); 
00459     char id[3];
00460     itoa(ID_ROBOT, id, 10);  
00461     strcat(string,id);
00462     strcat(string,"/move_base"); //string = "robot_X/move_base"  
00463   }
00464   
00465   //printf("string = %s",string);
00466   MoveBaseClient ac(string, true); 
00467   
00468   //wait for the action server to come up
00469   while(!ac.waitForServer(ros::Duration(5.0))){
00470      ROS_INFO("Waiting for the move_base action server to come up");
00471   }  
00472 
00473   //Define Goal:
00474   move_base_msgs::MoveBaseGoal goal;
00475   
00476   //Publicar dados para "results"
00477   results_pub = nh.advertise<geometry_msgs::PointStamped>("results", 1); //only concerned about the most recent
00478   results_sub = nh.subscribe("results", 10, resultsCB); //Subscrever "results" vindo dos robots
00479   
00480   initialize_node(); //dizer q está vivo
00481   ros::Rate loop_rate(1); //1 segundo
00482   
00483   /* Wait until all nodes are ready.. */
00484   while(initialize){
00485         ros::spinOnce();
00486         loop_rate.sleep();
00487   }  
00488   
00489    /* Run Algorithm (Get All Vertices from file and follow the route) */
00490   uint next_vertex;
00491  
00492   //Check Route Dimension:
00493   uint route_dimension = get_MSP_dimension(msp_file);
00494   
00495   //Create Structure to save the Route Info;
00496   uint route [route_dimension];
00497   
00498   //Get the Route info from the MSP Route File
00499   get_MSP_route (route, route_dimension, msp_file);
00500   
00501   printf("\nFinal Path: ");
00502   for(i=0; i<route_dimension; i++){
00503         if(i==route_dimension-1){ printf("%i\n", route[i]); }else{ printf("%i, ", route[i]); }
00504   }
00505   printf("Number of elements = %i\n", route_dimension);  
00506   
00507   if(route_dimension>1){ i=1; next_vertex = route[i]; }  
00508   
00509   interference = false;
00510   ResendGoal = false;
00511   goal_complete = true;
00512     
00513   
00514   while(1){
00515           
00516         if(goal_complete){  
00517                 //printf("Move Robot to Vertex %d (%f,%f)\n", next_vertex, vertex_web[next_vertex].x, vertex_web[next_vertex].y);
00518                 
00519                 //Send the goal to the robot (Global Map)
00520                 geometry_msgs::Quaternion angle_quat = tf::createQuaternionMsgFromYaw(0.0);
00521                 goal.target_pose.header.frame_id = "map"; 
00522                 goal.target_pose.header.stamp = ros::Time::now();    
00523                 goal.target_pose.pose.position.x = vertex_web[next_vertex].x;
00524                 goal.target_pose.pose.position.y = vertex_web[next_vertex].y;  
00525                 goal.target_pose.pose.orientation = angle_quat;
00526                 ROS_INFO("Sending goal - Vertex %d (%f,%f)", next_vertex, vertex_web[next_vertex].x, vertex_web[next_vertex].y);
00527                 goalvertex = next_vertex;
00528                 ac.sendGoal(goal, boost::bind(&goalDoneCallback, _1, _2), boost::bind(&goalActiveCallback), boost::bind(&goalFeedbackCallback, _1));
00529                 //ac.waitForResult();
00530                 
00531                 current_vertex = next_vertex;
00532                 i++;
00533                 if ( i>=route_dimension ){ i=1;}
00534                 next_vertex = route[i];    
00535                 goal_complete = false;
00536         }else{
00537                 if (interference){
00538                                                         
00539                         // Stop the robot..                     
00540                         ROS_INFO("Interference detected!\n");   
00541                         send_interference();
00542 
00543                         //get own "odom" positions...
00544                         ros::spinOnce();                
00545                                                 
00546                         //Waiting until conflict is solved...
00547                         while(interference){
00548                                 interference = check_interference();
00549                                 if (goal_complete || ResendGoal){
00550                                         interference = false;
00551                                 }
00552                         }
00553                         // se saiu é pq interference = false                   
00554                 }           
00555                 
00556                 if(ResendGoal){
00557                         //Send the goal to the robot (Global Map)
00558                         geometry_msgs::Quaternion angle_quat = tf::createQuaternionMsgFromYaw(0.0);         
00559                         goal.target_pose.header.frame_id = "map"; 
00560                         goal.target_pose.header.stamp = ros::Time::now();    
00561                         goal.target_pose.pose.position.x = vertex_web[current_vertex].x;
00562                         goal.target_pose.pose.position.y = vertex_web[current_vertex].y;  
00563                         goal.target_pose.pose.orientation = angle_quat; //alpha -> orientação  (queria optimizar este parametro -> através da direcção do vizinho!)
00564                         ROS_INFO("Sending goal - Vertex %d (%f,%f)\n", current_vertex, vertex_web[current_vertex].x, vertex_web[current_vertex].y);
00565                         goalvertex = current_vertex;
00566                         ac.sendGoal(goal, boost::bind(&goalDoneCallback, _1, _2), boost::bind(&goalActiveCallback), boost::bind(&goalFeedbackCallback, _1));
00567                         ResendGoal = false; //para nao voltar a entrar (envia goal so uma vez)
00568                 }  
00569                 
00570                 if(end_simulation){
00571                         return 0;
00572                 }               
00573         }   
00574   }  
00575   return 0; 
00576 }


patrolling_sim
Author(s): David Portugal
autogenerated on Mon Jan 6 2014 11:26:29