Fawkes API  Fawkes Development Version
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
eclipse_thread.cpp
1 
2 /***************************************************************************
3  * eclipse_thread.cpp - Fawkes Readylog ECLiPSe Thread
4  *
5  * Created: Wed Jul 16 10:42:49 2009
6  * Copyright 2009 Daniel Beck
7  *
8  ****************************************************************************/
9 
10 /* This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18  * GNU Library General Public License for more details.
19  *
20  * Read the full text in the LICENSE.GPL file in the doc directory.
21  */
22 
23 #include "eclipse_thread.h"
24 #include "externals/fawkes_bb_interface.h"
25 #include "externals/fawkes_logger.h"
26 
27 #include <interfaces/TestInterface.h>
28 #include <core/exception.h>
29 
30 #include <eclipseclass.h>
31 
32 #include <cstdio>
33 #include <cstdlib>
34 #include <cstring>
35 #include <vector>
36 
37 using namespace std;
38 using namespace fawkes;
39 
40 /** @class EclipseAgentThread "eclipse_thread.h"
41  * This thread creates an ECLiPSe context in which the Readylog
42  * interpreter and the program are loaded.
43  * @author Daniel Beck
44  */
45 
46 extern "C" int ec_external( dident, int (*) (), dident );
47 
48 EclipseAgentThread* EclipseAgentThread::m_instance = NULL;
49 
50 /** Constructor. */
52  : Thread( "ECLiPSe thread", fawkes::Thread::OPMODE_CONTINUOUS ),
53  m_initialized( false )
54 {
55  m_instance = this;
56 }
57 
58 /** Destructor. */
60 {
61 }
62 
63 void
65 {
66  // set ECLiPSe installation directory
67  char* eclipse_dir = NULL;
68  try
69  {
70  eclipse_dir = strdup( config->get_string( "/readylogagent/eclipse_dir" ).c_str() );
71  logger->log_info( name(), "Setting ECLIPSEDIR to %s", eclipse_dir );
72  ec_set_option_ptr( EC_OPTION_ECLIPSEDIR, (void*) eclipse_dir );
73  }
74  catch (...)
75  {
76  // ignore
77  }
78 
79  // initialize ECLiPSe context
80  if ( 0 != ec_init() )
81  { throw fawkes::Exception( "Failed to initialize ECLiPSe context" ); }
82 
83  free( eclipse_dir );
84 
85  // register external predicates
86  if ( EC_succeed != ec_external( ec_did( "read_interface", 2 ), p_read_interface, ec_did( "eclipse", 0 ) ) )
87  { throw Exception( "Registering external predicate read_interface/2 failed" ); }
88  if ( EC_succeed != ec_external( ec_did( "write_interface", 2 ), p_write_interface, ec_did( "eclipse", 0 ) ) )
89  { throw Exception( "Registering external predicate write_interface/2 failed" ); }
90  if ( EC_succeed != ec_external( ec_did( "send_message", 2 ), p_send_message, ec_did( "eclipse", 0 ) ) )
91  { throw Exception( "Registering external predicate send_message/2 failed" ); }
92  if ( EC_succeed != ec_external( ec_did( "recv_messages", 2 ), p_recv_messages, ec_did( "eclipse", 0 ) ) )
93  { throw Exception( "Registering external predicate recv_messages/2 failed" ); }
94  if ( EC_succeed != ec_external( ec_did( "log", 2 ), p_log, ec_did( "eclipse", 0 ) ) )
95  { throw Exception( "Registering external predicate log/2 failed" ); }
96 
97  m_initialized = true;
98 
99  // open & register interfaces
100  try
101  {
102  // open for interfaces reading
103  Configuration::ValueIterator* vit = config->search( "/readylogagent/interfaces/reading" );
104  while ( vit->next() )
105  {
106  if ( vit->is_string() )
107  {
108  string s = vit->get_string();
109  if ( s.find("::") == string::npos )
110  { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
111 
112  string iftype = s.substr( 0, s.find( "::" ) );
113  string ifname = s.substr( s.find( "::" ) + 2 );
114 
115  logger->log_debug( name(), "Opening interface %s of type %s for reading",
116  ifname.c_str(), iftype.c_str() );
117 
118  Interface* iface = blackboard->open_for_reading( iftype.c_str(), ifname.c_str() );
119  m_reading_ifaces.push_back( iface );
120  register_interface( iface );
121  }
122  }
123 
124  // open interfaces for writing
125  vit = config->search( "/readylogagent/interfaces/writing" );
126  while ( vit->next() )
127  {
128  if ( vit->is_string() )
129  {
130  string s = vit->get_string();
131  if ( s.find("::") == string::npos )
132  { throw Exception( "Not a valid interface id: %s", s.c_str() ); }
133 
134  string iftype = s.substr( 0, s.find( "::" ) );
135  string ifname = s.substr( s.find( "::" ) + 2 );
136 
137  logger->log_debug( name(), "Opening interface %s of type %s for writing",
138  ifname.c_str(), iftype.c_str() );
139 
140  Interface* iface = blackboard->open_for_writing( iftype.c_str(), ifname.c_str() );
141  m_writing_ifaces.push_back( iface );
142  register_interface( iface );
143  }
144  }
145  }
146  catch ( Exception& e )
147  {
148  e.append( "Failed to open interfaces" );
149  throw e;
150  }
151 
152  // load utility predicates
153  load_file( ECLIPSE_CODE_DIR"/utils/logging.ecl" );
154 
155  // load interpreter and agent
156  load_file( ECLIPSE_CODE_DIR"/interpreter/dummy.ecl" );
157 }
158 
159 void
161 {
162  ec_cleanup();
163 }
164 
165 void
167 {
168  post_goal( "run" );
169  if ( EC_succeed != EC_resume() )
170  { throw Exception( "Error running agent program" ); }
171 }
172 
173 /** Post an event to the ECLiPSe context.
174  * @param event the name of the event
175  */
176 void
177 EclipseAgentThread::post_event( const char* event )
178 {
179  if ( !m_initialized ) { return; }
180 
181  // send event to the interpreter
182  char* atom = strdup( event );
183  ::post_event( EC_atom( atom ) );
184  free( atom );
185 }
186 
187 /** Read all registered interfaces. */
188 void
190 {
191  for ( vector< Interface* >::iterator i = m_reading_ifaces.begin();
192  i != m_reading_ifaces.end();
193  ++i )
194  { (*i)->read(); }
195 
196  for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
197  i != m_writing_ifaces.end();
198  ++i )
199  { (*i)->read(); }
200 }
201 
202 /** Write the registered interface that have been opened for writing. */
203 void
205 {
206  for ( vector< Interface* >::iterator i = m_writing_ifaces.begin();
207  i != m_writing_ifaces.end();
208  ++i )
209  { (*i)->write(); }
210 }
211 
212 /** Load a file into the ECLiPSe context.
213  * @param filename the name of the file
214  * @return false if the ECLiPSe context hasn't been intialized yet
215  */
216 bool
217 EclipseAgentThread::load_file( const char* filename )
218 {
219  if ( !m_initialized ) { return false; }
220 
221  char* ensure_loaded = strdup( "ensure_loaded" );
222  post_goal( term( EC_functor( ensure_loaded, 1 ), filename ) );
223  free( ensure_loaded );
224 
225  if ( EC_succeed != ec_resume() )
226  { throw Exception( "File %s could not be loaded", filename ); }
227 
228  return true;
229 }
230 
231 /** Register an interface for access from within the ECLiPSe context.
232  * @param interface the interface to register
233  * @return false if the ECLiPSe context hasn't been intialized yet
234  */
235 bool
236 EclipseAgentThread::register_interface( fawkes::Interface* interface )
237 {
238  if ( !m_initialized ) { return false; }
239 
240  m_registered_interfaces[ string( interface->id() ) ] = interface;
241 
242 
243  // define structs for interface data ----------------------------------
244  // data_IntefaceType(field1, field2, ...) -----------------------------
245 
246  // check whether struct is already defined
247  char* struct_name;
248  asprintf( &struct_name, "data_%s", interface->type() );
249 
250  post_goal( term( EC_functor( (char *) "current_struct", 2 ),
251  EC_atom( struct_name ),
252  newvar() ) );
253 
254  if ( EC_succeed != ec_resume() )
255  {
256  // define named structure
257  // data_InterfaceType( field1, field2, ... )
258 
259  vector< string > fields;
260  for ( InterfaceFieldIterator i = interface->fields();
261  i != interface->fields_end();
262  ++i )
263  { fields.push_back( i.get_name() ); }
264 
265  EC_word args[ fields.size() ];
266 
267  for ( size_t i = 0 ; i < fields.size(); ++i )
268  {
269  char* c = strdup( fields.at( i ).c_str() );
270  args[ i ] = EC_atom( c );
271  free( c );
272  }
273 
274  EC_word new_struct = term( EC_functor( struct_name, (int) fields.size() ), args );
275 
276  char* local = strdup( "local" );
277  char* strct = strdup( "struct" );
278  EC_word struct_def = term( EC_functor( strct, 1 ), new_struct );
279  EC_word struct_def_local = term( EC_functor( local, 1), struct_def );
280 
281  char* call = strdup( "call" );
282  // call( struct( data_InterfaceType(field1, field2, ...) ) )
283  post_goal( term( EC_functor( call, 1 ), struct_def_local ) );
284 
285  // cleanup
286  free( local );
287  free( strct );
288  free( call );
289 
290  if ( EC_succeed != ec_resume() )
291  { throw Exception( "Failed to define structure %s", struct_name ); }
292  }
293 
294  free( struct_name );
295 
296 
297  // define structs for message data ------------------------------------
298  // data_IntefaceType_MessageType(field1, field2, ...) -----------------
299 
300  std::list<const char *> message_types = interface->get_message_types();
301  for ( std::list<const char *>::iterator type_iter = message_types.begin();
302  type_iter != message_types.end();
303  ++type_iter )
304  {
305  // check whether struct is already defined
306  char* struct_name;
307  asprintf( &struct_name, "data_%s_%s", interface->type(), *type_iter );
308 
309  post_goal( term( EC_functor( (char *) "current_struct", 2 ),
310  EC_atom( struct_name ),
311  newvar() ) );
312 
313  if ( EC_succeed != ec_resume() )
314  {
315  // define name structure
316  // data_InterfaceType_MessageType( field1, field2, ... )
317 
318  Message* msg = interface->create_message( *type_iter );
319 
320  vector< string > fields;
321  for ( InterfaceFieldIterator field_iter = msg->fields();
322  field_iter != msg->fields_end();
323  ++field_iter )
324  {
325  string name = field_iter.get_name();
326  fields.push_back( name );
327  }
328 
329  delete msg;
330 
331  EC_word args[ fields.size() ];
332 
333  for ( size_t i = 0; i < fields.size(); ++i )
334  {
335  char* c = strdup( fields.at( i ).c_str() );
336  args[ i ] = EC_atom( c );
337  free( c );
338  }
339 
340  if ( 0 != fields.size() )
341  {
342  EC_word new_struct = term( EC_functor( struct_name, (int) fields.size() ), args );
343  char* local = strdup( "local" );
344  char* strct = strdup( "struct" );
345  EC_word struct_def = term( EC_functor( strct, 1 ), new_struct );
346  EC_word struct_def_local = term( EC_functor( local, 1), struct_def );
347 
348  char* call = strdup( "call" );
349  // call( struct( data_InterfaceType_MessageType(field1, field2, ...) ) )
350  post_goal( term( EC_functor( call, 1 ), struct_def_local ) );
351 
352  // cleanup
353  free( local );
354  free( strct );
355  free( call );
356 
357  if ( EC_succeed != ec_resume() )
358  { throw Exception( "Failed to define structure %s", struct_name ); }
359  }
360  }
361 
362  free( struct_name );
363  }
364 
365  return true;
366 }
367 
368 /** Get the registered interface with the given id.
369  * @param id the interface id
370  * @return the interface or NULL if no interface with the given id is registerd
371  */
374 {
375  map< string, fawkes::Interface* >::iterator i = m_registered_interfaces.find( string( id ) );
376 
377  if ( i == m_registered_interfaces.end() ) { return NULL; }
378 
379  return i->second;
380 }
381 
382 /** Get the logger.
383  * @return the logger
384  */
387 {
388  return logger;
389 }
390 
391 /** Get the EclipseAgentThread instance.
392  * @return the instance
393  */
396 {
397  if ( !m_instance )
398  { throw Exception( "No instance of type EclipseThread instantiated" ); }
399 
400  return m_instance;
401 }