Fawkes API Fawkes Development Version
clips_executive_thread.cpp
1
2/***************************************************************************
3 * clips_executive_thread.cpp - CLIPS executive
4 *
5 * Created: Tue Sep 19 12:00:06 2017
6 * Copyright 2006-2017 Tim Niemueller [www.niemueller.de]
7 ****************************************************************************/
8
9/* This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Library General Public License for more details.
18 *
19 * Read the full text in the LICENSE.GPL file in the doc directory.
20 */
21
22#include "clips_executive_thread.h"
23
24#include <core/threading/mutex_locker.h>
25#include <interfaces/SwitchInterface.h>
26#include <utils/misc/map_skill.h>
27#include <utils/misc/string_conversions.h>
28#include <utils/misc/string_split.h>
29
30using namespace fawkes;
31
32/** @class ClipsExecutiveThread "clips_executive_thread.h"
33 * Main thread of CLIPS-based executive.
34 *
35 * @author Tim Niemueller
36 */
37
38/** Constructor. */
40: Thread("ClipsExecutiveThread", Thread::OPMODE_WAITFORWAKEUP),
41 BlockedTimingAspect(BlockedTimingAspect::WAKEUP_HOOK_THINK),
42 CLIPSAspect("executive", "CLIPS (executive)")
43{
44}
45
46/** Destructor. */
48{
49}
50
51void
53{
54 cfg_assert_time_each_loop_ = false;
55 try {
56 cfg_assert_time_each_loop_ = config->get_bool("/clips-executive/assert-time-each-loop");
57 } catch (Exception &e) {
58 } // ignore, use default
59
60 std::vector<std::string> clips_dirs;
61 try {
62 clips_dirs = config->get_strings("/clips-executive/clips-dirs");
63 for (size_t i = 0; i < clips_dirs.size(); ++i) {
64 if (clips_dirs[i][clips_dirs[i].size() - 1] != '/') {
65 clips_dirs[i] += "/";
66 }
67 logger->log_debug(name(), "DIR: %s", clips_dirs[i].c_str());
68 }
69 } catch (Exception &e) {
70 } // ignore, use default
71 clips_dirs.insert(clips_dirs.begin(), std::string(SRCDIR) + "/clips/");
72
73 try {
74 std::string cfg_spec = config->get_string("/clips-executive/spec");
75
76 std::string action_mapping_cfgpath =
77 std::string("/clips-executive/specs/") + cfg_spec + "/action-mapping/";
78#if __cplusplus >= 201103L
79 std::unique_ptr<Configuration::ValueIterator> v(config->search(action_mapping_cfgpath.c_str()));
80#else
81 std::auto_ptr<Configuration::ValueIterator> v(config_->search(action_mapping_cfgpath.c_str()));
82#endif
83 std::map<std::string, std::string> mapping;
84 while (v->next()) {
85 std::string action_name = std::string(v->path()).substr(action_mapping_cfgpath.length());
86 mapping[action_name] = v->get_as_string();
88 "Adding action mapping '%s' -> '%s'",
89 action_name.c_str(),
90 v->get_as_string().c_str());
91 }
92
93 action_skill_mapping_ = std::make_shared<fawkes::ActionSkillMapping>(mapping);
94 } catch (Exception &e) {
95 } // ignore
96
98
99 clips->evaluate(std::string("(path-add-subst \"@BASEDIR@\" \"") + BASEDIR + "\")");
100 clips->evaluate(std::string("(path-add-subst \"@FAWKES_BASEDIR@\" \"") + FAWKES_BASEDIR + "\")");
101 clips->evaluate(std::string("(path-add-subst \"@RESDIR@\" \"") + RESDIR + "\")");
102 clips->evaluate(std::string("(path-add-subst \"@CONFDIR@\" \"") + CONFDIR + "\")");
103
104 for (size_t i = 0; i < clips_dirs.size(); ++i) {
105 clips->evaluate("(path-add \"" + clips_dirs[i] + "\")");
106 }
107
108 clips->evaluate("(ff-feature-request \"config\")");
109
110 clips->add_function("map-action-skill",
111 sigc::slot<std::string, std::string, CLIPS::Values, CLIPS::Values>(
112 sigc::mem_fun(*this, &ClipsExecutiveThread::clips_map_skill)));
113
114 bool cfg_req_redefwarn_feature = true;
115 try {
116 cfg_req_redefwarn_feature =
117 config->get_bool("/clips-executive/request-redefine-warning-feature");
118 } catch (Exception &e) {
119 } // ignored, use default
120 if (cfg_req_redefwarn_feature) {
121 logger->log_debug(name(), "Enabling warnings for redefinitions");
122 clips->evaluate("(ff-feature-request \"redefine-warning\")");
123 }
124
125 std::vector<std::string> files{SRCDIR "/clips/saliences.clp", SRCDIR "/clips/init.clp"};
126 for (const auto &f : files) {
127 if (!clips->batch_evaluate(f)) {
129 "Failed to initialize CLIPS environment, "
130 "batch file '%s' failed.",
131 f.c_str());
132 throw Exception("Failed to initialize CLIPS environment, batch file '%s' failed.", f.c_str());
133 }
134 }
135
136 clips->assert_fact("(executive-init)");
137 clips->refresh_agenda();
138 clips->run();
139
140 // Verify that initialization did not fail (yet)
141 {
142 CLIPS::Fact::pointer fact = clips->get_facts();
143 while (fact) {
144 CLIPS::Template::pointer tmpl = fact->get_template();
145 if (tmpl->name() == "executive-init-stage") {
146 CLIPS::Values v = fact->slot_value("");
147 if (v.size() > 0 && v[0].as_string() == "FAILED") {
148 throw Exception("CLIPS Executive initialization failed");
149 }
150 }
151
152 fact = fact->next();
153 }
154 }
155}
156
157void
159{
160 clips->assert_fact("(executive-finalize)");
161 clips->refresh_agenda();
162 clips->run();
163}
164
165void
167{
169
170 // might be used to trigger loop events
171 // must be cleaned up each loop from within the CLIPS code
172 if (cfg_assert_time_each_loop_) {
173 clips->assert_fact("(time (now))");
174 }
175
176 clips->refresh_agenda();
177 clips->run();
178}
179
180std::string
181ClipsExecutiveThread::clips_map_skill(std::string action_name,
182 CLIPS::Values param_names,
183 CLIPS::Values param_values)
184{
185 if (!action_skill_mapping_) {
186 logger->log_error(name(), "No action mapping has been loaded");
187 return "";
188 }
189 if (action_name == "") {
190 logger->log_warn(name(), "Failed to map, action name is empty");
191 return "";
192 }
193 if (!action_skill_mapping_->has_mapping(action_name)) {
194 logger->log_warn(name(), "No mapping for action '%s' known", action_name.c_str());
195 return "";
196 }
197 if (param_names.size() != param_values.size()) {
199 "Number of parameter names and values "
200 "do not match for action '%s'",
201 action_name.c_str());
202 return "";
203 }
204 std::map<std::string, std::string> param_map;
205 for (size_t i = 0; i < param_names.size(); ++i) {
206 if (param_names[i].type() != CLIPS::TYPE_SYMBOL
207 && param_names[i].type() != CLIPS::TYPE_STRING) {
208 logger->log_error(name(), "Param for '%s' is not a string or symbol", action_name.c_str());
209 return "";
210 }
211 switch (param_values[i].type()) {
212 case CLIPS::TYPE_FLOAT:
213 param_map[param_names[i].as_string()] = std::to_string(param_values[i].as_float());
214 break;
215 case CLIPS::TYPE_INTEGER:
216 param_map[param_names[i].as_string()] = std::to_string(param_values[i].as_integer());
217 break;
218 case CLIPS::TYPE_SYMBOL:
219 case CLIPS::TYPE_STRING:
220 param_map[param_names[i].as_string()] = param_values[i].as_string();
221 break;
222 default:
224 "Param '%s' for action '%s' of invalid type",
225 param_names[i].as_string().c_str(),
226 action_name.c_str());
227 break;
228 }
229 }
230
231 std::multimap<std::string, std::string> messages;
232 std::string rv = action_skill_mapping_->map_skill(action_name, param_map, messages);
233 for (auto &m : messages) {
234 if (m.first == "WARN") {
235 logger->log_warn(name(), "%s", m.second.c_str());
236 } else if (m.first == "ERROR") {
237 logger->log_error(name(), "%s", m.second.c_str());
238 } else if (m.first == "DEBUG") {
239 logger->log_debug(name(), "%s", m.second.c_str());
240 } else {
241 logger->log_info(name(), "%s", m.second.c_str());
242 }
243 }
244 return rv;
245}
virtual void loop()
Code to execute in the thread.
virtual void finalize()
Finalize the thread.
virtual void init()
Initialize the thread.
virtual ~ClipsExecutiveThread()
Destructor.
Thread aspect to use blocked timing.
Thread aspect to get access to a CLIPS environment.
Definition: clips.h:41
LockPtr< CLIPS::Environment > clips
CLIPS environment for exclusive usage.
Definition: clips.h:50
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
Mutex locking helper.
Definition: mutex_locker.h:34
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
Fawkes library namespace.