eclipse_thread.cpp

00001
00002 /***************************************************************************
00003  *  eclipse_thread.cpp - Fawkes Readylog ECLiPSe Thread
00004  *
00005  *  Created: Wed Jul 16 10:42:49 2009
00006  *  Copyright  2009  Daniel Beck
00007  *
00008  ****************************************************************************/
00009
00010 /*  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; either version 2 of the License, or
00013  *  (at your option) any later version.
00014  *
00015  *  This program is distributed in the hope that it will be useful,
00016  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  *  GNU Library General Public License for more details.
00019  *
00020  *  Read the full text in the LICENSE.GPL file in the doc directory.
00021  */
00022
00023 #include "eclipse_thread.h"
00024 #include "externals/fawkes_bb_interface.h"
00025 #include "externals/fawkes_logger.h"
00026
00027 #include <interfaces/TestInterface.h>
00028 #include <core/exception.h>
00029
00030 #include <eclipseclass.h>
00031
00032 #include <cstdlib>
00033 #include <cstring>
00034 #include <vector>
00035
00036 using namespace std;
00037 using namespace fawkes;
00038 
00039 /** @class EclipseAgentThread "eclipse_thread.h"
00040  * This thread creates an ECLiPSe context in which the Readylog
00041  * interpreter and the program are loaded.
00042  * @author Daniel Beck
00043  */
00044
00045 extern "C" int ec_external( dident, int (*) (), dident );
00046
00047 EclipseAgentThread* EclipseAgentThread::m_instance = NULL;
00048 
00049 /** Constructor. */
00050 EclipseAgentThread::EclipseAgentThread()
00051   : Thread( "ECLiPSe thread", fawkes::Thread::OPMODE_CONTINUOUS ),
00052     m_initialized( false )
00053 {
00054   m_instance = this;
00055 }
00056 
00057 /** Destructor. */
00058 EclipseAgentThread::~EclipseAgentThread()
00059 {
00060 }
00061
00062 void
00063 EclipseAgentThread::init()
00064 {
00065   // set ECLiPSe installation directory
00066   char* eclipse_dir = NULL;
00067   try
00068   {
00069     eclipse_dir = strdup( config->get_string( "/readylogagent/eclipse_dir" ).c_str() );
00070     logger->log_info( name(), "Setting ECLIPSEDIR to %s", eclipse_dir );
00071     ec_set_option_ptr( EC_OPTION_ECLIPSEDIR, (void*) eclipse_dir );
00072   }
00073   catch (...)
00074   {
00075     // ignore
00076   }
00077
00078   // initialize ECLiPSe context
00079   if ( 0 != ec_init() )
00080   { throw fawkes::Exception( "Failed to initialize ECLiPSe context" ); }
00081
00082   free( eclipse_dir );
00083
00084   // register external predicates
00085   if ( EC_succeed != ec_external( ec_did( "read_interface",  2 ), p_read_interface,  ec_did( "eclipse", 0 ) ) )
00086   { throw Exception( "Registering external predicate read_interface/2 failed" ); }
00087   if ( EC_succeed != ec_external( ec_did( "write_interface", 2 ), p_write_interface, ec_did( "eclipse", 0 ) ) )
00088   { throw Exception( "Registering external predicate write_interface/2 failed" ); }
00089   if ( EC_succeed != ec_external( ec_did( "send_message",    2 ), p_send_message,    ec_did( "eclipse", 0 ) ) )
00090   { throw Exception( "Registering external predicate send_message/2 failed" ); }
00091   if ( EC_succeed != ec_external( ec_did( "recv_messages",   2 ), p_recv_messages,   ec_did( "eclipse", 0 ) ) )
00092   { throw Exception( "Registering external predicate recv_messages/2 failed" ); }
00093   if ( EC_succeed != ec_external( ec_did( "log",             2 ), p_log,             ec_did( "eclipse", 0 ) ) )
00094   { throw Exception( "Registering external predicate log/2 failed" ); }
00095
00096   m_initialized = true;
00097
00098   // open & register interfaces
00099   try
00100   {
00101     // open for interfaces reading
00102     Configuration::ValueIterator* vit = config->search( "/readylogagent/interfaces/reading" );
00103     while ( vit->next() )
00104     {
00105       if ( vit->is_string() )
00106       {
00107         string s = vit->get_string();
00108         if ( s.find("::") == string::npos )
00109         { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
00110
00111         string iftype = s.substr( 0, s.find( "::" ) );
00112         string ifname = s.substr( s.find( "::" ) + 2 );
00113
00114         logger->log_debug( name(), "Opening interface %s of type %s for reading",
00115                            ifname.c_str(), iftype.c_str() );
00116
00117         Interface* iface = blackboard->open_for_reading( iftype.c_str(), ifname.c_str() );
00118         m_reading_ifaces.push_back( iface );
00119         register_interface( iface );
00120       }
00121     }
00122
00123     // open interfaces for writing
00124     vit = config->search( "/readylogagent/interfaces/writing" );
00125     while ( vit->next() )
00126     {
00127       if ( vit->is_string() )
00128       {
00129         string s = vit->get_string();
00130         if ( s.find("::") == string::npos )
00131         { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
00132
00133         string iftype = s.substr( 0, s.find( "::" ) );
00134         string ifname = s.substr( s.find( "::" ) + 2 );
00135
00136         logger->log_debug( name(), "Opening interface %s of type %s for writing",
00137                            ifname.c_str(), iftype.c_str() );
00138
00139         Interface* iface = blackboard->open_for_writing( iftype.c_str(), ifname.c_str() );
00140         m_writing_ifaces.push_back( iface );
00141         register_interface( iface );
00142       }
00143     }
00144   }
00145   catch ( Exception& e )
00146   {
00147     e.append( "Failed to open interfaces" );
00148     throw e;
00149   }
00150
00151   // load utility predicates
00152   load_file( ECLIPSE_CODE_DIR"/utils/logging.ecl" );
00153
00154   // load interpreter and agent
00155   load_file( ECLIPSE_CODE_DIR"/interpreter/dummy.ecl" );
00156 }
00157
00158 void
00159 EclipseAgentThread::finalize()
00160 {
00161   ec_cleanup();
00162 }
00163
00164 void
00165 EclipseAgentThread::once()
00166 {
00167   post_goal( "run" );
00168   if ( EC_succeed != EC_resume() )
00169   { throw Exception( "Error running agent program" ); }
00170 }
00171 
00172 /** Post an event to the ECLiPSe context.
00173  * @param event the name of the event
00174  */
00175 void
00176 EclipseAgentThread::post_event( const char* event )
00177 {
00178   if ( !m_initialized ) { return; }
00179
00180   // send event to the interpreter
00181   char* atom = strdup( event );
00182   ::post_event( EC_atom( atom ) );
00183   free( atom );
00184 }
00185 
00186 /** Read all registered interfaces. */
00187 void
00188 EclipseAgentThread::read_interfaces()
00189 {
00190   for ( vector< Interface* >::iterator i = m_reading_ifaces.begin();
00191         i != m_reading_ifaces.end();
00192         ++i )
00193   { (*i)->read(); }
00194
00195   for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
00196         i != m_writing_ifaces.end();
00197         ++i )
00198   { (*i)->read(); }
00199 }
00200 
00201 /** Write the registered interface that have been opened for writing. */
00202 void
00203 EclipseAgentThread::write_interfaces()
00204 {
00205   for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
00206         i != m_writing_ifaces.end();
00207         ++i )
00208   { (*i)->write(); }
00209 }
00210 
00211 /** Load a file into the ECLiPSe context.
00212  * @param filename the name of the file
00213  * @return false if the ECLiPSe context hasn't been intialized yet
00214  */
00215 bool
00216 EclipseAgentThread::load_file( const char* filename )
00217 {
00218   if ( !m_initialized )  { return false; }
00219
00220   char* ensure_loaded = strdup( "ensure_loaded" );
00221   post_goal( term( EC_functor( ensure_loaded, 1 ), filename ) );
00222   free( ensure_loaded );
00223
00224   if ( EC_succeed != ec_resume() )
00225   { throw Exception( "File %s could not be loaded", filename ); }
00226
00227   return true;
00228 }
00229 
00230 /** Register an interface for access from within the ECLiPSe context.
00231  * @param interface the interface to register
00232  * @return false if the ECLiPSe context hasn't been intialized yet
00233  */
00234 bool
00235 EclipseAgentThread::register_interface( fawkes::Interface* interface )
00236 {
00237   if ( !m_initialized ) { return false; }
00238
00239   m_registered_interfaces[ string( interface->id() ) ] = interface;
00240
00241
00242   // define structs for interface data ----------------------------------
00243   // data_IntefaceType(field1, field2, ...) -----------------------------
00244
00245   // check whether struct is already defined
00246   char* struct_name;
00247   asprintf( &struct_name, "data_%s", interface->type() );
00248
00249   char* current_struct = strdup( "current_struct" );
00250   post_goal( term( EC_functor( current_struct, 2 ),
00251                    EC_atom( struct_name ),
00252                    newvar() ) );
00253
00254   if ( EC_succeed != ec_resume() )
00255   {
00256     // define named structure
00257     // data_InterfaceType( field1, field2, ... )
00258
00259     vector< string > fields;
00260     for ( InterfaceFieldIterator i = interface->fields();
00261           i != interface->fields_end();
00262           ++i )
00263     { fields.push_back( i.get_name() ); }
00264
00265     EC_word args[ fields.size() ];
00266
00267     for ( size_t i = 0 ; i < fields.size(); ++i )
00268     {
00269       char* c = strdup( fields.at( i ).c_str() );
00270       args[ i ] = EC_atom( c );
00271       free( c );
00272     }
00273
00274     EC_word new_struct = term( EC_functor( struct_name, (int) fields.size() ), args );
00275
00276     char* local = strdup( "local" );
00277     char* strct = strdup( "struct" );
00278     EC_word struct_def = term( EC_functor( strct, 1 ), new_struct );
00279     EC_word struct_def_local = term( EC_functor( local, 1), struct_def );
00280
00281     char* call = strdup( "call" );
00282     // call( struct( data_InterfaceType(field1, field2, ...) ) )
00283     post_goal( term( EC_functor( call, 1 ), struct_def_local ) );
00284
00285     // cleanup
00286     free( local );
00287     free( strct );
00288     free( call );
00289
00290     if ( EC_succeed != ec_resume() )
00291     { throw Exception( "Failed to define structure %s", struct_name ); }
00292   }
00293
00294   free( struct_name );
00295
00296
00297   // define structs for message data ------------------------------------
00298   // data_IntefaceType_MessageType(field1, field2, ...) -----------------
00299
00300   list<const char *> message_types = interface->get_message_types();
00301   for ( list<const char *>::iterator type_iter = message_types.begin();
00302         type_iter != message_types.end();
00303         ++type_iter )
00304   {
00305     // check whether struct is already defined
00306     char* struct_name;
00307     asprintf( &struct_name, "data_%s_%s", interface->type(), *type_iter );
00308
00309     post_goal( term( EC_functor( current_struct, 2 ),
00310                      EC_atom( struct_name ),
00311                      newvar() ) );
00312
00313     if ( EC_succeed != ec_resume() )
00314     {
00315       // define name structure
00316       // data_InterfaceType_MessageType( field1, field2, ... )
00317
00318       Message* msg = interface->create_message( type_iter->c_str() );
00319
00320       vector< string > fields;
00321       for ( InterfaceFieldIterator field_iter = msg->fields();
00322             field_iter != msg->fields_end();
00323             ++field_iter )
00324       {
00325         string name = field_iter.get_name();
00326         fields.push_back( name );
00327       }
00328
00329       delete msg;
00330
00331       EC_word args[ fields.size() ];
00332
00333       for ( size_t i = 0; i < fields.size(); ++i )
00334       {
00335         char* c = strdup( fields.at( i ).c_str() );
00336         args[ i ] = EC_atom( c );
00337         free( c );
00338       }
00339
00340       EC_word new_struct = term( EC_functor( struct_name, (int) fields.size() ), args );
00341       char* local = strdup( "local" );
00342       char* strct = strdup( "struct" );
00343       EC_word struct_def = term( EC_functor( strct, 1 ), new_struct );
00344       EC_word struct_def_local = term( EC_functor( local, 1), struct_def );
00345
00346       char* call = strdup( "call" );
00347       // call( struct( data_InterfaceType_MessageType(field1, field2, ...) ) )
00348       post_goal( term( EC_functor( call, 1 ), struct_def_local ) );
00349
00350       // cleanup
00351       free( local );
00352       free( strct );
00353       free( call );
00354
00355       if ( EC_succeed != ec_resume() )
00356       { throw Exception( "Failed to define structure %s", struct_name ); }
00357     }
00358
00359     free( struct_name );
00360   }
00361
00362   // cleanup
00363   free( current_struct );
00364
00365   return true;
00366 }
00367 
00368 /** Get the registered interface with the given id.
00369  * @param id the interface id
00370  * @return the interface or NULL if no interface with the given id is registerd
00371  */
00372 fawkes::Interface*
00373 EclipseAgentThread::get_registered_interface( const char* id )
00374 {
00375   map< string, fawkes::Interface* >::iterator i = m_registered_interfaces.find( string( id ) );
00376
00377   if ( i == m_registered_interfaces.end() ) { return NULL; }
00378
00379   return i->second;
00380 }
00381 
00382 /** Get the logger.
00383  * @return the logger
00384  */
00385 fawkes::Logger*
00386 EclipseAgentThread::get_logger()
00387 {
00388   return logger;
00389 }
00390 
00391 /** Get the EclipseAgentThread instance.
00392  * @return the instance
00393  */
00394 EclipseAgentThread*
00395 EclipseAgentThread::instance()
00396 {
00397   if ( !m_instance )
00398   { throw Exception( "No instance of type EclipseThread instantiated" ); }
00399
00400   return m_instance;
00401 }