Fawkes API Fawkes Development Version
|
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