Fawkes API  Fawkes Development Version
interface_importer.cpp
1 
2 /***************************************************************************
3  * interfaceimporter.cpp - Fawkes Lua Interface Importer
4  *
5  * Created: Thu Jan 01 14:32:11 2009
6  * Copyright 2006-2009 Tim Niemueller [www.niemueller.de]
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 <lua/interface_importer.h>
24 #include <lua/context.h>
25 
26 #include <config/config.h>
27 #include <interface/interface.h>
28 #include <blackboard/blackboard.h>
29 #include <logging/logger.h>
30 
31 #include <cstring>
32 
33 namespace fawkes {
34 #if 0 /* just to make Emacs auto-indent happy */
35 }
36 #endif
37 
38 /** @class LuaInterfaceImporter <lua/interface_importer.h>
39  * Lua interface importer.
40  * The Lua interface importer reads a list from the configuration for a given
41  * prefix and exports them to the Lua environment. The configuration entries have
42  * the form "/this/is/the/prefix/variablename" -> Interface UID. The interfaces
43  * are exported as a table assigned to the global variable named "interfaces".
44  * This table has four entries, reading and writing to tables with variablename
45  * to interface mappings and reading_by_uid and writing_by_uid with mappings from
46  * the interface UID to the interface.
47  * @author Tim Niemueller
48  */
49 
50 /** Constructor.
51  * @param context Lua context
52  * @param blackboard BlackBoard
53  * @param config configuration
54  * @param logger Logger
55  */
57  BlackBoard *blackboard,
58  Configuration *config,
59  Logger *logger)
60 {
61  __context = context;
62  __blackboard = blackboard;
63  __config = config;
64  __logger = logger;
65  __two_stage = false;
66  __context->add_watcher(this);
67 
68  __interfaces_pushed = false;
69 }
70 
71 
72 /** Destructor. */
74 {
75  __context->remove_watcher(this);
78  __ext_rifs.clear();
79  __ext_wifs.clear();
80 }
81 
82 
83 /** Open interfaces (internal).
84  * @param prefix configuration prefix for the interface list
85  * @param imap interface map to fill with interfaces
86  * @param write if true interfaces are opened for writing, false to open for reading
87  */
88 void
89 LuaInterfaceImporter::open_interfaces(std::string &prefix, InterfaceMap &imap, bool write)
90 {
91  if (! __config) throw NullPointerException("Config has not been set");
92 
93  Configuration::ValueIterator *vi = __config->search(prefix.c_str());
94  while (vi->next()) {
95  if (strcmp(vi->type(), "string") != 0) {
96  TypeMismatchException e("Only values of type string may occur in %s, "
97  "but found value of type %s",
98  prefix.c_str(), vi->type());
99  delete vi;
100  throw e;
101  }
102  std::string uid = vi->get_string();
103 
104  if (uid.find("::") == std::string::npos) {
105  delete vi;
106  throw Exception("Interface UID '%s' at %s is not valid, missing double colon",
107  uid.c_str(), vi->path());
108  }
109  std::string varname = std::string(vi->path()).substr(prefix.length());
110  std::string iftype = uid.substr(0, uid.find("::"));
111  std::string ifname = uid.substr(uid.find("::") + 2);
112 
113  if ( __reading_ifs.find(varname) != __reading_ifs.end() ) {
114  delete vi;
115  throw Exception("Reading interface with varname %s already opened", varname.c_str());
116  }
117  if ( __reading_multi_ifs.find(varname) != __reading_multi_ifs.end() ) {
118  delete vi;
119  throw Exception("Reading multi interface with varname %s already opened", varname.c_str());
120  }
121  if ( __writing_ifs.find(varname) != __writing_ifs.end() ) {
122  delete vi;
123  throw Exception("Writing interface with varname %s already opened", varname.c_str());
124  }
125 
126 
127  if (ifname.find_first_of("*?[") == std::string::npos) {
128  __logger->log_info("LuaInterfaceImporter", "Adding %s interface %s::%s with name %s",
129  write ? "writing" : "reading",
130  iftype.c_str(), ifname.c_str(), varname.c_str());
131  try {
132  Interface *iface;
133  if (write) {
134  iface = __blackboard->open_for_writing(iftype.c_str(), ifname.c_str());
135  } else {
136  iface = __blackboard->open_for_reading(iftype.c_str(), ifname.c_str());
137  }
138  if (__two_stage) {
139  iface->resize_buffers(1);
140  }
141  imap[varname] = iface;
142  } catch (Exception &e) {
143  delete vi;
144  throw;
145  }
146  } else {
147  if (write) {
148  delete vi;
149  throw Exception("Illegal config entry %s=%s, multiple interfaces can "
150  "only be opened for reading", vi->path(), uid.c_str());
151  }
152  __logger->log_info("LuaInterfaceImporter", "Adding multiple %s interfaces %s::%s with in table %s",
153  write ? "writing" : "reading",
154  iftype.c_str(), ifname.c_str(), varname.c_str());
155 
156  std::list<Interface *> interfaces = __blackboard->open_multiple_for_reading(iftype.c_str(), ifname.c_str());
157  __reading_multi_ifs[varname] = interfaces;
158  InterfaceObserver *observer =
159  new InterfaceObserver(this, varname, iftype.c_str(), ifname.c_str());
160  __observers[varname] = observer;
161  __blackboard->register_observer(observer);
162  }
163  }
164  delete vi;
165 }
166 
167 
168 /** Open interfaces for reading.
169  * @param prefix configuration prefix for the interface list
170  */
171 void
173 {
174  open_interfaces(prefix, __reading_ifs, /* write */ false);
175 }
176 
177 /** Open interfaces for writing.
178  * @param prefix configuration prefix for the interface list
179  */
180 void
182 {
183  open_interfaces(prefix, __writing_ifs, /* write */ true);
184 }
185 
186 
187 /** Add a single interface to be pushed to the context.
188  * The given interface is pushed with the given variable name to the context,
189  * on explicit push_interfaces() and on the next LuaContext restart. However, the
190  * interface is not owned by the importer and thus neither is the interface read
191  * during read() nor is it written during write(). It is also not automatically
192  * closed in the destructor.
193  * @param varname the variable name of the interface
194  * @param interface the interface to push
195  */
196 void
197 LuaInterfaceImporter::add_interface(std::string varname, Interface *interface)
198 {
199  if ( interface->is_writer() ) {
200  __ext_wifs[varname] = interface;
201  } else {
202  __ext_rifs[varname] = interface;
203  }
204 }
205 
206 
207 void
208 LuaInterfaceImporter::add_observed_interface(std::string varname,
209  const char *type, const char *id)
210 {
211  try {
212  if (__reading_multi_ifs.find(varname) == __reading_multi_ifs.end() ) {
213  throw Exception("Notified about unknown interface varname %s", varname.c_str());
214  }
215  Interface *iface = __blackboard->open_for_reading(type, id);
216  __context->add_package((std::string("interfaces.") + iface->type()).c_str());
217  __reading_multi_ifs[varname].push_back(iface);
218  __context->get_global("interfaces"); // it
219  __context->get_field(-1, "reading"); // it rt
220  __context->get_field(-1, varname.c_str()); // it rt vt
221  __context->push_usertype(iface, iface->type(), "fawkes"); // it rt vt iface
222  __context->raw_seti(-2, __reading_multi_ifs[varname].size()); // it rt vt
223  __context->push_usertype(iface, iface->type(), "fawkes"); // it rt vt iface
224  __context->set_field(iface->uid(), -2); // it rt vt
225  __context->pop(3); // ---
226  } catch (Exception &e) {
227  __logger->log_warn("LuaInterfaceImporter", "Failed to add observed interface "
228  "%s:%s, exception follows", type, id);
229  __logger->log_warn("LuaInterfaceImporter", e);
230  }
231 }
232 
233 
234 /** Close interfaces for reading. */
235 void
237 {
238  for (InterfaceMap::iterator i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
239  __blackboard->close(i->second);
240  }
241  __reading_ifs.clear();
242 
243  for (ObserverMap::iterator o = __observers.begin(); o != __observers.end(); ++o) {
244  __blackboard->unregister_observer(o->second);
245  delete o->second;
246  }
247  __observers.clear();
248 
249  for (InterfaceListMap::iterator i = __reading_multi_ifs.begin(); i != __reading_multi_ifs.end(); ++i) {
250  for (std::list<Interface *>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
251  __blackboard->close(*j);
252  }
253  }
254  __reading_multi_ifs.clear();
255 }
256 
257 
258 /** Close interfaces for writing. */
259 void
261 {
262  for (InterfaceMap::iterator i = __writing_ifs.begin(); i != __writing_ifs.end(); ++i) {
263  __blackboard->close(i->second);
264  }
265  __writing_ifs.clear();
266 }
267 
268 /** Get interface map of reading interfaces.
269  * @return interface map of reading interfaces
270  */
273 {
274  return __reading_ifs;
275 }
276 
277 
278 /** Get interface map of writing interfaces.
279  * @return interface map of writing interfaces
280  */
283 {
284  return __writing_ifs;
285 }
286 
287 
288 /** Read from all reading interfaces. */
289 void
291 {
292  for (InterfaceMap::iterator i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
293  i->second->read();
294  }
295 }
296 
297 
298 /** Read from all reading interfaces into a buffer.
299  */
300 void
302 {
303  InterfaceMap::iterator i;
304  if (! __two_stage) {
305  for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
306  i->second->resize_buffers(1);
307  }
308  __two_stage = true;
309  }
310  for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
311  i->second->copy_shared_to_buffer(0);
312  }
313 }
314 
315 /** Update interfaces from internal buffers.
316  * @exception Exception thrown if read_to_buffer() was not called
317  * before.
318  */
319 void
321 {
322  if (! __two_stage) {
323  throw Exception("LuaInterfaceImporter: trying to read buffer witout "
324  "previous read_to_buffer()");
325  }
326  InterfaceMap::iterator i;
327  for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
328  i->second->read_from_buffer(0);
329  }
330 }
331 
332 /** Write all writing interfaces. */
333 void
335 {
336  for (InterfaceMap::iterator i = __writing_ifs.begin(); i != __writing_ifs.end(); ++i) {
337  try {
338  i->second->write();
339  } catch (Exception &e) {
340  e.append("Failed to write interface %s, ignoring.", i->second->uid());
341  e.print_trace();
342  }
343  }
344 }
345 
346 void
347 LuaInterfaceImporter::push_interfaces_varname(LuaContext *context, InterfaceMap &imap)
348 {
349  InterfaceMap::iterator imi;
350  for (imi = imap.begin(); imi != imap.end(); ++imi) {
351  context->add_package((std::string("interfaces.") + imi->second->type()).c_str());
352  context->push_usertype(imi->second, imi->second->type(), "fawkes");
353  context->set_field(imi->first.c_str());
354  }
355 }
356 
357 void
358 LuaInterfaceImporter::push_multi_interfaces_varname(LuaContext *context, InterfaceListMap &imap)
359 {
360  InterfaceListMap::iterator imi;
361  for (imi = imap.begin(); imi != imap.end(); ++imi) {
362  context->create_table(0, imi->second.size());
363  int idx = 0;
364  for (std::list<Interface *>::iterator i = imi->second.begin(); i != imi->second.end(); ++i) {
365  context->add_package((std::string("interfaces.") + (*i)->type()).c_str());
366  context->push_usertype(*i, (*i)->type(), "fawkes");
367  context->raw_seti(-2, ++idx);
368  context->push_usertype(*i, (*i)->type(), "fawkes");
369  context->set_field((*i)->uid(), -2);
370  }
371  context->set_field(imi->first.c_str());
372  }
373 }
374 
375 void
376 LuaInterfaceImporter::push_interfaces_uid(LuaContext *context, InterfaceMap &imap)
377 {
378  InterfaceMap::iterator imi;
379  for (imi = imap.begin(); imi != imap.end(); ++imi) {
380  context->add_package((std::string("interfaces.") + imi->second->type()).c_str());
381  context->push_usertype(imi->second, imi->second->type(), "fawkes");
382  context->set_field(imi->second->uid());
383  }
384 }
385 
386 void
388 {
389 
390  // it: interface table, rt: reading table, wt: writing table, rtu: rt by uid, wtu: wt by uid
391  context->create_table(0, 4); // it
392 
393  context->create_table(0, __reading_ifs.size() + __ext_rifs.size()); // it rt
394  push_interfaces_varname(context, __reading_ifs); // it rt
395  push_interfaces_varname(context, __ext_rifs); // it rt
396  push_multi_interfaces_varname(context, __reading_multi_ifs); // it rt
397  context->set_field("reading"); // it
398 
399  context->create_table(0, __reading_ifs.size() + __ext_rifs.size()); // it rtu
400  push_interfaces_uid(context, __reading_ifs); // it rtu
401  push_interfaces_uid(context, __ext_rifs); // it rtu
402  context->set_field("reading_by_uid"); // it
403 
404  context->create_table(0, __writing_ifs.size() + __ext_wifs.size()); // it wt
405  push_interfaces_varname(context, __writing_ifs); // it wt
406  push_interfaces_varname(context, __ext_wifs); // it wt
407  context->set_field("writing"); // it
408 
409  context->create_table(0, __writing_ifs.size()); // it wtu
410  push_interfaces_uid(context, __writing_ifs); // it wtu
411  push_interfaces_uid(context, __ext_wifs); // it wtu
412  context->set_field("writing_by_uid"); // it
413 
414  context->set_global("interfaces"); // ---
415 }
416 
417 /** Push interfaces to Lua environment.
418  * The interfaces are pushed to the interfaces table described in the class
419  * documentation. Note that you need to do this only once. The table is
420  * automatically re-pushed on a Lua restart event.
421  */
422 void
424 {
425  __interfaces_pushed = true;
426  push_interfaces(__context);
427 }
428 
429 
430 void
432 {
433  try {
434  if ( __interfaces_pushed ) {
435  push_interfaces(context);
436  }
437  } catch (Exception &e) {
438  __logger->log_warn("LuaInterfaceImporter", "Failed to re-push interfacs, exception follows");
439  __logger->log_warn("LuaInterfaceImporter", e);
440  throw;
441  }
442 }
443 
444 
445 /** Constructor.
446  * @param lii LuaInterfaceImporter instance this observer is assigned to
447  * @param varname variable name
448  * @param type type of interface
449  * @param id_pattern ID pattern to observe
450  */
451 LuaInterfaceImporter::InterfaceObserver::InterfaceObserver(LuaInterfaceImporter *lii,
452  std::string varname,
453  const char *type, const char *id_pattern)
454 {
455  __lii = lii;
456  __varname = varname;
457 
458  bbio_add_observed_create(type, id_pattern);
459 }
460 
461 
462 void
463 LuaInterfaceImporter::InterfaceObserver::bb_interface_created(const char *type, const char *id) throw()
464 {
465  __lii->add_observed_interface(__varname, type, id);
466 }
467 
468 } // end of namespace fawkes
virtual void register_observer(BlackBoardInterfaceObserver *observer)
Register BB interface observer.
Definition: blackboard.cpp:230
LuaInterfaceImporter::InterfaceMap & reading_interfaces()
Get interface map of reading interfaces.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
LuaInterfaceImporter(LuaContext *__context, BlackBoard *blackboard, Configuration *config, Logger *logger)
Constructor.
virtual const char * type() const =0
Type of value.
Fawkes library namespace.
void read_to_buffer()
Read from all reading interfaces into a buffer.
void pop(int n)
Pop value(s) from stack.
Definition: context.cpp:964
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.
void raw_seti(int idx, int n)
Set indexed value without invoking meta methods.
Definition: context.cpp:1093
A NULL pointer was supplied where not allowed.
Definition: software.h:34
void remove_watcher(LuaContextWatcher *watcher)
Remove a context watcher.
Definition: context.cpp:1406
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:79
void open_writing_interfaces(std::string &prefix)
Open interfaces for writing.
Map with a lock.
Definition: lock_map.h:37
void set_field(const char *key, int t_index=-2)
Set field of a table.
Definition: context.cpp:1030
void add_package(const char *package)
Add a default package.
Definition: context.cpp:383
void close_reading_interfaces()
Close interfaces for reading.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Lua interface importer.
void open_reading_interfaces(std::string &prefix)
Open interfaces for reading.
Lua C++ wrapper.
Definition: context.h:47
void get_field(int idx, const char *k)
Get named value from table.
Definition: context.cpp:1069
virtual void unregister_observer(BlackBoardInterfaceObserver *observer)
Unregister BB interface observer.
Definition: blackboard.cpp:244
void read_from_buffer()
Update interfaces from internal buffers.
virtual std::string get_string() const =0
Get string value.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual const char * path() const =0
Path of value.
void push_usertype(void *data, const char *type_name, const char *name_space=0)
Push usertype on top of stack.
Definition: context.cpp:925
virtual std::list< Interface * > open_multiple_for_reading(const char *type_pattern, const char *id_pattern="*", const char *owner=NULL)=0
Open multiple interfaces for reading.
void add_interface(std::string varname, Interface *interface)
Add a single interface to be pushed to the context.
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:440
void read()
Read from all reading interfaces.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:619
void get_global(const char *name)
Get global variable.
Definition: context.cpp:1126
void resize_buffers(unsigned int num_buffers)
Resize buffer array.
Definition: interface.cpp:1241
Iterator interface to iterate over config values.
Definition: config.h:72
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
LuaInterfaceImporter::InterfaceMap & writing_interfaces()
Get interface map of writing interfaces.
void close_writing_interfaces()
Close interfaces for writing.
void write()
Write all writing interfaces.
The BlackBoard abstract class.
Definition: blackboard.h:48
void set_global(const char *name)
Set a global value.
Definition: context.cpp:1042
void create_table(int narr=0, int nrec=0)
Create a table on top of the stack.
Definition: context.cpp:1002
Interface for configuration handling.
Definition: config.h:67
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
void lua_restarted(LuaContext *context)
Lua restart event.
void push_interfaces()
Push interfaces to Lua environment.
void append(const char *format,...)
Append messages to the message list.
Definition: exception.cpp:341
void add_watcher(LuaContextWatcher *watcher)
Add a context watcher.
Definition: context.cpp:1396
virtual void close(Interface *interface)=0
Close interface.
Interface for logging.
Definition: logger.h:34