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  gaThread = new WorkerThread( this );
102 
103  connect(gaThread, SIGNAL(exceptionDuringOperation(BaseException*)), this, SLOT(exceptionDuringOperation(BaseException*)), Qt::BlockingQueuedConnection);
104 }
105 
107  gaThread->quit();
108  delete gaThread;
109  delete ga;
110 }
111 
113  return new EvoRobotViewer( this );
114 }
115 
117  if ( ga ) {
118  // delete the previous GA
119  gaThread->quit();
120  delete gaThread;
121  delete ga;
122  }
123  batchRunning = ConfigurationHelper::getBool(params, "__INTERNAL__/BatchRunning", batchRunning);
124  ga = params.getObjectFromGroup<Evoga>( prefix+"GA" );
125 
126  // Now we also instantiate all test classes. The configuration file should have subgroups
127  // [TESTS:NN] where NN is a progressive number starting from zero.
128  // Each group describe the test (in particular the group must have a type
129  // parameter corresponding the the type of the Test to run).
130  // The tests indicated by the following boolean flags are added by default if not present on
131  // the configuration file
132  bool testAllAdded = false;
133  bool testIndividualAdded = false;
134  bool testRandomAdded = false;
135  bool testZeroAdded = false;
136  // clear the old content of currentTestName, it will be configured later
137  currentTestName = QString();
138  QStringList testGroups = params.getGroupsWithPrefixList( prefix, "TEST:" );
139  for( int i=0; i<testGroups.size(); i++ ) {
140  if ( i==0 ) {
141  //--- currentTestName is initially setted to contain the name of the first test specified by the user
142  currentTestName = testGroups[i];
143  }
144  AbstractTest* test = params.getObjectFromGroup<AbstractTest>(prefix+testGroups[i]);
145  test->setComponent( this );
146  AvailableTestList::addTest( testGroups[i], test );
147  /*
148  if (dynamic_cast<TestAll *>(test) != NULL) {
149  testAllAdded = true;
150  } else */ if (dynamic_cast<TestIndividual *>(test) != NULL) {
151  testIndividualAdded = true;
152  } else if (dynamic_cast<TestRandom *>(test) != NULL) {
153  testRandomAdded = true;
154  } /* else if (dynamic_cast<TestZero *>(test) != NULL) {
155  testZeroAdded = true;
156  } */
157  }
158  /*
159  if (!testAllAdded) {
160  // TestAll is not present among tests configured by the user, adding it. We know for sure
161  // TestAll doesn't need to be configured, so we don't
162  AbstractTest* test = new TestAll();
163  test->setComponent( this );
164  AvailableTestList::addTest("TestAll", test);
165  }
166  */
167  if (!testIndividualAdded) {
168  // TestBest is not present among tests configured by the user, adding it. We know for sure
169  // TestBest doesn't need to be configured, so we don't
170  AbstractTest* test = new TestIndividual();
171  test->setComponent( this );
172  AvailableTestList::addTest("TestIndividual", test);
173  }
174  if (!testRandomAdded) {
175  // TestRandom is not present among tests configured by the user, adding it. We know for sure
176  // TestRnadom doesn't need to be configured, so we don't
177  AbstractTest* test = new TestRandom();
178  test->setComponent( this );
179  AvailableTestList::addTest("TestRandom", test);
180  }
181  /*
182  if(!testZeroAdded) {
183  // TestZero is not present among tests configured by the user, adding it. We know for sure
184  // TestZero doesn't need to be configured, so we don't
185  AbstractTest* test = new TestZero();
186  test->setComponent( this );
187  AvailableTestList::addTest("TestZero", test);
188  }
189  */
190 
191  if ( currentTestName.isEmpty() ) {
192  // there is no tests specified by the user and then the defaul currentTestName in this
193  // case will be the automatically added "TestIndividual"
194  currentTestName = "TestIndividual";
195  }
196  currentTestName = ConfigurationHelper::getString( params, prefix+"testToRun", currentTestName );
197 
198  // start the innerThread
199  gaThread->start();
200 
201  setStatus( "Configured" );
202 }
203 
205  ga->save( params, prefix+"/GA" );
206 }
207 
209 }
210 
211 void EvoRobotComponent::describe( QString type ) {
212  Descriptor d = addTypeDescription( type, "This component allow to do evolutionary robotics experiments" );
213  d.describeSubgroup( "GA" ).type( "Evoga" ).props( IsMandatory ).help( "The Genetic Algorithm to use to perform the evolutionary process" );
214  d.describeSubgroup( "TEST" ).type( "AbstractTest" ).props( AllowMultiple ).help( "let to configure a test condition for the evolved individual" );
215 }
216 
218  return ga;
219 }
220 
222  gaThread->stopCurrentOperation();
223 }
224 
226 {
227  if ( batchRunning ) {
228  ga->getEvoRobotExperiment()->setActivityPhase( EvoRobotExperiment::INEVOLUTION );
229  ga->evolveAllReplicas();
230  ga->resetStop();
231  } else {
232  mutex.lock();
233  if ( runningOperation ) {
234  mutex.unlock();
235  Logger::error( "EvoRobotComponent - Cannot run evolve because another action is currently running; please wait until it finish, or stop it before run evolve action" );
236  return;
237  }
238  runningOperation = true;
239  mutex.unlock();
240  gaThread->addOperation( new EvolveOperation( ga, this ) );
241  }
242 }
243 
245  AbstractTest* test = AvailableTestList::getTest( currentTestName );
246  if ( !test ) {
247  Logger::error( QString("EvoRobotComponent - Test ") + currentTestName + " is not present into the list of available Tests" );
248  return;
249  }
250  if ( batchRunning ) {
251  test->runTest();
252  ga->resetStop();
253  } else {
254  mutex.lock();
255  if ( runningOperation ) {
256  mutex.unlock();
257  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) );
258  return;
259  }
260  runningOperation = true;
261  mutex.unlock();
262  gaThread->addOperation( new TestOperation( test, ga, this ) );
263  }
264 }
265 
267  // get the name of the test from the data into QAction
268  QAction* action = dynamic_cast<QAction*>( sender() );
269  if ( !action ) {
270  Logger::error( "EvoRobotComponent - runTestFromQAction can only be called by a QAction" );
271  return;
272  }
273  currentTestName = action->data().toString();
274  runTest();
275 }
276 
278  if (batchRunning) {
279  return;
280  }
281  Logger::error( QString("EvoRobotComponent - Error while executing the current operation, an exception was thrown. Reason: ") + e->what() );
282 }
283 
285  mutex.lock();
286  runningOperation = false;
287  mutex.unlock();
288  setStatus( message );
289  emit actionFinished();
290 }
291 
292 } // end namespace farsa