Fawkes API  Fawkes Development Version
interface_importer.cpp
00001 
00002 /***************************************************************************
00003  *  interfaceimporter.cpp - Fawkes Lua Interface Importer
00004  *
00005  *  Created: Thu Jan 01 14:32:11 2009
00006  *  Copyright  2006-2009  Tim Niemueller [www.niemueller.de]
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 <lua/interface_importer.h>
00024 #include <lua/context.h>
00025 
00026 #include <config/config.h>
00027 #include <interface/interface.h>
00028 #include <blackboard/blackboard.h>
00029 #include <logging/logger.h>
00030 
00031 #include <cstring>
00032 
00033 namespace fawkes {
00034 #if 0 /* just to make Emacs auto-indent happy */
00035 }
00036 #endif
00037 
00038 /** @class LuaInterfaceImporter <lua/interface_importer.h>
00039  * Lua interface importer.
00040  * The Lua interface importer reads a list from the configuration for a given
00041  * prefix and exports them to the Lua environment. The configuration entries have
00042  * the form "/this/is/the/prefix/variablename" -> Interface UID. The interfaces
00043  * are exported as a table assigned to the global variable named "interfaces".
00044  * This table has four entries, reading and writing to tables with variablename
00045  * to interface mappings and reading_by_uid and writing_by_uid with mappings from
00046  * the interface UID to the interface.
00047  * @author Tim Niemueller
00048  */
00049 
00050 /** Constructor.
00051  * @param context Lua context
00052  * @param blackboard BlackBoard
00053  * @param config configuration
00054  * @param logger Logger
00055  */
00056 LuaInterfaceImporter::LuaInterfaceImporter(LuaContext *context,
00057                                            BlackBoard *blackboard,
00058                                            Configuration *config,
00059                                            Logger *logger)
00060 {
00061   __context = context;
00062   __blackboard = blackboard;
00063   __config = config;
00064   __logger = logger;
00065   __two_stage = false;
00066   __context->add_watcher(this);
00067 
00068   __interfaces_pushed = false;
00069 }
00070 
00071 
00072 /** Destructor. */
00073 LuaInterfaceImporter::~LuaInterfaceImporter()
00074 {
00075   __context->remove_watcher(this);
00076   close_writing_interfaces();
00077   close_reading_interfaces();
00078   __ext_rifs.clear();
00079   __ext_wifs.clear();
00080 }
00081 
00082 
00083 /** Open interfaces (internal).
00084  * @param prefix configuration prefix for the interface list
00085  * @param imap interface map to fill with interfaces
00086  * @param write if true interfaces are opened for writing, false to open for reading
00087  */
00088 void
00089 LuaInterfaceImporter::open_interfaces(std::string &prefix, InterfaceMap &imap, bool write)
00090 {
00091   if (! __config) throw NullPointerException("Config has not been set");
00092 
00093   Configuration::ValueIterator *vi = __config->search(prefix.c_str());
00094   while (vi->next()) {
00095     if (strcmp(vi->type(), "string") != 0) {
00096       TypeMismatchException e("Only values of type string may occur in %s, "
00097                               "but found value of type %s",
00098                               prefix.c_str(), vi->type());
00099       delete vi;
00100       throw e;
00101     }
00102     std::string uid = vi->get_string();
00103 
00104     if (uid.find("::") == std::string::npos) {
00105       delete vi;
00106       throw Exception("Interface UID '%s' at %s is not valid, missing double colon",
00107                       uid.c_str(), vi->path());
00108     }
00109     std::string varname = std::string(vi->path()).substr(prefix.length());
00110     std::string iftype = uid.substr(0, uid.find("::"));
00111     std::string ifname = uid.substr(uid.find("::") + 2);
00112 
00113     if ( __reading_ifs.find(varname) != __reading_ifs.end() ) {
00114       delete vi;
00115       throw Exception("Reading interface with varname %s already opened", varname.c_str());
00116     }
00117     if ( __reading_multi_ifs.find(varname) != __reading_multi_ifs.end() ) {
00118       delete vi;
00119       throw Exception("Reading multi interface with varname %s already opened", varname.c_str());
00120     }
00121     if ( __writing_ifs.find(varname) != __writing_ifs.end() ) {
00122       delete vi;
00123       throw Exception("Writing interface with varname %s already opened", varname.c_str());
00124     }
00125 
00126 
00127     if (ifname.find_first_of("*?[") == std::string::npos) {
00128       __logger->log_info("LuaInterfaceImporter", "Adding %s interface %s::%s with name %s",
00129                          write ? "writing" : "reading",
00130                          iftype.c_str(), ifname.c_str(), varname.c_str());
00131       try {
00132         Interface *iface;
00133         if (write) {
00134           iface = __blackboard->open_for_writing(iftype.c_str(), ifname.c_str());
00135         } else {
00136           iface = __blackboard->open_for_reading(iftype.c_str(), ifname.c_str());
00137         }
00138         if (__two_stage) {
00139           iface->resize_buffers(1);
00140         }
00141         imap[varname] = iface;
00142       } catch (Exception &e) {
00143         delete vi;
00144         throw;
00145       }
00146     } else {
00147       if (write) {
00148         delete vi;
00149         throw Exception("Illegal config entry %s=%s, multiple interfaces can "
00150                         "only be opened for reading", vi->path(), uid.c_str());
00151       }
00152       __logger->log_info("LuaInterfaceImporter", "Adding multiple %s interfaces %s::%s with in table %s",
00153                          write ? "writing" : "reading",
00154                          iftype.c_str(), ifname.c_str(), varname.c_str());
00155 
00156       std::list<Interface *> interfaces = __blackboard->open_multiple_for_reading(iftype.c_str(), ifname.c_str());
00157       __reading_multi_ifs[varname] = interfaces;
00158       InterfaceObserver *observer =
00159         new InterfaceObserver(this, varname, iftype.c_str(), ifname.c_str());
00160       __observers[varname] = observer;
00161       __blackboard->register_observer(observer);
00162     }
00163   }
00164   delete vi;
00165 }
00166 
00167 
00168 /** Open interfaces for reading.
00169  * @param prefix configuration prefix for the interface list
00170  */
00171 void
00172 LuaInterfaceImporter::open_reading_interfaces(std::string &prefix)
00173 {
00174   open_interfaces(prefix, __reading_ifs, /* write */ false);
00175 }
00176 
00177 /** Open interfaces for writing.
00178  * @param prefix configuration prefix for the interface list
00179  */
00180 void
00181 LuaInterfaceImporter::open_writing_interfaces(std::string &prefix)
00182 {
00183   open_interfaces(prefix, __writing_ifs, /* write */ true);
00184 }
00185 
00186 
00187 /** Add a single interface to be pushed to the context.
00188  * The given interface is pushed with the given variable name to the context,
00189  * on explicit push_interfaces() and on the next LuaContext restart. However, the
00190  * interface is not owned by the importer and thus neither is the interface read
00191  * during read() nor is it written during write(). It is also not automatically
00192  * closed in the destructor.
00193  * @param varname the variable name of the interface
00194  * @param interface the interface to push
00195  */
00196 void
00197 LuaInterfaceImporter::add_interface(std::string varname, Interface *interface)
00198 {
00199   if ( interface->is_writer() ) {
00200     __ext_wifs[varname] = interface;
00201   } else {
00202     __ext_rifs[varname] = interface;
00203   }
00204 }
00205 
00206 
00207 void
00208 LuaInterfaceImporter::add_observed_interface(std::string varname,
00209                                              const char *type, const char *id)
00210 {
00211   try {
00212     if (__reading_multi_ifs.find(varname) == __reading_multi_ifs.end() ) {
00213       throw Exception("Notified about unknown interface varname %s", varname.c_str());
00214     }
00215     Interface *iface = __blackboard->open_for_reading(type, id);
00216     __context->add_package((std::string("interfaces.") + iface->type()).c_str());
00217     __reading_multi_ifs[varname].push_back(iface);
00218     __context->get_global("interfaces");                        // it
00219     __context->get_field(-1, "reading");                        // it rt
00220     __context->get_field(-1, varname.c_str());                  // it rt vt
00221     __context->push_usertype(iface, iface->type(), "fawkes");   // it rt vt iface
00222     __context->raw_seti(-2, __reading_multi_ifs[varname].size()); // it rt vt
00223     __context->push_usertype(iface, iface->type(), "fawkes");   // it rt vt iface
00224     __context->set_field(iface->uid(), -2);                     // it rt vt
00225     __context->pop(3);                                          // ---
00226  } catch (Exception &e) {
00227     __logger->log_warn("LuaInterfaceImporter", "Failed to add observed interface "
00228                        "%s:%s, exception follows", type, id);
00229     __logger->log_warn("LuaInterfaceImporter", e);    
00230   }
00231 }
00232 
00233 
00234 /** Close interfaces for reading. */
00235 void
00236 LuaInterfaceImporter::close_reading_interfaces()
00237 {
00238   for (InterfaceMap::iterator i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
00239     __blackboard->close(i->second);
00240   }
00241   __reading_ifs.clear();
00242 
00243   for (ObserverMap::iterator o = __observers.begin(); o != __observers.end(); ++o) {
00244     __blackboard->unregister_observer(o->second);
00245     delete o->second;
00246   }
00247   __observers.clear();
00248 
00249   for (InterfaceListMap::iterator i = __reading_multi_ifs.begin(); i != __reading_multi_ifs.end(); ++i) {
00250     for (std::list<Interface *>::iterator j = i->second.begin(); j != i->second.end(); ++j) {
00251       __blackboard->close(*j);
00252     }
00253   }
00254   __reading_multi_ifs.clear();
00255 }
00256 
00257 
00258 /** Close interfaces for writing. */
00259 void
00260 LuaInterfaceImporter::close_writing_interfaces()
00261 {
00262   for (InterfaceMap::iterator i = __writing_ifs.begin(); i != __writing_ifs.end(); ++i) {
00263     __blackboard->close(i->second);
00264   }
00265   __writing_ifs.clear();
00266 }
00267 
00268 /** Get interface map of reading interfaces.
00269  * @return interface map of reading interfaces
00270  */
00271 LuaInterfaceImporter::InterfaceMap &
00272 LuaInterfaceImporter::reading_interfaces()
00273 {
00274   return __reading_ifs;
00275 }
00276 
00277 
00278 /** Get interface map of writing interfaces.
00279  * @return interface map of writing interfaces
00280  */
00281 LuaInterfaceImporter::InterfaceMap &
00282 LuaInterfaceImporter::writing_interfaces()
00283 {
00284   return __writing_ifs;
00285 }
00286 
00287 
00288 /** Read from all reading interfaces. */
00289 void
00290 LuaInterfaceImporter::read()
00291 {
00292   for (InterfaceMap::iterator i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
00293     i->second->read();
00294   }
00295 }
00296 
00297 
00298 /** Read from all reading interfaces into a buffer.
00299  */
00300 void
00301 LuaInterfaceImporter::read_to_buffer()
00302 {
00303   InterfaceMap::iterator i;
00304   if (! __two_stage) {
00305     for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
00306       i->second->resize_buffers(1);
00307     }
00308     __two_stage = true;
00309   }
00310   for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
00311     i->second->copy_shared_to_buffer(0);
00312   }
00313 }
00314 
00315 /** Update interfaces from internal buffers.
00316  * @exception Exception thrown if read_to_buffer() was not called
00317  * before.
00318  */
00319 void
00320 LuaInterfaceImporter::read_from_buffer()
00321 {
00322   if (! __two_stage) {
00323     throw Exception("LuaInterfaceImporter: trying to read buffer witout "
00324                     "previous read_to_buffer()");
00325   }
00326   InterfaceMap::iterator i;
00327   for (i = __reading_ifs.begin(); i != __reading_ifs.end(); ++i) {
00328     i->second->read_from_buffer(0);
00329   }
00330 }
00331 
00332 /** Write all writing interfaces. */
00333 void
00334 LuaInterfaceImporter::write()
00335 {
00336   for (InterfaceMap::iterator i = __writing_ifs.begin(); i != __writing_ifs.end(); ++i) {
00337     try {
00338       i->second->write();
00339     } catch (Exception &e) {
00340       e.append("Failed to write interface %s, ignoring.", i->second->uid());
00341       e.print_trace();
00342     }
00343   }
00344 }
00345 
00346 void
00347 LuaInterfaceImporter::push_interfaces_varname(LuaContext *context, InterfaceMap &imap)
00348 {
00349   InterfaceMap::iterator imi;
00350   for (imi = imap.begin(); imi != imap.end(); ++imi) {
00351     context->add_package((std::string("interfaces.") + imi->second->type()).c_str());
00352     context->push_usertype(imi->second, imi->second->type(), "fawkes");
00353     context->set_field(imi->first.c_str());
00354   }
00355 }
00356 
00357 void
00358 LuaInterfaceImporter::push_multi_interfaces_varname(LuaContext *context, InterfaceListMap &imap)
00359 {
00360   InterfaceListMap::iterator imi;
00361   for (imi = imap.begin(); imi != imap.end(); ++imi) {
00362     context->create_table(0, imi->second.size());
00363     int idx = 0;
00364     for (std::list<Interface *>::iterator i = imi->second.begin(); i != imi->second.end(); ++i) {
00365       context->add_package((std::string("interfaces.") + (*i)->type()).c_str());
00366       context->push_usertype(*i, (*i)->type(), "fawkes");
00367       context->raw_seti(-2, ++idx);
00368       context->push_usertype(*i, (*i)->type(), "fawkes");
00369       context->set_field((*i)->uid(), -2);
00370     }
00371     context->set_field(imi->first.c_str());
00372   }
00373 }
00374 
00375 void
00376 LuaInterfaceImporter::push_interfaces_uid(LuaContext *context, InterfaceMap &imap)
00377 {
00378   InterfaceMap::iterator imi;
00379   for (imi = imap.begin(); imi != imap.end(); ++imi) {
00380     context->add_package((std::string("interfaces.") + imi->second->type()).c_str());
00381     context->push_usertype(imi->second, imi->second->type(), "fawkes");
00382     context->set_field(imi->second->uid());
00383   }
00384 }
00385 
00386 void
00387 LuaInterfaceImporter::push_interfaces(LuaContext *context)
00388 {
00389 
00390   // it: interface table, rt: reading table, wt: writing table, rtu: rt by uid, wtu: wt by uid
00391   context->create_table(0, 4);                          // it
00392 
00393   context->create_table(0, __reading_ifs.size() + __ext_rifs.size());   // it rt
00394   push_interfaces_varname(context, __reading_ifs);      // it rt
00395   push_interfaces_varname(context, __ext_rifs);         // it rt
00396   push_multi_interfaces_varname(context, __reading_multi_ifs);  // it rt
00397   context->set_field("reading");                        // it
00398 
00399   context->create_table(0, __reading_ifs.size() + __ext_rifs.size());   // it rtu
00400   push_interfaces_uid(context, __reading_ifs);          // it rtu
00401   push_interfaces_uid(context, __ext_rifs);             // it rtu
00402   context->set_field("reading_by_uid");                 // it
00403 
00404   context->create_table(0, __writing_ifs.size() + __ext_wifs.size());   // it wt
00405   push_interfaces_varname(context, __writing_ifs);              // it wt
00406   push_interfaces_varname(context, __ext_wifs);         // it wt
00407   context->set_field("writing");                        // it
00408 
00409   context->create_table(0, __writing_ifs.size());       // it wtu
00410   push_interfaces_uid(context, __writing_ifs);          // it wtu
00411   push_interfaces_uid(context, __ext_wifs);             // it wtu
00412   context->set_field("writing_by_uid");                 // it
00413 
00414   context->set_global("interfaces");                    // ---
00415 }
00416 
00417 /** Push interfaces to Lua environment.
00418  * The interfaces are pushed to the interfaces table described in the class
00419  * documentation. Note that you need to do this only once. The table is
00420  * automatically re-pushed on a Lua restart event.
00421  */
00422 void
00423 LuaInterfaceImporter::push_interfaces()
00424 {
00425   __interfaces_pushed = true;
00426   push_interfaces(__context);
00427 }
00428 
00429 
00430 void
00431 LuaInterfaceImporter::lua_restarted(LuaContext *context)
00432 {
00433   try {
00434     if ( __interfaces_pushed ) {
00435       push_interfaces(context);
00436     }
00437   } catch (Exception &e) {
00438     __logger->log_warn("LuaInterfaceImporter", "Failed to re-push interfacs, exception follows");
00439     __logger->log_warn("LuaInterfaceImporter", e);
00440     throw;
00441   }
00442 }
00443 
00444 
00445 /** Constructor.
00446  * @param lii LuaInterfaceImporter instance this observer is assigned to
00447  * @param varname variable name
00448  * @param type type of interface
00449  * @param id_pattern ID pattern to observe
00450  */
00451 LuaInterfaceImporter::InterfaceObserver::InterfaceObserver(LuaInterfaceImporter *lii,
00452                                                            std::string varname,
00453                                                            const char *type, const char *id_pattern)
00454 {
00455   __lii = lii;
00456   __varname = varname;
00457 
00458   bbio_add_observed_create(type, id_pattern);
00459 }
00460 
00461 
00462 void
00463 LuaInterfaceImporter::InterfaceObserver::bb_interface_created(const char *type, const char *id) throw()
00464 {
00465   __lii->add_observed_interface(__varname, type, id);
00466 }
00467 
00468 } // end of namespace fawkes