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

Generated on Tue Feb 22 13:31:27 2011 for Fawkes API by  doxygen 1.4.7