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
32namespace 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 */
82void
83LuaInterfaceImporter::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 */
175void
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 */
184void
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 */
199void
200LuaInterfaceImporter::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
209void
210LuaInterfaceImporter::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. */
238void
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. */
262void
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. */
290void
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 */
300void
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 */
319void
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. */
333void
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
346void
347LuaInterfaceImporter::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
357void
358LuaInterfaceImporter::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
375void
376LuaInterfaceImporter::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
386void
387LuaInterfaceImporter::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 */
421void
423{
424 interfaces_pushed_ = true;
425 push_interfaces(context_);
426}
427
428void
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 */
448LuaInterfaceImporter::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
459void
460LuaInterfaceImporter::InterfaceObserver::bb_interface_created(const char *type,
461 const char *id) noexcept
462{
463 lii_->add_observed_interface(varname_, type, id);
464}
465
466} // end of namespace fawkes
The BlackBoard abstract class.
Definition: blackboard.h:46
virtual void unregister_observer(BlackBoardInterfaceObserver *observer)
Unregister BB interface observer.
Definition: blackboard.cpp:240
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
virtual void register_observer(BlackBoardInterfaceObserver *observer)
Register BB interface observer.
Definition: blackboard.cpp:225
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
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.
virtual void close(Interface *interface)=0
Close interface.
Iterator interface to iterate over config values.
Definition: config.h:75
virtual const char * path() const =0
Path of value.
virtual bool next()=0
Check if there is another element and advance to this if possible.
virtual const char * type() const =0
Type of value.
virtual std::string get_string() const =0
Get string value.
Interface for configuration handling.
Definition: config.h:68
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
Base class for exceptions in Fawkes.
Definition: exception.h:36
void print_trace() noexcept
Prints trace to stderr.
Definition: exception.cpp:601
void append(const char *format,...) noexcept
Append messages to the message list.
Definition: exception.cpp:333
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
void resize_buffers(unsigned int num_buffers)
Resize buffer array.
Definition: interface.cpp:1261
bool is_writer() const
Check if this is a writing instance.
Definition: interface.cpp:445
Interface for logging.
Definition: logger.h:42
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Lua C++ wrapper.
Definition: context.h:44
void get_global(const char *name)
Get global variable.
Definition: context.cpp:1068
void remove_watcher(LuaContextWatcher *watcher)
Remove a context watcher.
Definition: context.cpp:1328
void set_field(const char *key, int t_index=-2)
Set field of a table.
Definition: context.cpp:980
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 raw_seti(int idx, int n)
Set indexed value without invoking meta methods.
Definition: context.cpp:1038
void pop(int n)
Pop value(s) from stack.
Definition: context.cpp:918
void add_watcher(LuaContextWatcher *watcher)
Add a context watcher.
Definition: context.cpp:1319
void add_package(const char *package)
Add a default package.
Definition: context.cpp:386
void get_field(int idx, const char *k)
Get named value from table.
Definition: context.cpp:1016
Lua interface importer.
LuaInterfaceImporter::InterfaceMap & reading_interfaces()
Get interface map of reading interfaces.
void read()
Read from all reading interfaces.
void add_interface(std::string varname, Interface *interface)
Add a single interface to be pushed to the context.
LuaInterfaceImporter::InterfaceMap & writing_interfaces()
Get interface map of writing interfaces.
void close_writing_interfaces()
Close interfaces for writing.
LuaInterfaceImporter(LuaContext *context_, BlackBoard *blackboard, Configuration *config, Logger *logger)
Constructor.
void push_interfaces()
Push interfaces to Lua environment.
void lua_restarted(LuaContext *context)
Lua restart event.
void read_from_buffer()
Update interfaces from internal buffers.
void read_to_buffer()
Read from all reading interfaces into a buffer.
void open_writing_interfaces(std::string &prefix)
Open interfaces for writing.
void close_reading_interfaces()
Close interfaces for reading.
void write()
Write all writing interfaces.
void open_reading_interfaces(std::string &prefix)
Open interfaces for reading.
A NULL pointer was supplied where not allowed.
Definition: software.h:32
Fawkes library namespace.