Fawkes API Fawkes Development Version
exog_manager.cpp
1/***************************************************************************
2 * exog_manager.cpp - Insert exog actions into Golog++
3 *
4 * Created: Mon 26 Aug 2019 CEST 15:38
5 * Copyright 2019 Victor Mataré <matare@fh-aachen.de>
6 ****************************************************************************/
7
8/* This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Library General Public License for more details.
17 *
18 * Read the full text in the LICENSE.GPL file in the doc directory.
19 */
20
21#include "exog_manager.h"
22
23#include "execution_thread.h"
24#include "utils.h"
25
26#include <core/exception.h>
27#include <golog++/model/grounding.h>
28#include <libs/interface/field_iterator.h>
29
30using namespace fawkes;
31using namespace gologpp;
32
33namespace fawkes {
34namespace gpp {
35
36const std::unordered_map<interface_fieldtype_t, std::string> ExogManager::iface_type_to_golog_type_{
37 {IFT_BOOL, BoolType::static_name()},
38 {IFT_BYTE, NumberType::static_name()},
39 {IFT_ENUM, SymbolType::static_name()},
40 {IFT_INT8, NumberType::static_name()},
41 {IFT_FLOAT, NumberType::static_name()},
42 {IFT_INT16, NumberType::static_name()},
43 {IFT_INT32, NumberType::static_name()},
44 {IFT_INT64, NumberType::static_name()},
45 {IFT_UINT8, NumberType::static_name()},
46 {IFT_DOUBLE, NumberType::static_name()},
47 {IFT_STRING, StringType::static_name()},
48 {IFT_UINT16, NumberType::static_name()},
49 {IFT_UINT32, NumberType::static_name()},
50 {IFT_UINT64, NumberType::static_name()}};
51
52/** @class ExogManager
53 * Watch/observe blackboard interfaces according to the mappings
54 * specified for exogenous actions in the agent program. The config
55 * has to specify whether some mapped backend name is supposed to be
56 * an interface ID or a pattern.
57 */
58
59/** @class ConfigError
60 * Thrown if the config is somehow inconsistent with the agent program.
61 */
62
63/** Construct a ConfigError.
64 * @param msg A message describing the error.
65 */
66ConfigError::ConfigError(const std::string &msg) : Exception(msg.c_str())
67{
68}
69
70/** Constructor.
71 * Construct an ExogManager.
72 * @param exec_thread The Golog++ ExecutionContext to use
73 * @param config The Fawkes configuration to read config values from
74 * @param cfg_prefix The spec-specific config prefix to use
75 * @param blackboard The blackboard to use to read data from
76 * @param logger A logger instance to use for logging messages
77 */
79 Configuration * config,
80 const std::string &cfg_prefix,
81 BlackBoard * blackboard,
82 Logger * logger)
83: exec_thread_(exec_thread), config_(config), blackboard_(blackboard), logger_(logger)
84{
85 // Build a map to lookup all exog actions by their mapped name (i.e. the
86 // interface ID or pattern)
87 for (shared_ptr<Global> g : global_scope().globals()) {
88 shared_ptr<ExogAction> exog = std::dynamic_pointer_cast<ExogAction>(g);
89 if (exog)
90 mapped_exogs_.emplace(exog->mapping().backend_name(), exog);
91 }
92
93 // Register an InterfaceWatcher and a PatternObserver for each
94 // watched/observed interface (pattern). These also implement the event
95 // handlers.
96 for (const string &id :
97 config->get_strings_or_defaults((cfg_prefix + "/blackboard/watch").c_str(), {})) {
98 shared_ptr<ExogAction> exog = find_mapped_exog(id);
99 watchers_.push_back(std::make_unique<InterfaceWatcher>(blackboard, id, exog, *this));
100 }
101
102 for (const string &pattern :
103 config->get_strings_or_defaults((cfg_prefix + "/blackboard/observe").c_str(), {})) {
104 shared_ptr<ExogAction> exog = find_mapped_exog(pattern);
105 observers_.push_back(std::make_unique<PatternObserver>(blackboard, pattern, exog, *this));
106 }
107}
108
109/** Get the ExogManager's thread name.
110 * @return the thread name
111 */
112const char *
114{
115 return "ExogManager";
116}
117
118shared_ptr<ExogAction>
119ExogManager::find_mapped_exog(const std::string &mapped_name)
120{
121 auto map_it = mapped_exogs_.find(mapped_name);
122
123 if (map_it == mapped_exogs_.end())
124 throw ConfigError("No exogenous action handles " + mapped_name);
125
126 return map_it->second;
127}
128
129void
130ExogManager::exog_queue_push(shared_ptr<ExogEvent> evt)
131{
132 exec_thread_->gologpp_context().exog_queue_push(evt);
133}
134
135/** Construct an event handler.
136 * @param bb The fawkes blackboard to listen to
137 * @param exog The ExogAction to trigger on data change
138 * @param exog_mgr The ExogManager to send the ExogAction to
139 */
140ExogManager::BlackboardEventHandler::BlackboardEventHandler(
141 BlackBoard * bb,
142 gologpp::shared_ptr<gologpp::ExogAction> exog,
143 ExogManager & exog_mgr)
144: blackboard_(bb), target_exog_(exog), exog_manager_(exog_mgr)
145{
146 for (const auto &pair : target_exog_->mapping().arg_mapping()) {
147 /* TODO: This is not very nice. First we have to look up the index of the
148 * mapped parameter variable and then remember to put arg values at that
149 * index when an actual event happens. This is necessary because the
150 * ExogEvent (or rather the ReferenceBase) constructor accepts only vectors
151 * with positional arguments.
152 */
153
154 auto &var_ref = dynamic_cast<Reference<Variable> &>(*pair.second);
155
156 // Make sure argument types match interface
157 string iface_type = extract_type_name(target_exog_->mapping().backend_name());
158 Interface *iface =
159 blackboard_->open_for_reading(iface_type.c_str(), "/gologpp_interface_analysis");
161 for (fi = iface->fields(); fi != iface->fields_end(); ++fi) {
162 if (pair.first == fi.get_name())
163 break;
164 }
165 if (fi == iface->fields_end()) {
166 throw ConfigError("Interface type " + iface_type + " does not have a field `" + pair.first
167 + "'");
168 }
169 auto it = iface_type_to_golog_type_.find(fi.get_type());
170 if (it == iface_type_to_golog_type_.end()) {
171 throw Exception("Unhandled interface field type %s", fi.get_typename());
172 }
173
174 shared_ptr<const Type> desired_type = gologpp::global_scope().lookup_type(it->second);
175 if (!desired_type)
176 throw Exception("Type %s required for field %s is not defined in the golog++ code model",
177 it->second.c_str(),
178 pair.first.c_str());
179
180 if (!(*desired_type <= gologpp::get_type<StringType>()
181 || *desired_type >= gologpp::get_type<StringType>())
182 && fi.get_length() > 1) {
183 desired_type = gologpp::global_scope().lookup_list_type(*desired_type);
184 }
185
186 if (!(var_ref.type() <= *desired_type || var_ref.type() >= *desired_type)) {
187 throw ConfigError(target_exog_->name() + "'s argument " + var_ref.target()->name() + " is a "
188 + var_ref.type().name() + ", but the interface field requires "
189 + desired_type->name());
190 }
191 blackboard_->close(iface);
192
193 auto param_it =
194 std::find(target_exog_->params().begin(), target_exog_->params().end(), var_ref.target());
195 auto param_idx = param_it - target_exog_->params().begin();
196 fields_order_.emplace(pair.first, arity_t(param_idx));
197 }
198}
199
200std::string
201ExogManager::BlackboardEventHandler::extract_type_name(const std::string &iface_uid)
202{
203 auto idx = iface_uid.find("::");
204 if (idx == std::string::npos)
205 throw ConfigError(iface_uid + " is not an interface UID. Must be IFACE_TYPE::IFACE_ID.");
206 return iface_uid.substr(0, idx);
207}
208
209std::string
210ExogManager::BlackboardEventHandler::extract_id(const std::string &iface_uid)
211{
212 auto idx = iface_uid.find("::");
213 if (idx == std::string::npos)
214 throw ConfigError(iface_uid + " is not an interface UID. Must be IFACE_TYPE::IFACE_ID.");
215 return iface_uid.substr(idx + 2);
216}
217
218ExogManager::InterfaceWatcher::InterfaceWatcher(BlackBoard * bb,
219 const string & uid,
220 shared_ptr<ExogAction> exog,
221 ExogManager & exog_mgr)
222: BlackboardEventHandler(bb, exog, exog_mgr),
223 BlackBoardInterfaceListener("gologpp_blackboard_manager"),
224 iface_(blackboard_->open_for_reading(extract_type_name(uid).c_str(), extract_id(uid).c_str()))
225{
226 bbil_add_data_interface(iface_);
228}
229
230ExogManager::InterfaceWatcher::~InterfaceWatcher()
231{
232 blackboard_->unregister_listener(this);
233 blackboard_->close(iface_);
234}
235
236shared_ptr<ExogEvent>
237ExogManager::BlackboardEventHandler::make_exog_event(Interface *iface) const
238{
239 // clang-format off
240 // alignment of assignments just makes this unreadable
241 iface->read();
242 InterfaceFieldIterator fi = iface->fields();
243 vector<unique_ptr<Value>> args(target_exog_->arity());
244
245 while (fi != iface->fields_end()) {
246 if (target_exog_->mapping().is_mapped(fi.get_name())) {
247 auto order_it = fields_order_.find(fi.get_name());
248
249 if (fi.get_length() == 1 || (fi.get_length() > 1 && fi.get_type() == IFT_STRING))
250 args[order_it->second].reset(field_to_value(fi, 0));
251 else if (fi.get_length() > 1) {
252 vector<unique_ptr<Value>> list_init;
253 for (unsigned int idx = 0; idx < fi.get_length(); ++idx)
254 list_init.emplace_back(
255 field_to_value(fi, idx)
256 );
257 shared_ptr<const Type> list_type = global_scope().lookup_list_type(list_init[0]->type());
258 args[order_it->second].reset(
259 new Value(*list_type, list_init)
260 );
261 }
262 else
263 throw IllegalArgumentException("%s: Field %s has length %d and type %s, which shouldn't happen",
264 iface->uid(), fi.get_name(), fi.get_length(), fi.get_typename());
265 }
266 ++fi;
267 }
268
269 return std::make_shared<ExogEvent>(target_exog_, std::move(args));
270 // clang-format on
271}
272
273void
274ExogManager::InterfaceWatcher::bb_interface_data_refreshed(Interface *iface) noexcept
275{
276 try {
277 exog_manager_.exog_queue_push(make_exog_event(iface));
278 } catch (IllegalArgumentException &e) {
279 exog_manager_.logger_->log_error(exog_manager_.name(),
280 "Error when creating exogenous event: %s, ignoring event!",
282 } catch (gologpp::UserError &e) {
283 exog_manager_.logger_->log_error(exog_manager_.name(),
284 "Error when creating exogenous event: %s, ignoring event!",
285 e.what());
286 }
287}
288
289ExogManager::PatternObserver::PatternObserver(BlackBoard * bb,
290 const std::string & pattern,
291 shared_ptr<ExogAction> exog,
292 ExogManager & exog_mgr)
293: BlackboardEventHandler(bb, exog, exog_mgr)
294{
295 bbio_add_observed_create(extract_type_name(pattern).c_str(), extract_id(pattern).c_str());
296 blackboard_->register_observer(this);
297}
298
299ExogManager::PatternObserver::~PatternObserver()
300{
301 blackboard_->unregister_observer(this);
302}
303
304void
305ExogManager::PatternObserver::bb_interface_created(const char *type, const char *id) noexcept
306{
307 std::lock_guard<std::mutex> locked{handler_mutex_};
308 exog_manager_.watchers_.push_back(std::make_unique<InterfaceWatcher>(
309 blackboard_, string(type) + "::" + id, target_exog_, exog_manager_));
310}
311
312} // namespace gpp
313} // namespace fawkes
BlackBoard interface listener.
The BlackBoard abstract class.
Definition: blackboard.h:46
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
@ BBIL_FLAG_DATA
consider data events
Definition: blackboard.h:88
virtual void register_observer(BlackBoardInterfaceObserver *observer)
Register BB interface observer.
Definition: blackboard.cpp:225
virtual void register_listener(BlackBoardInterfaceListener *listener, ListenerRegisterFlag flag=BBIL_FLAG_ALL)
Register BB event listener.
Definition: blackboard.cpp:185
virtual void close(Interface *interface)=0
Close interface.
Interface for configuration handling.
Definition: config.h:68
virtual std::vector< std::string > get_strings_or_defaults(const char *path, const std::vector< std::string > &default_val)
Get list of values from configuration which is of type string, or the given default if the path does ...
Definition: config.cpp:786
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const noexcept
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
Expected parameter is missing.
Definition: software.h:80
Interface field iterator.
size_t get_length() const
Get length of current field.
interface_fieldtype_t get_type() const
Get type of current field.
const char * get_name() const
Get name of current field.
const char * get_typename() const
Get type of current field as string.
Base class for all Fawkes BlackBoard interfaces.
Definition: interface.h:80
InterfaceFieldIterator fields_end()
Invalid iterator.
Definition: interface.cpp:1240
InterfaceFieldIterator fields()
Get iterator over all fields of this interface instance.
Definition: interface.cpp:1231
const char * uid() const
Get unique identifier of interface.
Definition: interface.cpp:686
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:479
Interface for logging.
Definition: logger.h:42
Thrown if the config is somehow inconsistent with the agent program.
Definition: exog_manager.h:44
ConfigError(const std::string &)
Construct a ConfigError.
const char * name()
Get the ExogManager's thread name.
ExogManager(GologppThread *exec_thread, Configuration *, const std::string &cfg_prefix, BlackBoard *, Logger *)
Constructor.
Main golog++ thread that handles program execution, i.e.
gologpp::ExecutionContext & gologpp_context()
GologppThread::gologpp_context.
Fawkes library namespace.
@ IFT_INT8
8 bit integer field
Definition: types.h:38
@ IFT_UINT32
32 bit unsigned integer field
Definition: types.h:43
@ IFT_FLOAT
float field
Definition: types.h:46
@ IFT_BYTE
byte field, alias for uint8
Definition: types.h:49
@ IFT_UINT64
64 bit unsigned integer field
Definition: types.h:45
@ IFT_UINT16
16 bit unsigned integer field
Definition: types.h:41
@ IFT_INT32
32 bit integer field
Definition: types.h:42
@ IFT_INT64
64 bit integer field
Definition: types.h:44
@ IFT_DOUBLE
double field
Definition: types.h:47
@ IFT_INT16
16 bit integer field
Definition: types.h:40
@ IFT_STRING
string field
Definition: types.h:48
@ IFT_BOOL
boolean field
Definition: types.h:37
@ IFT_ENUM
field with interface specific enum type
Definition: types.h:50
@ IFT_UINT8
8 bit unsigned integer field
Definition: types.h:39