worldsim/src/worldcontroller.cpp

00001 /********************************************************************************
00002  *  WorldSim -- library for robot simulations                                   *
00003  *  Copyright (C) 2008-2011 Gianluca Massera <emmegian@yahoo.it>                *
00004  *                                                                              *
00005  *  This program is free software; you can redistribute it and/or modify        *
00006  *  it under the terms of the GNU General Public License as published by        *
00007  *  the Free Software Foundation; either version 2 of the License, or           *
00008  *  (at your option) any later version.                                         *
00009  *                                                                              *
00010  *  This program is distributed in the hope that it will be useful,             *
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00013  *  GNU General Public License for more details.                                *
00014  *                                                                              *
00015  *  You should have received a copy of the GNU General Public License           *
00016  *  along with this program; if not, write to the Free Software                 *
00017  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00018  ********************************************************************************/
00019 
00020 #ifdef FARSA_USE_YARP_AND_ICUB
00021 
00022 #include "worldcontroller.h"
00023 #include "phybox.h"
00024 #include "physphere.h"
00025 #include "phycylinder.h"
00026 #include "wcamera.h"
00027 #include "phyicub.h"
00028 #include <QCoreApplication>
00029 #include <QTimerEvent>
00030 
00031 using namespace yarp::os;
00032 
00033 namespace farsa {
00034 
00035 ServerWorldController::ServerWorldController( WorldController* ctrl ) : QThread() {
00036     wctrl = ctrl;
00037     //--- list of commands
00038     asyn_cmds["start"] = &ServerWorldController::startCmd;
00039     asyn_cmds["pause"] = &ServerWorldController::pauseCmd;
00040     asyn_cmds["stop"] = &ServerWorldController::stopCmd;
00041     asyn_cmds["advance"] = &ServerWorldController::advanceCmd;
00042     //--- Sync Commands
00043     syn_cmds["mk"] = &ServerWorldController::makeCmd;
00044     syn_cmds["set"] = &ServerWorldController::setCmd;
00045     setTerminationEnabled( true );
00046 }
00047 
00048 ServerWorldController::~ServerWorldController() {
00049     //--- nothing to do
00050 }
00051 
00052 void ServerWorldController::applyQueuedCommands() {
00053     Port* port = wctrl->outPort();
00054     while( !queue_cmds.isEmpty() ) {
00055         Bottle cmd = queue_cmds.dequeue();
00056         Bottle res;
00057         QString cmd0( cmd.get(0).toString().c_str() );
00058         //--- we are sure that there is an entry into sync_cmds
00059         //--- because it was checked into run loop
00060         res = (this->*(syn_cmds[cmd0]))( cmd );
00061         port->write(res);
00062     }
00063 }
00064 
00065 void ServerWorldController::run() {
00066     Port* inport = wctrl->inPort();
00067     Port* outport = wctrl->outPort();
00068     while( true ) {
00069         Bottle cmd;
00070         Bottle res;
00071         //--- read the command... and will reply on outPort
00072         inport->read( cmd, false );
00073         //--- checking the command
00074         QString cmd0( cmd.get(0).asString().c_str() );
00075         if ( asyn_cmds.contains( cmd0 ) ) {
00076             res = (this->*(asyn_cmds[cmd0]))( cmd );
00077         } else if ( syn_cmds.contains( cmd0 ) ) {
00078             res.addString( "Command Queued" );
00079             queue_cmds.enqueue( cmd );
00080         } else {
00081             res.addString( "Unsupported Command: " );
00082             res.addString( cmd.get(0).toString() );
00083         }
00084         //--- reply
00085         outport->write(res);
00086     }
00087 }
00088 
00089 yarp::os::Bottle ServerWorldController::startCmd( const yarp::os::Bottle& ) {
00090     QCoreApplication::postEvent( wctrl->world(), new QEvent( (QEvent::Type)(World::E_Play) ) );
00091     Bottle res;
00092     res.addString("World Running");
00093     return res;
00094 }
00095 
00096 yarp::os::Bottle ServerWorldController::pauseCmd( const yarp::os::Bottle& ) {
00097     QCoreApplication::postEvent( wctrl->world(), new QEvent( (QEvent::Type)(World::E_Pause) ) );
00098     Bottle res;
00099     res.addString("World Paused");
00100     return res;
00101 }
00102 
00103 yarp::os::Bottle ServerWorldController::stopCmd( const yarp::os::Bottle& ) {
00104     QCoreApplication::postEvent( wctrl->world(), new QEvent( (QEvent::Type)(World::E_Stop) ) );
00105     Bottle res;
00106     res.addString("World Stopped");
00107     return res;
00108 }
00109 
00110 yarp::os::Bottle ServerWorldController::advanceCmd( const yarp::os::Bottle& ) {
00111     Bottle res;
00112     if ( wctrl->world()->status() == World::playingS ) {
00113         res.addString("World is currently running, pause it before use advance command");
00114     } else {
00115         QCoreApplication::postEvent( wctrl->world(), new QEvent( (QEvent::Type)(World::E_Advance) ) );
00116         res.addString("World Advanced");
00117     }
00118     return res;
00119 }
00120 
00121 yarp::os::Bottle ServerWorldController::makeCmd( const yarp::os::Bottle& cmd ) {
00122     Bottle res;
00123     // --- switch over the type of object to create
00124     QString objtype = cmd.get(1).toString().c_str();
00125     //--- The rotation will be computed on the basis of Pitch-Yaw-Roll rotations about
00126     //    the given euler angles (ZYX) convention
00127     //--- there are many variants of the "mk" command depending on the second parameter:
00128     //   mk box name x_dim y_dim z_dim x_pos y_pos z_pos x_rot y_rot z_rot
00129     //   mk sphere name radius x_pos y_pos z_pos x_rot y_rot z_rot
00130     //   mk cylinder name radius length x_pos y_pos z_pos x_rot y_rot z_rot
00131     //   mk icub name x_pos y_pos z_pos x_rot y_rot z_rot
00132     //   mk camera name x_pos y_pos z_pos x_rot y_rot z_rot width heigth
00133     // ------- following not yet implemented commands
00134     //   mk fixed name obj_parent obj_child
00135     //   mk hinge name obj_parent obj_child axis centre startAngle
00136     if ( objtype == "box" ) {
00137         // --- create a box
00138         real x_dim = 1.0;
00139         real y_dim = 1.0;
00140         real z_dim = 1.0;
00141         wMatrix tm = wMatrix::identity();
00142         Value v = cmd.get(2);
00143         if ( v.isNull() ) {
00144             res.addString( "You must specify a name for the object" );
00145             return res;
00146         }
00147         QString name = v.toString().c_str();
00148         x_dim = cmd.get(3).asDouble();
00149         y_dim = cmd.get(4).asDouble();
00150         z_dim = cmd.get(5).asDouble();
00151         if ( x_dim == 0 || y_dim == 0 || z_dim == 0 ) {
00152             res.addString( "Dimension of the object must be greater than zero" );
00153             return res;
00154         }
00155         wVector pos( cmd.get(6).asDouble(), cmd.get(7).asDouble(), cmd.get(8).asDouble() );
00156         tm = tm * wMatrix::pitch( toRad( cmd.get(9).asDouble() ) );
00157         tm = tm * wMatrix::yaw( toRad( cmd.get(10).asDouble() ) );
00158         tm = tm * wMatrix::roll( toRad( cmd.get(11).asDouble() ) );
00159         tm.w_pos = pos;
00160         new PhyBox( x_dim, y_dim, z_dim, wctrl->world(), name, tm );
00161     } else if ( objtype == "sphere" ) {
00162         // --- create a sphere
00163         real radius = 1.0;
00164         wMatrix tm = wMatrix::identity();
00165         Value v = cmd.get(2);
00166         if ( v.isNull() ) {
00167             res.addString( "You must specify a name for the object" );
00168             return res;
00169         }
00170         QString name = v.toString().c_str();
00171         radius = cmd.get(3).asDouble();
00172         if ( radius == 0 ) {
00173             res.addString( "Dimension of the object must be greater than zero" );
00174             return res;
00175         }
00176         wVector pos( cmd.get(4).asDouble(), cmd.get(5).asDouble(), cmd.get(6).asDouble() );
00177         tm = tm * wMatrix::pitch( toRad( cmd.get(7).asDouble() ) );
00178         tm = tm * wMatrix::yaw( toRad( cmd.get(8).asDouble() ) );
00179         tm = tm * wMatrix::roll( toRad( cmd.get(9).asDouble() ) );
00180         tm.w_pos = pos;
00181         new PhySphere( radius, wctrl->world(), name, tm );
00182     } else if ( objtype == "cylinder" ) {
00183         // --- create a cylinder
00184         real radius = 1.0;
00185         real len = 1.0;
00186         wMatrix tm = wMatrix::identity();
00187         Value v = cmd.get(2);
00188         if ( v.isNull() ) {
00189             res.addString( "You must specify a name for the object" );
00190             return res;
00191         }
00192         QString name = v.toString().c_str();
00193         radius = cmd.get(3).asDouble();
00194         len = cmd.get(4).asDouble();
00195         if ( radius == 0 || len == 0 ) {
00196             res.addString( "Dimension of the object must be greater than zero" );
00197             return res;
00198         }
00199         wVector pos( cmd.get(5).asDouble(), cmd.get(6).asDouble(), cmd.get(7).asDouble() );
00200         tm = tm * wMatrix::pitch( toRad(cmd.get(8).asDouble()) );
00201         tm = tm * wMatrix::yaw( toRad(cmd.get(9).asDouble()) );
00202         tm = tm * wMatrix::roll( toRad(cmd.get(10).asDouble()) );
00203         tm.w_pos = pos;
00204         new PhyCylinder( radius, len, wctrl->world(), name, tm );
00205     } else if ( objtype == "icub" ) {
00206         // --- create a iCub
00207         wMatrix tm = wMatrix::identity();
00208         Value v = cmd.get(2);
00209         if ( v.isNull() ) {
00210             res.addString( "You must specify a name for the object" );
00211             return res;
00212         }
00213         QString name = v.toString().c_str();
00214         wVector pos( cmd.get(3).asDouble(), cmd.get(4).asDouble(), cmd.get(5).asDouble() );
00215         tm = tm * wMatrix::pitch( toRad(cmd.get(6).asDouble()) );
00216         tm = tm * wMatrix::yaw( toRad(cmd.get(7).asDouble()) );
00217         tm = tm * wMatrix::roll( toRad(cmd.get(8).asDouble()) );
00218         tm.w_pos = pos;
00219         PhyiCub* icub = new PhyiCub( wctrl->world(), name, tm );
00220         icub->blockTorso0( true );
00221     } else if ( objtype == "camera" ) {
00222         // --- create a camera
00223         wMatrix tm = wMatrix::identity();
00224         Value v = cmd.get(2);
00225         if ( v.isNull() ) {
00226             res.addString( "You must specify a name for the object" );
00227             return res;
00228         }
00229         QString name = v.toString().c_str();
00230         wVector pos( cmd.get(3).asDouble(), cmd.get(4).asDouble(), cmd.get(5).asDouble() );
00231         tm = tm * wMatrix::pitch( toRad(cmd.get(6).asDouble()) );
00232         tm = tm * wMatrix::yaw( toRad(cmd.get(7).asDouble()) );
00233         tm = tm * wMatrix::roll( toRad(cmd.get(8).asDouble()) );
00234         tm.w_pos = pos;
00235         unsigned int width = cmd.get(9).asInt();
00236         unsigned int height = cmd.get(10).asInt();
00237         if ( width == 0 || height == 0 ) {
00238             res.addString( "Dimension of the frame must be greater than zero" );
00239             return res;
00240         }
00241         new WCamera( wctrl->world(), name, width, height, tm );
00242     }
00243     res.addString("Make Command executed");
00244     return res;
00245 }
00246 
00247 yarp::os::Bottle ServerWorldController::setCmd( const yarp::os::Bottle& cmd ) {
00248     // --- the set command is quite complex but essentialy the structure is the following
00249     //     set [object_name] [property_name] [parameters]
00250     //     if the object_name is "world" then it configure the world's properties
00251     //     property_name specify what you want to change and it can be one of the following:
00252     //  position : and three number of the new position of the object
00253     //  rotation : and three number specifing angle of rotation about X, Y and Z axes
00254     //     Only in the case of "world" there are the following property
00255     //  size     : dimension of the world
00256     //  timestep : time interval of integration step
00257     //  threads  : if more than one activate the multi-thread capability
00258     //  realtime : on or off
00259     QString objname = cmd.get(1).toString().c_str();
00260     if ( objname == "world" ) {
00261         return setWorldCmd( cmd );
00262     } else {
00263         WObject* obj = wctrl->world()->getObject( objname );
00264         if ( obj != NULL ) {
00265             return setObjectCmd( cmd, obj );
00266         }
00267     }
00268     Bottle res;
00269     res.addString( "Object not found" );
00270     return res;
00271 }
00272 
00273 yarp::os::Bottle ServerWorldController::setWorldCmd( const yarp::os::Bottle& cmd ) {
00274     //--- see setCmd comments for understand the behaviour
00275     Bottle res;
00276     QString propname = cmd.get(2).toString().c_str();
00277     if ( propname == "size" ) {
00278         real x_dim = fabs( cmd.get(3).asDouble()/2.0 );
00279         real y_dim = fabs( cmd.get(4).asDouble()/2.0 );
00280         real z_dim = fabs( cmd.get(5).asDouble() );
00281         if ( x_dim == 0 || y_dim == 0 || z_dim == 0 ) {
00282             res.addString( "Dimension of the world must be greater than zero" );
00283             return res;
00284         }
00285         wctrl->world()->setSize( wVector( -x_dim, -y_dim, 0 ), wVector( +x_dim, +y_dim, +z_dim ) );
00286         res.addString( "World Re-Sized" );
00287     } else if ( propname == "timestep" ) {
00288         real newtimestep = fabs( cmd.get(3).asDouble() );
00289         if ( newtimestep == 0 ) {
00290             res.addString( "TimeStep of the world must be greater than zero" );
00291             return res;
00292         }
00293         wctrl->world()->setTimeStep( newtimestep );
00294         res.addString( "New TimeStep setted" );
00295     } else if ( propname == "threads" ) {
00296         real numThreads = fabs( (float)(cmd.get(3).asInt()) );
00297         if ( numThreads == 0 ) {
00298             res.addString( "Threads of the world must be greater than zero" );
00299             return res;
00300         }
00301         wctrl->world()->setMultiThread( numThreads );
00302         res.addString( "Multi-Thread setted" );
00303     } else if ( propname == "realtime" ) {
00304         bool ison = ( QString(cmd.get(3).toString().c_str()) == "on" );
00305         wctrl->world()->setIsRealTime( ison );
00306         if ( ison ) {
00307             res.addString( "RealTime Activated" );
00308         } else {
00309             res.addString( "RealTime De-Activated" );
00310         }
00311     }
00312     return res;
00313 }
00314 
00315 yarp::os::Bottle ServerWorldController::setObjectCmd( const yarp::os::Bottle& /*cmd*/, WObject* /*object*/ ) {
00316     //--- see setCmd comments for understand the behaviour
00317     Bottle res;
00318     res.addString( "Not Yet Implemented" );
00319     return res;
00320 }
00321 
00322 WorldController::WorldController( World* w, QString name )
00323     : YarpObject( w, name ) {
00324     inport = new Port();
00325     outport = new Port();
00326     inport->open( QString( "/%1/%2:i" ).arg( world()->name() ).arg( name ).toAscii().data() );
00327     outport->open( QString( "/%1/%2:o" ).arg( world()->name() ).arg( name ).toAscii().data() );
00328     w->pushObject( this );
00329     srv = new ServerWorldController( this );
00330     srv->start();
00331 }
00332 
00333 WorldController::~WorldController() {
00334     srv->terminate();
00335     inport->close();
00336     outport->close();
00337     delete srv;
00338     //--- this delete blocks MAC application
00339     //delete inport;
00340     //delete outport;
00341 }
00342 
00343 void WorldController::update() {
00344     srv->applyQueuedCommands();
00345 }
00346 
00347 } // end namespace farsa
00348 
00349 #endif //FARSA_USE_YARP_AND_ICUB