Fawkes API  Fawkes Development Version
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 <cstdio>
00033 #include <cstdlib>
00034 #include <cstring>
00035 #include <vector>
00036 
00037 using namespace std;
00038 using namespace fawkes;
00039 
00040 /** @class EclipseAgentThread "eclipse_thread.h"
00041  * This thread creates an ECLiPSe context in which the Readylog
00042  * interpreter and the program are loaded.
00043  * @author Daniel Beck
00044  */
00045 
00046 extern "C" int ec_external( dident, int (*) (), dident );
00047 
00048 EclipseAgentThread* EclipseAgentThread::m_instance = NULL;
00049 
00050 /** Constructor. */
00051 EclipseAgentThread::EclipseAgentThread()
00052   : Thread( "ECLiPSe thread", fawkes::Thread::OPMODE_CONTINUOUS ),
00053     m_initialized( false )
00054 {
00055   m_instance = this;
00056 }
00057 
00058 /** Destructor. */
00059 EclipseAgentThread::~EclipseAgentThread()
00060 {
00061 }
00062 
00063 void
00064 EclipseAgentThread::init()
00065 {
00066   // set ECLiPSe installation directory
00067   char* eclipse_dir = NULL;
00068   try
00069   {
00070     eclipse_dir = strdup( config->get_string( "/readylogagent/eclipse_dir" ).c_str() );
00071     logger->log_info( name(), "Setting ECLIPSEDIR to %s", eclipse_dir );
00072     ec_set_option_ptr( EC_OPTION_ECLIPSEDIR, (void*) eclipse_dir );
00073   }
00074   catch (...)
00075   {
00076     // ignore
00077   }
00078 
00079   // initialize ECLiPSe context
00080   if ( 0 != ec_init() )
00081   { throw fawkes::Exception( "Failed to initialize ECLiPSe context" ); }
00082 
00083   free( eclipse_dir );
00084 
00085   // register external predicates
00086   if ( EC_succeed != ec_external( ec_did( "read_interface",  2 ), p_read_interface,  ec_did( "eclipse", 0 ) ) )
00087   { throw Exception( "Registering external predicate read_interface/2 failed" ); }
00088   if ( EC_succeed != ec_external( ec_did( "write_interface", 2 ), p_write_interface, ec_did( "eclipse", 0 ) ) )
00089   { throw Exception( "Registering external predicate write_interface/2 failed" ); }
00090   if ( EC_succeed != ec_external( ec_did( "send_message",    2 ), p_send_message,    ec_did( "eclipse", 0 ) ) )
00091   { throw Exception( "Registering external predicate send_message/2 failed" ); }
00092   if ( EC_succeed != ec_external( ec_did( "recv_messages",   2 ), p_recv_messages,   ec_did( "eclipse", 0 ) ) )
00093   { throw Exception( "Registering external predicate recv_messages/2 failed" ); }
00094   if ( EC_succeed != ec_external( ec_did( "log",             2 ), p_log,             ec_did( "eclipse", 0 ) ) )
00095   { throw Exception( "Registering external predicate log/2 failed" ); }
00096 
00097   m_initialized = true;
00098 
00099   // open & register interfaces
00100   try
00101   {
00102     // open for interfaces reading
00103     Configuration::ValueIterator* vit = config->search( "/readylogagent/interfaces/reading" );
00104     while ( vit->next() )
00105     {
00106       if ( vit->is_string() )
00107       {
00108         string s = vit->get_string();
00109         if ( s.find("::") == string::npos )
00110         { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
00111 
00112         string iftype = s.substr( 0, s.find( "::" ) );
00113         string ifname = s.substr( s.find( "::" ) + 2 );
00114 
00115         logger->log_debug( name(), "Opening interface %s of type %s for reading",
00116                            ifname.c_str(), iftype.c_str() );
00117 
00118         Interface* iface = blackboard->open_for_reading( iftype.c_str(), ifname.c_str() );
00119         m_reading_ifaces.push_back( iface );
00120         register_interface( iface );
00121       }
00122     }
00123 
00124     // open interfaces for writing
00125     vit = config->search( "/readylogagent/interfaces/writing" );
00126     while ( vit->next() )
00127     {
00128       if ( vit->is_string() )
00129       {
00130         string s = vit->get_string();
00131         if ( s.find("::") == string::npos )
00132         { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
00133 
00134         string iftype = s.substr( 0, s.find( "::" ) );
00135         string ifname = s.substr( s.find( "::" ) + 2 );
00136 
00137         logger->log_debug( name(), "Opening interface %s of type %s for writing",
00138                            ifname.c_str(), iftype.c_str() );
00139 
00140         Interface* iface = blackboard->open_for_writing( iftype.c_str(), ifname.c_str() );
00141         m_writing_ifaces.push_back( iface );
00142         register_interface( iface );
00143       }
00144     }
00145   }
00146   catch ( Exception& e )
00147   {
00148     e.append( "Failed to open interfaces" );
00149     throw e;
00150   }
00151 
00152   // load utility predicates
00153   load_file( ECLIPSE_CODE_DIR"/utils/logging.ecl" );
00154 
00155   // load interpreter and agent
00156   load_file( ECLIPSE_CODE_DIR"/interpreter/dummy.ecl" );
00157 }
00158 
00159 void
00160 EclipseAgentThread::finalize()
00161 {
00162   ec_cleanup();
00163 }
00164 
00165 void
00166 EclipseAgentThread::once()
00167 {
00168   post_goal( "run" );
00169   if ( EC_succeed != EC_resume() )
00170   { throw Exception( "Error running agent program" ); }
00171 }
00172 
00173 /** Post an event to the ECLiPSe context.
00174  * @param event the name of the event
00175  */
00176 void
00177 EclipseAgentThread::post_event( const char* event )
00178 {
00179   if ( !m_initialized ) { return; }
00180 
00181   // send event to the interpreter
00182   char* atom = strdup( event );
00183   ::post_event( EC_atom( atom ) );
00184   free( atom );
00185 }
00186 
00187 /** Read all registered interfaces. */
00188 void
00189 EclipseAgentThread::read_interfaces()
00190 {
00191   for ( vector< Interface* >::iterator i = m_reading_ifaces.begin();
00192         i != m_reading_ifaces.end();
00193         ++i )
00194   { (*i)->read(); }
00195 
00196   for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
00197         i != m_writing_ifaces.end();
00198         ++i )
00199   { (*i)->read(); }
00200 }
00201 
00202 /** Write the registered interface that have been opened for writing. */
00203 void
00204 EclipseAgentThread::write_interfaces()
00205 {
00206   for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
00207         i != m_writing_ifaces.end();
00208         ++i )
00209   { (*i)->write(); }
00210 }
00211 
00212 /** Load a file into the ECLiPSe context.
00213  * @param filename the name of the file
00214  * @return false if the ECLiPSe context hasn't been intialized yet
00215  */
00216 bool
00217 EclipseAgentThread::load_file( const char* filename )
00218 {
00219   if ( !m_initialized )  { return false; }
00220 
00221   char* ensure_loaded = strdup( "ensure_loaded" );
00222   post_goal( term( EC_functor( ensure_loaded, 1 ), filename ) );
00223   free( ensure_loaded );
00224 
00225   if ( EC_succeed != ec_resume() )
00226   { throw Exception( "File %s could not be loaded", filename ); }
00227 
00228   return true;
00229 }
00230 
00231 /** Register an interface for access from within the ECLiPSe context.
00232  * @param interface the interface to register
00233  * @return false if the ECLiPSe context hasn't been intialized yet
00234  */
00235 bool
00236 EclipseAgentThread::register_interface( fawkes::Interface* interface )
00237 {
00238   if ( !m_initialized ) { return false; }
00239 
00240   m_registered_interfaces[ string( interface->id() ) ] = interface;
00241 
00242 
00243   // define structs for interface data ----------------------------------
00244   // data_IntefaceType(field1, field2, ...) -----------------------------
00245   
00246   // check whether struct is already defined
00247   char* struct_name;
00248   asprintf( &struct_name, "data_%s", interface->type() );
00249 
00250   post_goal( term( EC_functor( (char *) "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   std::list<const char *> message_types = interface->get_message_types();
00301   for ( std::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( (char *) "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 );
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       if ( 0 != fields.size() )
00341       {
00342         EC_word new_struct = term( EC_functor( struct_name, (int) fields.size() ), args );
00343         char* local = strdup( "local" );
00344         char* strct = strdup( "struct" );
00345         EC_word struct_def = term( EC_functor( strct, 1 ), new_struct );
00346         EC_word struct_def_local = term( EC_functor( local, 1), struct_def );
00347 
00348         char* call = strdup( "call" );
00349         // call( struct( data_InterfaceType_MessageType(field1, field2, ...) ) )
00350         post_goal( term( EC_functor( call, 1 ), struct_def_local ) );
00351 
00352         // cleanup
00353         free( local );
00354         free( strct );
00355         free( call );
00356       
00357         if ( EC_succeed != ec_resume() )
00358         { throw Exception( "Failed to define structure %s", struct_name ); }
00359       }
00360     }
00361     
00362     free( struct_name );
00363   }
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 }