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 <blackboard/blackboard.h>
24 #include <config/config.h>
25 #include <interface/interface.h>
26 #include <logging/logger.h>
27 #include <lua/context.h>
28 #include <lua/interface_importer.h>
29 
30 #include <cstring>
31 
32 namespace fawkes {
33 
34 /** @class LuaInterfaceImporter <lua/interface_importer.h>
35  * Lua interface importer.
36  * The Lua interface importer reads a list from the configuration for a given
37  * prefix and exports them to the Lua environment. The configuration entries have
38  * the form "/this/is/the/prefix/variablename" -> Interface UID. The interfaces
39  * are exported as a table assigned to the global variable named "interfaces".
40  * This table has four entries, reading and writing to tables with variablename
41  * to interface mappings and reading_by_uid and writing_by_uid with mappings from
42  * the interface UID to the interface.
43  * @author Tim Niemueller
44  */
45 
46 /** Constructor.
47  * @param context Lua context
48  * @param blackboard BlackBoard
49  * @param config configuration
50  * @param logger Logger
51  */
53  BlackBoard * blackboard,
54  Configuration *config,
55  Logger * logger)
56 {
57  context_ = context;
58  blackboard_ = blackboard;
59  config_ = config;
60  logger_ = logger;
61  two_stage_ = false;
62  context_->add_watcher(this);
63 
64  interfaces_pushed_ = false;
65 }
66 
67 /** Destructor. */
69 {
70  context_->remove_watcher(this);
73  ext_rifs_.clear();
74  ext_wifs_.clear();
75 }
76 
77 /** Open interfaces (internal).
78  * @param prefix configuration prefix for the interface list
79  * @param imap interface map to fill with interfaces
80  * @param write if true interfaces are opened for writing, false to open for reading
81  */
82 void
83 LuaInterfaceImporter::open_interfaces(std::string &prefix, InterfaceMap &imap, bool write)
84 {
85  if (!config_)
86  throw NullPointerException("Config has not been set");
87 
88  Configuration::ValueIterator *vi = config_->search(prefix.c_str());
89  while (vi->next()) {
90  if (strcmp(vi->type(), "string") != 0) {
91  TypeMismatchException e("Only values of type string may occur in %s, "
92  "but found value of type %s",
93  prefix.c_str(),
94  vi->type());
95  delete vi;
96  throw e;
97  }
98  std::string uid = vi->get_string();
99 
100  if (uid.find("::") == std::string::npos) {
101  delete vi;
102  throw Exception("Interface UID '%s' at %s is not valid, missing double colon",
103  uid.c_str(),
104  vi->path());
105  }
106  std::string varname = std::string(vi->path()).substr(prefix.length());
107  std::string iftype = uid.substr(0, uid.find("::"));
108  std::string ifname = uid.substr(uid.find("::") + 2);
109 
110  if (reading_ifs_.find(varname) != reading_ifs_.end()) {
111  delete vi;
112  throw Exception("Reading interface with varname %s already opened", varname.c_str());
113  }
114  if (reading_multi_ifs_.find(varname) != reading_multi_ifs_.end()) {
115  delete vi;
116  throw Exception("Reading multi interface with varname %s already opened", varname.c_str());
117  }
118  if (writing_ifs_.find(varname) != writing_ifs_.end()) {
119  delete vi;
120  throw Exception("Writing interface with varname %s already opened", varname.c_str());
121  }
122 
123  if (ifname.find_first_of("*?[") == std::string::npos) {
124  logger_->log_info("LuaInterfaceImporter",
125  "Adding %s interface %s::%s with name %s",
126  write ? "writing" : "reading",
127  iftype.c_str(),
128  ifname.c_str(),
129  varname.c_str());
130  try {
131  Interface *iface;
132  if (write) {
133  iface = blackboard_->open_for_writing(iftype.c_str(), ifname.c_str());
134  } else {
135  iface = blackboard_->open_for_reading(iftype.c_str(), ifname.c_str());
136  }
137  if (two_stage_) {
138  iface->resize_buffers(1);
139  }
140  imap[varname] = iface;
141  } catch (Exception &e) {
142  delete vi;
143  throw;
144  }
145  } else {
146  if (write) {
147  delete vi;
148  throw Exception("Illegal config entry %s=%s, multiple interfaces can "
149  "only be opened for reading",
150  vi->path(),
151  uid.c_str());
152  }
153  logger_->log_info("LuaInterfaceImporter",
154  "Adding multiple %s interfaces %s::%s with in table %s",
155  write ? "writing" : "reading",
156  iftype.c_str(),
157  ifname.c_str(),
158  varname.c_str());
159 
160  std::list<Interface *> interfaces =
161  blackboard_->open_multiple_for_reading(iftype.c_str(), ifname.c_str());
162  reading_multi_ifs_[varname] = interfaces;
163  InterfaceObserver *observer =
164  new InterfaceObserver(this, varname, iftype.c_str(), ifname.c_str());
165  observers_[varname] = observer;
166  blackboard_->register_observer(observer);
167  }
168  }
169  delete vi;
170 }
171 
172 /** Open interfaces for reading.
173  * @param prefix configuration prefix for the interface list
174  */
175 void
177 {
178  open_interfaces(prefix, reading_ifs_, /* write */ false);
179 }
180 
181 /** Open interfaces for writing.
182  * @param prefix configuration prefix for the interface list
183  */
184 void
186 {
187  open_interfaces(prefix, writing_ifs_, /* write */ true);
188 }
189 
190 /** Add a single interface to be pushed to the context.
191  * The given interface is pushed with the given variable name to the context,
192  * on explicit push_interfaces() and on the next LuaContext restart. However, the
193  * interface is not owned by the importer and thus neither is the interface read
194  * during read() nor is it written during write(). It is also not automatically
195  * closed in the destructor.
196  * @param varname the variable name of the interface
197  * @param interface the interface to push
198  */
199 void
200 LuaInterfaceImporter::add_interface(std::string varname, Interface *interface)
201 {
202  if (interface->is_writer()) {
203  ext_wifs_[varname] = interface;
204  } else {
205  ext_rifs_[varname] = interface;
206  }
207 }
208 
209 void
210 LuaInterfaceImporter::add_observed_interface(std::string varname, const char *type, const char *id)
211 {
212  try {
213  if (reading_multi_ifs_.find(varname) == reading_multi_ifs_.end()) {
214  throw Exception("Notified about unknown interface varname %s", varname.c_str());
215  }
216  Interface *iface = blackboard_->open_for_reading(type, id);
217  context_->add_package((std::string("interfaces.") + iface->type()).c_str());
218  reading_multi_ifs_[varname].push_back(iface);
219  context_->get_global("interfaces"); // it
220  context_->get_field(-1, "reading"); // it rt
221  context_->get_field(-1, varname.c_str()); // it rt vt
222  context_->push_usertype(iface, iface->type(), "fawkes"); // it rt vt iface
223  context_->raw_seti(-2, reading_multi_ifs_[varname].size()); // it rt vt
224  context_->push_usertype(iface, iface->type(), "fawkes"); // it rt vt iface
225  context_->set_field(iface->uid(), -2); // it rt vt
226  context_->pop(3); // ---
227  } catch (Exception &e) {
228  logger_->log_warn("LuaInterfaceImporter",
229  "Failed to add observed interface "
230  "%s:%s, exception follows",
231  type,
232  id);
233  logger_->log_warn("LuaInterfaceImporter", e);
234  }
235 }
236 
237 /** Close interfaces for reading. */
238 void
240 {
241  for (InterfaceMap::iterator i = reading_ifs_.begin(); i != reading_ifs_.end(); ++i) {
242  blackboard_->close(i->second);
243  }
244  reading_ifs_.clear();
245 
246  for (ObserverMap::iterator o = observers_.begin(); o != observers_.end(); ++o) {
247  blackboard_->unregister_observer(o->second);
248  delete o->second;
249  }
250  observers_.clear();
251 
252  for (InterfaceListMap::iterator i = reading_multi_ifs_.begin(); i != reading_multi_ifs_.end();
253  ++i) {
254  for (std::list<Interface *>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
255  blackboard_->close(*j);
256  }
257  }
258  reading_multi_ifs_.clear();
259 }
260 
261 /** Close interfaces for writing. */
262 void
264 {
265  for (InterfaceMap::iterator i = writing_ifs_.begin(); i != writing_ifs_.end(); ++i) {
266  blackboard_->close(i->second);
267  }
268  writing_ifs_.clear();
269 }
270 
271 /** Get interface map of reading interfaces.
272  * @return interface map of reading interfaces
273  */
276 {
277  return reading_ifs_;
278 }
279 
280 /** Get interface map of writing interfaces.
281  * @return interface map of writing interfaces
282  */
285 {
286  return writing_ifs_;
287 }
288 
289 /** Read from all reading interfaces. */
290 void
292 {
293  for (InterfaceMap::iterator i = reading_ifs_.begin(); i != reading_ifs_.end(); ++i) {
294  i->second->read();
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
387 LuaInterfaceImporter::push_interfaces(LuaContext *context)
388 {
389  // it: interface table, rt: reading table, wt: writing table, rtu: rt by uid, wtu: wt by uid
390  context->create_table(0, 4); // it
391 
392  context->create_table(0, reading_ifs_.size() + ext_rifs_.size()); // it rt
393  push_interfaces_varname(context, reading_ifs_); // it rt
394  push_interfaces_varname(context, ext_rifs_); // it rt
395  push_multi_interfaces_varname(context, reading_multi_ifs_); // it rt
396  context->set_field("reading"); // it
397 
398  context->create_table(0, reading_ifs_.size() + ext_rifs_.size()); // it rtu
399  push_interfaces_uid(context, reading_ifs_); // it rtu
400  push_interfaces_uid(context, ext_rifs_); // it rtu
401  context->set_field("reading_by_uid"); // it
402 
403  context->create_table(0, writing_ifs_.size() + ext_wifs_.size()); // it wt
404  push_interfaces_varname(context, writing_ifs_); // it wt
405  push_interfaces_varname(context, ext_wifs_); // it wt
406  context->set_field("writing"); // it
407 
408  context->create_table(0, writing_ifs_.size()); // it wtu
409  push_interfaces_uid(context, writing_ifs_); // it wtu
410  push_interfaces_uid(context, ext_wifs_); // it wtu
411  context->set_field("writing_by_uid"); // it
412 
413  context->set_global("interfaces"); // ---
414 }
415 
416 /** Push interfaces to Lua environment.
417  * The interfaces are pushed to the interfaces table described in the class
418  * documentation. Note that you need to do this only once. The table is
419  * automatically re-pushed on a Lua restart event.
420  */
421 void
423 {
424  interfaces_pushed_ = true;
425  push_interfaces(context_);
426 }
427 
428 void
430 {
431  try {
432  if (interfaces_pushed_) {
433  push_interfaces(context);
434  }
435  } catch (Exception &e) {
436  logger_->log_warn("LuaInterfaceImporter", "Failed to re-push interfacs, exception follows");
437  logger_->log_warn("LuaInterfaceImporter", e);
438  throw;
439  }
440 }
441 
442 /** Constructor.
443  * @param lii LuaInterfaceImporter instance this observer is assigned to
444  * @param varname variable name
445  * @param type type of interface
446  * @param id_pattern ID pattern to observe
447  */
448 LuaInterfaceImporter::InterfaceObserver::InterfaceObserver(LuaInterfaceImporter *lii,
449  std::string varname,
450  const char * type,
451  const char * id_pattern)
452 {
453  lii_ = lii;
454  varname_ = varname;
455 
456  bbio_add_observed_create(type, id_pattern);
457 }
458 
459 void
460 LuaInterfaceImporter::InterfaceObserver::bb_interface_created(const char *type,
461  const char *id) throw()
462 {
463  lii_->add_observed_interface(varname_, type, id);
464 }
465 
466 } // end of namespace fawkes
virtual void register_observer(BlackBoardInterfaceObserver *observer)
Register BB interface observer.
Definition: blackboard.cpp:225
LuaInterfaceImporter::InterfaceMap & reading_interfaces()
Get interface map of reading interfaces.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
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:918
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:1038
A NULL pointer was supplied where not allowed.
Definition: software.h:31
void remove_watcher(LuaContextWatcher *watcher)
Remove a context watcher.
Definition: context.cpp:1328
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:78
void open_writing_interfaces(std::string &prefix)
Open interfaces for writing.
void set_field(const char *key, int t_index=-2)
Set field of a table.
Definition: context.cpp:980
void add_package(const char *package)
Add a default package.
Definition: context.cpp:386
void close_reading_interfaces()
Close interfaces for reading.
Base class for exceptions in Fawkes.
Definition: exception.h:35
Lua interface importer.
LuaInterfaceImporter(LuaContext *context_, BlackBoard *blackboard, Configuration *config, Logger *logger)
Constructor.
void open_reading_interfaces(std::string &prefix)
Open interfaces for reading.
Lua C++ wrapper.
Definition: context.h:43
void get_field(int idx, const char *k)
Get named value from table.
Definition: context.cpp:1016
virtual void unregister_observer(BlackBoardInterfaceObserver *observer)
Unregister BB interface observer.
Definition: blackboard.cpp:240
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:882
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:438
void read()
Read from all reading interfaces.
void print_trace()
Prints trace to stderr.
Definition: exception.cpp:601
void get_global(const char *name)
Get global variable.
Definition: context.cpp:1068
void resize_buffers(unsigned int num_buffers)
Resize buffer array.
Definition: interface.cpp:1225
Iterator interface to iterate over config values.
Definition: config.h:71
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:45
Interface for configuration handling.
Definition: config.h:64
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:333
void add_watcher(LuaContextWatcher *watcher)
Add a context watcher.
Definition: context.cpp:1319
virtual void close(Interface *interface)=0
Close interface.
Interface for logging.
Definition: logger.h:41