experiments/evorobot/src/evorobotcomponent.cpp

00001 /********************************************************************************
00002  *  FARSA Experiments Library                                                   *
00003  *  Copyright (C) 2007-2012                                                     *
00004  *  Gianluca Massera <emmegian@yahoo.it>                                        *
00005  *  Stefano Nolfi <stefano.nolfi@istc.cnr.it>                                   *
00006  *  Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it>                         *
00007  *                                                                              *
00008  *  This program is free software; you can redistribute it and/or modify        *
00009  *  it under the terms of the GNU General Public License as published by        *
00010  *  the Free Software Foundation; either version 2 of the License, or           *
00011  *  (at your option) any later version.                                         *
00012  *                                                                              *
00013  *  This program is distributed in the hope that it will be useful,             *
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of              *
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               *
00016  *  GNU General Public License for more details.                                *
00017  *                                                                              *
00018  *  You should have received a copy of the GNU General Public License           *
00019  *  along with this program; if not, write to the Free Software                 *
00020  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA  *
00021  ********************************************************************************/
00022 
00023 #include "evorobotcomponent.h"
00024 #include "evorobotviewer.h"
00025 #include "abstracttest.h"
00026 #include "tests.h"
00027 #include "configurationhelper.h"
00028 #include "logger.h"
00029 
00030 namespace farsa {
00031 
00033 class EvolveOperation : public ThreadOperation {
00034 public:
00035     EvolveOperation( Evoga* ga, EvoRobotComponent* evorobot ) :
00036         ga(ga),
00037         evorobot(evorobot) { };
00038     virtual ~EvolveOperation() { };
00040     virtual void stop() {
00041         ga->stop();
00042     };
00044     virtual void run() {
00045         evorobot->setStatus( "In Evolution ..." );
00046         ga->getEvoRobotExperiment()->setActivityPhase( EvoRobotExperiment::INEVOLUTION );
00047         ga->evolveAllReplicas();
00048         QString message;
00049         if ( ga->isStopped() ) {
00050             message = "Evolution Stopped";
00051         } else {
00052             message = "Evolution Finished";
00053         }
00054         // reset the stopped status
00055         ga->resetStop();
00056         evorobot->onThreadOperationDone( message );
00057     };
00058 private:
00059     Evoga* ga;
00060     EvoRobotComponent* evorobot;
00061 };
00062 
00064 class TestOperation : public ThreadOperation {
00065 public:
00066     TestOperation( AbstractTest* test, Evoga* ga, EvoRobotComponent* evorobot ) :
00067         test(test),
00068         ga(ga),
00069         evorobot(evorobot) { };
00070     virtual ~TestOperation() { };
00072     virtual void stop() {
00073         ga->stop();
00074     };
00076     virtual void run() {
00077         evorobot->setStatus( QString("Running ")+test->menuText()+" ..." );
00078         test->runTest();
00079         QString message;
00080         if ( ga->isStopped() ) {
00081             message = "Test Stopped";
00082         } else {
00083             message = "Test Finished";
00084         }
00085         // reset the stopped status
00086         ga->resetStop();
00087         evorobot->onThreadOperationDone( message );
00088     };
00089 private:
00090     AbstractTest* test;
00091     Evoga* ga;
00092     EvoRobotComponent* evorobot;
00093 };
00094 
00095 EvoRobotComponent::EvoRobotComponent() :
00096     Component(),
00097     ga(NULL),
00098     batchRunning(false),
00099     runningOperation(false),
00100     mutex() {
00101     gaThread = new WorkerThread( this );
00102 
00103     connect(gaThread, SIGNAL(exceptionDuringOperation(BaseException*)), this, SLOT(exceptionDuringOperation(BaseException*)), Qt::BlockingQueuedConnection);
00104 }
00105 
00106 EvoRobotComponent::~EvoRobotComponent() {
00107     gaThread->quit();
00108     delete gaThread;
00109     delete ga;
00110 }
00111 
00112 ComponentUI* EvoRobotComponent::getUIManager() {
00113     return new EvoRobotViewer( this );
00114 }
00115 
00116 void EvoRobotComponent::configure(farsa::ConfigurationParameters& params, QString prefix) {
00117     if ( ga ) {
00118         // delete the previous GA
00119         gaThread->quit();
00120         delete gaThread;
00121         delete ga;
00122     }
00123     batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning);
00124     ga = params.getObjectFromGroup<Evoga>( prefix+"GA" );
00125     
00126     // Now we also instantiate all test classes. The configuration file should have subgroups
00127     // [TESTS:NN] where NN is a progressive number starting from zero.
00128     // Each group describe the test (in particular the group must have a type
00129     // parameter corresponding the the type of the Test to run).
00130     // The tests indicated by the following boolean flags are added by default if not present on
00131     // the configuration file
00132     bool testAllAdded = false;
00133     bool testIndividualAdded = false;
00134     bool testRandomAdded = false;
00135     bool testZeroAdded = false;
00136     // clear the old content of currentTestName, it will be configured later
00137     currentTestName = QString();
00138     QStringList testGroups = params.getGroupsWithPrefixList( prefix, "TEST:" );
00139     for( int i=0; i<testGroups.size(); i++ ) {
00140         if ( i==0 ) {
00141             //--- currentTestName is initially setted to contain the name of the first test specified by the user
00142             currentTestName = testGroups[i];
00143         }
00144         AbstractTest* test = params.getObjectFromGroup<AbstractTest>(prefix+testGroups[i]);
00145         test->setComponent( this );
00146         AvailableTestList::addTest( testGroups[i], test );
00147         /*
00148         if (dynamic_cast<TestAll *>(test) != NULL) {
00149             testAllAdded = true;
00150         } else */ if (dynamic_cast<TestIndividual *>(test) != NULL) {
00151             testIndividualAdded = true;
00152         } else if (dynamic_cast<TestRandom *>(test) != NULL) {
00153             testRandomAdded = true;
00154         } /* else if (dynamic_cast<TestZero *>(test) != NULL) {
00155             testZeroAdded = true;
00156         } */
00157     }
00158     /*
00159     if (!testAllAdded) {
00160         // TestAll is not present among tests configured by the user, adding it. We know for sure
00161         // TestAll doesn't need to be configured, so we don't
00162         AbstractTest* test = new TestAll();
00163         test->setComponent( this );
00164         AvailableTestList::addTest("TestAll", test);
00165     }
00166     */
00167     if (!testIndividualAdded) {
00168         // TestBest is not present among tests configured by the user, adding it. We know for sure
00169         // TestBest doesn't need to be configured, so we don't
00170         AbstractTest* test = new TestIndividual();
00171         test->setComponent( this );
00172         AvailableTestList::addTest("TestIndividual", test);
00173     }
00174     if (!testRandomAdded) {
00175         // TestRandom is not present among tests configured by the user, adding it. We know for sure
00176         // TestRnadom doesn't need to be configured, so we don't
00177         AbstractTest* test = new TestRandom();
00178         test->setComponent( this );
00179         AvailableTestList::addTest("TestRandom", test);
00180     }
00181     /*
00182     if(!testZeroAdded) {
00183         // TestZero is not present among tests configured by the user, adding it. We know for sure
00184         // TestZero doesn't need to be configured, so we don't
00185         AbstractTest* test = new TestZero();
00186         test->setComponent( this );
00187         AvailableTestList::addTest("TestZero", test);
00188     }
00189     */
00190     
00191     if ( currentTestName.isEmpty() ) {
00192         // there is no tests specified by the user and then the defaul currentTestName in this
00193         // case will be the automatically added "TestIndividual"
00194         currentTestName = "TestIndividual";
00195     }
00196     currentTestName = ConfigurationHelper::getString( params, prefix+"testToRun", currentTestName );
00197 
00198     // start the innerThread
00199     gaThread->start();
00200     
00201     setStatus( "Configured" );
00202 }
00203 
00204 void EvoRobotComponent::save(farsa::ConfigurationParameters& params, QString prefix) {
00205     ga->save( params, prefix+"/GA" );
00206 }
00207 
00208 void EvoRobotComponent::postConfigureInitialization() {
00209 }
00210 
00211 void EvoRobotComponent::describe( QString type ) {
00212     Descriptor d = addTypeDescription( type, "This component allow to do evolutionary robotics experiments" );
00213     d.describeSubgroup( "GA" ).type( "Evoga" ).props( IsMandatory ).help( "The Genetic Algorithm to use to perform the evolutionary process" );
00214     d.describeSubgroup( "TEST" ).type( "AbstractTest" ).props( AllowMultiple ).help( "let to configure a test condition for the evolved individual" );
00215 }
00216 
00217 Evoga* EvoRobotComponent::getGA() {
00218     return ga;
00219 }
00220 
00221 void EvoRobotComponent::stop() {
00222     gaThread->stopCurrentOperation();
00223 }
00224 
00225 void EvoRobotComponent::evolve()
00226 {
00227     if ( batchRunning ) {
00228         ga->getEvoRobotExperiment()->setActivityPhase( EvoRobotExperiment::INEVOLUTION );
00229         ga->evolveAllReplicas();
00230         ga->resetStop();
00231     } else {
00232         mutex.lock();
00233         if ( runningOperation ) {
00234             mutex.unlock();
00235             Logger::error( "EvoRobotComponent - Cannot run evolve because another action is currently running; please wait until it finish, or stop it before run evolve action" );
00236             return;
00237         }
00238         runningOperation = true;
00239         mutex.unlock();
00240         gaThread->addOperation( new EvolveOperation( ga, this ) );
00241     }
00242 }
00243 
00244 void EvoRobotComponent::runTest() {
00245     AbstractTest* test = AvailableTestList::getTest( currentTestName );
00246     if ( !test ) {
00247         Logger::error( QString("EvoRobotComponent - Test ") + currentTestName + " is not present into the list of available Tests" );
00248         return;
00249     }
00250     if ( batchRunning ) {
00251         test->runTest();
00252         ga->resetStop();
00253     } else {
00254         mutex.lock();
00255         if ( runningOperation ) {
00256             mutex.unlock();
00257             Logger::error( QString("EvoRobotComponent - Cannot run test %1 because another action is currently running; please wait until it finish, or stop it before run test %1").arg(currentTestName) );
00258             return;
00259         }
00260         runningOperation = true;
00261         mutex.unlock();
00262         gaThread->addOperation( new TestOperation( test, ga, this ) );
00263     }
00264 }
00265 
00266 void EvoRobotComponent::runTestFromQAction() {
00267     // get the name of the test from the data into QAction
00268     QAction* action = dynamic_cast<QAction*>( sender() );
00269     if ( !action ) {
00270         Logger::error( "EvoRobotComponent - runTestFromQAction can only be called by a QAction" );
00271         return;
00272     }
00273     currentTestName = action->data().toString();
00274     runTest();
00275 }
00276 
00277 void EvoRobotComponent::exceptionDuringOperation(BaseException *e) {
00278     if (batchRunning) {
00279         return;
00280     }
00281     Logger::error( QString("EvoRobotComponent - Error while executing the current operation, an exception was thrown. Reason: ") + e->what() );
00282 }
00283 
00284 void EvoRobotComponent::onThreadOperationDone( QString message ) {
00285     mutex.lock();
00286     runningOperation = false;
00287     mutex.unlock();
00288     setStatus( message );
00289     emit actionFinished();
00290 }
00291 
00292 } // end namespace farsa