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 
30 using 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 
51 void
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();
87  logger->log_info(name(),
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)) {
128  logger->log_error(name(),
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 
157 void
159 {
160  clips->assert_fact("(executive-finalize)");
161  clips->refresh_agenda();
162  clips->run();
163 }
164 
165 void
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 
180 std::string
181 ClipsExecutiveThread::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()) {
198  logger->log_warn(name(),
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:
223  logger->log_error(name(),
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 }
ClipsExecutiveThread::~ClipsExecutiveThread
virtual ~ClipsExecutiveThread()
Destructor.
Definition: clips_executive_thread.cpp:47
ClipsExecutiveThread::init
virtual void init()
Initialize the thread.
Definition: clips_executive_thread.cpp:52
fawkes::Configuration::ValueIterator::get_as_string
virtual std::string get_as_string() const =0
Get value as string.
fawkes::Configuration::get_bool
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
fawkes::Logger::log_info
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
fawkes::MutexLocker
Mutex locking helper.
Definition: mutex_locker.h:34
fawkes::LockPtr::objmutex_ptr
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
fawkes::BlockedTimingAspect
Thread aspect to use blocked timing.
Definition: blocked_timing.h:51
fawkes::Thread::name
const char * name() const
Get name of thread.
Definition: thread.h:100
fawkes::CLIPSAspect::clips
LockPtr< CLIPS::Environment > clips
CLIPS environment for exclusive usage.
Definition: clips.h:50
ClipsExecutiveThread::ClipsExecutiveThread
ClipsExecutiveThread()
Constructor.
Definition: clips_executive_thread.cpp:39
fawkes::Configuration::search
virtual ValueIterator * search(const char *path)=0
Iterator with search results.
fawkes::LoggingAspect::logger
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
fawkes::Logger::log_error
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
ClipsExecutiveThread::loop
virtual void loop()
Code to execute in the thread.
Definition: clips_executive_thread.cpp:166
fawkes
Fawkes library namespace.
fawkes::Logger::log_warn
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
fawkes::Configuration::get_strings
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
fawkes::ConfigurableAspect::config
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
fawkes::CLIPSAspect
Thread aspect to get access to a CLIPS environment.
Definition: clips.h:41
fawkes::Thread
Thread class encapsulation of pthreads.
Definition: thread.h:46
fawkes::Configuration::get_string
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
ClipsExecutiveThread::finalize
virtual void finalize()
Finalize the thread.
Definition: clips_executive_thread.cpp:158
fawkes::Configuration::ValueIterator::path
virtual const char * path() const =0
Path of value.
fawkes::Logger::log_debug
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
fawkes::Configuration::ValueIterator::next
virtual bool next()=0
Check if there is another element and advance to this if possible.
fawkes::Exception
Base class for exceptions in Fawkes.
Definition: exception.h:36