Fawkes API  Fawkes Development Version
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 }
Interface field iterator.
EclipseAgentThread()
Constructor.
Base class for all messages passed through interfaces in Fawkes BlackBoard.
Definition: message.h:43
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
virtual void finalize()
Finalize the thread.
fawkes::Logger * get_logger()
Get the logger.
Fawkes library namespace.
STL namespace.
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
virtual bool next()=0
Check if there is another element and advance to this if possible.
Thread class encapsulation of pthreads.
Definition: thread.h:42
virtual Message * create_message(const char *type) const =0
Create message based on type name.
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
virtual ~EclipseAgentThread()
Destructor.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:44
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: message.cpp:398
virtual Interface * open_for_writing(const char *interface_type, const char *identifier)=0
Open interface for writing.
void post_event(const char *)
Post an event to the ECLiPSe context.
const char * id() const
Get identifier of interface.
Definition: interface.cpp:645
static EclipseAgentThread * instance()
Get the EclipseAgentThread instance.
virtual bool is_string() const =0
Check if current value is a string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void read_interfaces()
Read all registered interfaces.
virtual std::string get_string() const =0
Get string value.
fawkes::Interface * get_registered_interface(const char *id)
Get the registered interface with the given id.
This thread creates an ECLiPSe context in which the Readylog interpreter and the program are loaded...
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: message.cpp:388
const char * name() const
Get name of thread.
Definition: thread.h:95
virtual void init()
Initialize the thread.
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1143
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void once()
Execute an action exactly once.
Iterator interface to iterate over config values.
Definition: config.h:68
virtual Interface * open_for_reading(const char *interface_type, const char *identifier)=0
Open interface for reading.
void write_interfaces()
Write the registered interface that have been opened for writing.
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1133
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:44
const char * type() const
Get type of interface.
Definition: interface.cpp:635
std::list< const char * > get_message_types()
Obtain a list of textual representations of the message types available for this interface.
Definition: interface.cpp:395
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:43
Interface for logging.
Definition: logger.h:34