evorobotcomponent.cpp
1 /********************************************************************************
2  * FARSA Experiments Library *
3  * Copyright (C) 2007-2012 *
4  * Gianluca Massera <emmegian@yahoo.it> *
5  * Stefano Nolfi <stefano.nolfi@istc.cnr.it> *
6  * Tomassino Ferrauto <tomassino.ferrauto@istc.cnr.it> *
7  * *
8  * This program is free software; you can redistribute it and/or modify *
9  * it under the terms of the GNU General Public License as published by *
10  * the Free Software Foundation; either version 2 of the License, or *
11  * (at your option) any later version. *
12  * *
13  * This program is distributed in the hope that it will be useful, *
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
16  * GNU General Public License for more details. *
17  * *
18  * You should have received a copy of the GNU General Public License *
19  * along with this program; if not, write to the Free Software *
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA *
21  ********************************************************************************/
22 
23 #include "evorobotcomponent.h"
24 #include "evorobotviewer.h"
25 #include "abstracttest.h"
26 #include "tests.h"
27 #include "configurationhelper.h"
28 #include "logger.h"
29 
30 namespace farsa {
31 
34 public:
35  EvolveOperation( Evoga* ga, EvoRobotComponent* evorobot ) :
36  ga(ga),
37  evorobot(evorobot) { };
38  virtual ~EvolveOperation() { };
40  virtual void stop() {
41  ga->stop();
42  };
44  virtual void run() {
45  evorobot->setStatus( "In Evolution ..." );
46  ga->getEvoRobotExperiment()->setActivityPhase( EvoRobotExperiment::INEVOLUTION );
47  ga->evolveAllReplicas();
48  QString message;
49  if ( ga->isStopped() ) {
50  message = "Evolution Stopped";
51  } else {
52  message = "Evolution Finished";
53  }
54  // reset the stopped status
55  ga->resetStop();
56  evorobot->onThreadOperationDone( message );
57  };
58 private:
59  Evoga* ga;
60  EvoRobotComponent* evorobot;
61 };
62 
65 public:
66  TestOperation( AbstractTest* test, Evoga* ga, EvoRobotComponent* evorobot ) :
67  test(test),
68  ga(ga),
69  evorobot(evorobot) { };
70  virtual ~TestOperation() { };
72  virtual void stop() {
73  ga->stop();
74  };
76  virtual void run() {
77  evorobot->setStatus( QString("Running ")+test->menuText()+" ..." );
78  test->runTest();
79  QString message;
80  if ( ga->isStopped() ) {
81  message = "Test Stopped";
82  } else {
83  message = "Test Finished";
84  }
85  // reset the stopped status
86  ga->resetStop();
87  evorobot->onThreadOperationDone( message );
88  };
89 private:
90  AbstractTest* test;
91  Evoga* ga;
92  EvoRobotComponent* evorobot;
93 };
94 
96  : Component()
97  , ga(NULL)
98  , batchRunning(false)
99  , runningOperation(false)
100  , mutex()
101 {
102  gaThread = new WorkerThread( this );
103 
104  connect(gaThread, SIGNAL(exceptionDuringOperation(farsa::BaseException*)), this, SLOT(exceptionDuringOperation(farsa::BaseException*)), Qt::BlockingQueuedConnection);
105 }
106 
108  gaThread->quit();
109  delete gaThread;
110  delete ga;
111 }
112 
114  return new EvoRobotViewer( this );
115 }
116 
118  if ( ga ) {
119  // delete the previous GA
120  gaThread->quit();
121  delete gaThread;
122  delete ga;
123  }
124  batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning);
125  ga = params.getObjectFromGroup<Evoga>( prefix+"GA" );
126 
127  // Now we also instantiate all test classes. The configuration file should have subgroups
128  // [TESTS:NN] where NN is a progressive number starting from zero.
129  // Each group describe the test (in particular the group must have a type
130  // parameter corresponding the the type of the Test to run).
131  // The tests indicated by the following boolean flags are added by default if not present on
132  // the configuration file
133 // bool testAllAdded = false;
134  bool testIndividualAdded = false;
135  bool testRandomAdded = false;
136 // bool testZeroAdded = false;
137  bool testCurrentAdded = false;
138  // clear the old content of currentTestName, it will be configured later
139  currentTestName = QString();
140  QStringList testGroups = params.getGroupsWithPrefixList( prefix, "TEST:" );
141  for( int i=0; i<testGroups.size(); i++ ) {
142  if ( i==0 ) {
143  //--- currentTestName is initially setted to contain the name of the first test specified by the user
144  currentTestName = testGroups[i];
145  }
146  AbstractTest* test = params.getObjectFromGroup<AbstractTest>(prefix+testGroups[i]);
147  test->setComponent( this );
148  AvailableTestList::addTest( testGroups[i], test );
149  /*
150  if (dynamic_cast<TestAll *>(test) != NULL) {
151  testAllAdded = true;
152  } else */ if (dynamic_cast<TestIndividual *>(test) != NULL) {
153  testIndividualAdded = true;
154  } else if (dynamic_cast<TestRandom *>(test) != NULL) {
155  testRandomAdded = true;
156  } else if (dynamic_cast<TestCurrent *>(test) != NULL) {
157  testCurrentAdded = true;
158  } /* else if (dynamic_cast<TestZero *>(test) != NULL) {
159  testZeroAdded = true;
160  } */
161  }
162  /*
163  if (!testAllAdded) {
164  // TestAll is not present among tests configured by the user, adding it. We know for sure
165  // TestAll doesn't need to be configured, so we don't
166  AbstractTest* test = new TestAll();
167  test->setComponent( this );
168  AvailableTestList::addTest("TestAll", test);
169  }
170  */
171  if (!testIndividualAdded) {
172  // TestBest is not present among tests configured by the user, adding it. We know for sure
173  // TestBest doesn't need to be configured, so we don't
174  AbstractTest* test = new TestIndividual();
175  test->setComponent( this );
176  AvailableTestList::addTest("TestIndividual", test);
177  }
178  if (!testRandomAdded) {
179  // TestRandom is not present among tests configured by the user, adding it. We know for sure
180  // TestRnadom doesn't need to be configured, so we don't
181  AbstractTest* test = new TestRandom();
182  test->setComponent( this );
183  AvailableTestList::addTest("TestRandom", test);
184  }
185  if (!testCurrentAdded) {
186  // TestRandom is not present among tests configured by the user, adding it. We know for sure
187  // TestRnadom doesn't need to be configured, so we don't
188  AbstractTest* test = new TestCurrent();
189  test->setComponent( this );
190  AvailableTestList::addTest("TestCurrent", test);
191  }
192  /*
193  if(!testZeroAdded) {
194  // TestZero is not present among tests configured by the user, adding it. We know for sure
195  // TestZero doesn't need to be configured, so we don't
196  AbstractTest* test = new TestZero();
197  test->setComponent( this );
198  AvailableTestList::addTest("TestZero", test);
199  }
200  */
201 
202  if ( currentTestName.isEmpty() ) {
203  // there is no tests specified by the user and then the defaul currentTestName in this
204  // case will be the automatically added "TestIndividual"
205  currentTestName = "TestIndividual";
206  }
207  currentTestName = ConfigurationHelper::getString( params, prefix+"testToRun", currentTestName );
208 
209  // start the innerThread
210  gaThread->start();
211 
212  setStatus( "Configured" );
213 }
214 
216  ga->save( params, prefix+"/GA" );
217 }
218 
220 }
221 
223 {
224  stop();
225 }
226 
227 void EvoRobotComponent::describe( QString type ) {
228  Descriptor d = addTypeDescription( type, "This component allow to do evolutionary robotics experiments" );
229  d.describeSubgroup( "GA" ).type( "Evoga" ).props( IsMandatory ).help( "The Genetic Algorithm to use to perform the evolutionary process" );
230  d.describeSubgroup( "TEST" ).type( "AbstractTest" ).props( AllowMultiple ).help( "let to configure a test condition for the evolved individual" );
231 }
232 
234  return ga;
235 }
236 
238  gaThread->stopCurrentOperation(false);
239 }
240 
242 {
243  if ( batchRunning ) {
244  ga->getEvoRobotExperiment()->setActivityPhase( EvoRobotExperiment::INEVOLUTION );
245  ga->evolveAllReplicas();
246  ga->resetStop();
247  } else {
248  mutex.lock();
249  if ( runningOperation ) {
250  mutex.unlock();
251  Logger::error( "EvoRobotComponent - Cannot run evolve because another action is currently running; please wait until it finish, or stop it before run evolve action" );
252  return;
253  }
254  runningOperation = true;
255  mutex.unlock();
256  gaThread->addOperation( new EvolveOperation( ga, this ) );
257  }
258 }
259 
261  AbstractTest* test = AvailableTestList::getTest( currentTestName );
262  if ( !test ) {
263  Logger::error( QString("EvoRobotComponent - Test ") + currentTestName + " is not present into the list of available Tests" );
264  return;
265  }
266  if ( batchRunning ) {
267  test->runTest();
268  ga->resetStop();
269  } else {
270  mutex.lock();
271  if ( runningOperation ) {
272  mutex.unlock();
273  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) );
274  return;
275  }
276  runningOperation = true;
277  mutex.unlock();
278  gaThread->addOperation( new TestOperation( test, ga, this ) );
279  }
280 }
281 
283  // get the name of the test from the data into QAction
284  QAction* action = dynamic_cast<QAction*>( sender() );
285  if ( !action ) {
286  Logger::error( "EvoRobotComponent - runTestFromQAction can only be called by a QAction" );
287  return;
288  }
289  currentTestName = action->data().toString();
290  runTest();
291 }
292 
294  // This is never called in batch, exception are catched in total99 main
295  Logger::error( QString("EvoRobotComponent - Error while executing the current operation, an exception was thrown. Reason: ") + e->what() );
296 }
297 
299  mutex.lock();
300  runningOperation = false;
301  mutex.unlock();
302  setStatus( message );
303  emit actionFinished();
304 }
305 
306 } // end namespace farsa