Fawkes API  Fawkes Development Version
clips_agent_thread.cpp
1 
2 /***************************************************************************
3  * clips_agent_thread.cpp - CLIPS-based agent main thread
4  *
5  * Created: Sat Jun 16 14:40:56 2012 (Mexico City)
6  * Copyright 2006-2012 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_agent_thread.h"
23 
24 #include <core/threading/mutex_locker.h>
25 #include <interfaces/SwitchInterface.h>
26 #include <utils/misc/string_conversions.h>
27 #include <utils/misc/string_split.h>
28 
29 using namespace fawkes;
30 
31 /** @class ClipsAgentThread "clips_agent_thread.h"
32  * Main thread of CLIPS-based agent.
33  *
34  * @author Tim Niemueller
35  */
36 
37 /** Constructor. */
39 : Thread("ClipsAgentThread", Thread::OPMODE_WAITFORWAKEUP),
40  BlockedTimingAspect(BlockedTimingAspect::WAKEUP_HOOK_THINK),
41  CLIPSAspect("agent", "CLIPS (agent)")
42 {
43 }
44 
45 /** Destructor. */
47 {
48 }
49 
50 void
52 {
53  skiller_if_ = NULL;
54 
55  cfg_auto_start_ = false;
56  cfg_assert_time_each_loop_ = false;
57  cfg_skill_sim_time_ = 2.0;
58  cfg_skill_sim_ = false;
59  cfg_steal_skiller_control_ = true;
60 
61  try {
62  cfg_auto_start_ = config->get_bool("/clips-agent/auto-start");
63  } catch (Exception &e) {
64  } // ignore, use default
65  try {
66  cfg_assert_time_each_loop_ = config->get_bool("/clips-agent/assert-time-each-loop");
67  } catch (Exception &e) {
68  } // ignore, use default
69  try {
70  cfg_skill_sim_ = config->get_bool("/clips-agent/skill-sim");
71  } catch (Exception &e) {
72  } // ignore, use default
73  try {
74  cfg_skill_sim_time_ = config->get_float("/clips-agent/skill-sim-time");
75  } catch (Exception &e) {
76  } // ignore, use default
77  try {
78  cfg_steal_skiller_control_ = config->get_bool("/clips-agent/steal-skiller-control");
79  } catch (Exception &e) {
80  } // ignore, use default
81 
82  std::vector<std::string> clips_dirs;
83  try {
84  clips_dirs = config->get_strings("/clips-agent/clips-dirs");
85  for (size_t i = 0; i < clips_dirs.size(); ++i) {
86  if (clips_dirs[i][clips_dirs[i].size() - 1] != '/') {
87  clips_dirs[i] += "/";
88  }
89  logger->log_debug(name(), "DIR: %s", clips_dirs[i].c_str());
90  }
91  } catch (Exception &e) {
92  } // ignore, use default
93  clips_dirs.insert(clips_dirs.begin(), std::string(SRCDIR) + "/clips/");
94 
95  if (!cfg_skill_sim_) {
96  skiller_if_ = blackboard->open_for_reading<SkillerInterface>("Skiller");
97 
98  if (!skiller_if_->has_writer()) {
99  blackboard->close(skiller_if_);
100  throw Exception("Skiller has no writer, aborting");
101 
102  } else if (skiller_if_->exclusive_controller() != 0) {
103  blackboard->close(skiller_if_);
104  throw Exception("Skiller already has a different exclusive controller");
105  }
106  }
107 
108  switch_if_ = blackboard->open_for_reading<SwitchInterface>("Clips Agent Start");
109 
111 
112  clips->evaluate(std::string("(path-add-subst \"@BASEDIR@\" \"") + BASEDIR + "\")");
113  clips->evaluate(std::string("(path-add-subst \"@FAWKES_BASEDIR@\" \"") + FAWKES_BASEDIR + "\")");
114  clips->evaluate(std::string("(path-add-subst \"@RESDIR@\" \"") + RESDIR + "\")");
115  clips->evaluate(std::string("(path-add-subst \"@CONFDIR@\" \"") + CONFDIR + "\")");
116 
117  for (size_t i = 0; i < clips_dirs.size(); ++i) {
118  clips->evaluate("(path-add \"" + clips_dirs[i] + "\")");
119  }
120 
121  clips->add_function("skill-call-ext",
122  sigc::slot<void, std::string, std::string>(
123  sigc::mem_fun(*this, &ClipsAgentThread::clips_skill_call_ext)));
124 
125  clips->evaluate("(ff-feature-request \"config\")");
126 
127  bool cfg_req_redefwarn_feature = true;
128  try {
129  cfg_req_redefwarn_feature = config->get_bool("/clips-agent/request-redefine-warning-feature");
130  } catch (Exception &e) {
131  } // ignored, use default
132  if (cfg_req_redefwarn_feature) {
133  logger->log_debug(name(), "Enabling warnings for redefinitions");
134  clips->evaluate("(ff-feature-request \"redefine-warning\")");
135  }
136 
137  if (!clips->batch_evaluate(SRCDIR "/clips/init.clp")) {
138  logger->log_error(name(),
139  "Failed to initialize CLIPS environment, "
140  "batch file failed.");
141  blackboard->close(skiller_if_);
142  throw Exception("Failed to initialize CLIPS environment, batch file failed.");
143  }
144 
145  clips->assert_fact("(agent-init)");
146  clips->refresh_agenda();
147  clips->run();
148 
149  ctrl_recheck_ = true;
150  started_ = false;
151 }
152 
153 void
155 {
157 
158  clips->remove_function("skill-call-ext");
159 
160  if (!cfg_skill_sim_ && skiller_if_->has_writer()) {
162  skiller_if_->msgq_enqueue(msg);
163  }
164 
165  blackboard->close(skiller_if_);
166  blackboard->close(switch_if_);
167 }
168 
169 void
171 {
173 
174  if (!started_ && cfg_auto_start_) {
175  clips->assert_fact("(start)");
176  started_ = true;
177  }
178 
179  if (!cfg_skill_sim_) {
180  skiller_if_->read();
181 
182  if ((skiller_if_->exclusive_controller() != skiller_if_->serial().get_string())
183  && skiller_if_->has_writer()) {
184  if (ctrl_recheck_) {
185  logger->log_info(name(), "Acquiring exclusive skiller control");
187  new SkillerInterface::AcquireControlMessage(cfg_steal_skiller_control_);
188  skiller_if_->msgq_enqueue(msg);
189  ctrl_recheck_ = false;
190  } else {
191  ctrl_recheck_ = true;
192  }
193  return;
194  }
195  }
196 
197  if (!started_) {
198  switch_if_->read();
199  if (switch_if_->is_enabled()) {
200  clips->assert_fact("(start)");
201  started_ = true;
202  }
203  }
204 
205  // might be used to trigger loop events
206  // must be cleaned up each loop from within the CLIPS code
207  if (cfg_assert_time_each_loop_) {
208  clips->assert_fact("(time (now))");
209  }
210 
211  Time now(clock);
212  if (!active_skills_.empty()) {
213  if (!cfg_skill_sim_)
214  skiller_if_->read();
215 
216  std::list<std::string> finished_skills;
217  std::map<std::string, SkillExecInfo>::iterator as;
218  for (as = active_skills_.begin(); as != active_skills_.end(); ++as) {
219  const std::string & as_name = as->first;
220  const SkillExecInfo &as_info = as->second;
221 
222  if (cfg_skill_sim_) {
223  if ((now - as_info.start_time) >= cfg_skill_sim_time_) {
224  logger->log_warn(name(), "Simulated skill '%s' final", as_name.c_str());
225  clips->assert_fact_f("(skill-update (name \"%s\") (status FINAL))", as_name.c_str());
226  finished_skills.push_back(as_name);
227  } else {
228  clips->assert_fact_f("(skill-update (name \"%s\") (status RUNNING))", as_name.c_str());
229  }
230  } else {
231  if (as_info.skill_string == skiller_if_->skill_string()) {
232  clips->assert_fact_f("(skill-update (name \"%s\") (status %s))",
233  as_name.c_str(),
234  status_string(skiller_if_->status()));
235  if (skiller_if_->status() == SkillerInterface::S_FINAL
236  || skiller_if_->status() == SkillerInterface::S_FAILED) {
237  finished_skills.push_back(as_name);
238  }
239  }
240  }
241  }
242 
243  std::list<std::string>::iterator fs;
244  for (fs = finished_skills.begin(); fs != finished_skills.end(); ++fs) {
245  active_skills_.erase(*fs);
246  }
247  }
248 
249  clips->refresh_agenda();
250  clips->run();
251 }
252 
253 const char *
254 ClipsAgentThread::status_string(SkillerInterface::SkillStatusEnum status)
255 {
256  switch (status) {
257  case SkillerInterface::S_FINAL: return "FINAL";
258  case SkillerInterface::S_FAILED: return "FAILED";
259  case SkillerInterface::S_RUNNING: return "RUNNING";
260  default: return "IDLE";
261  }
262 }
263 
264 void
265 ClipsAgentThread::clips_skill_call_ext(std::string skill_name, std::string skill_string)
266 {
267  if (active_skills_.find(skill_name) != active_skills_.end()) {
268  logger->log_warn(name(), "Skill %s called again while already active", skill_name.c_str());
269  }
270 
271  if (cfg_skill_sim_) {
272  logger->log_info(name(), "Simulating skill %s", skill_string.c_str());
273 
274  } else {
275  logger->log_info(name(), "Calling skill %s", skill_string.c_str());
276 
278  new SkillerInterface::ExecSkillMessage(skill_string.c_str());
279 
280  skiller_if_->msgq_enqueue(msg);
281  }
282 
283  SkillExecInfo sei;
284  sei.start_time = clock->now();
285  sei.skill_string = skill_string;
286  active_skills_[skill_name] = sei;
287 }
virtual void finalize()
Finalize the thread.
ClipsAgentThread()
Constructor.
virtual void loop()
Code to execute in the thread.
virtual void init()
Initialize the thread.
virtual ~ClipsAgentThread()
Destructor.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
virtual void close(Interface *interface)=0
Close interface.
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
Clock * clock
By means of this member access to the clock is given.
Definition: clock.h:42
Time now() const
Get the current time.
Definition: clock.cpp:242
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 float get_float(const char *path)=0
Get value from configuration which is of type float.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
Base class for exceptions in Fawkes.
Definition: exception.h:36
unsigned int msgq_enqueue(Message *message, bool proxy=false)
Enqueue message at end of queue.
Definition: interface.cpp:915
Uuid serial() const
Get instance serial of interface.
Definition: interface.cpp:695
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:479
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:848
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
AcquireControlMessage Fawkes BlackBoard Interface Message.
ExecSkillMessage Fawkes BlackBoard Interface Message.
ReleaseControlMessage Fawkes BlackBoard Interface Message.
SkillerInterface Fawkes BlackBoard Interface.
SkillStatusEnum status() const
Get status value.
SkillStatusEnum
This determines the current status of skill execution.
char * skill_string() const
Get skill_string value.
char * exclusive_controller() const
Get exclusive_controller value.
SwitchInterface Fawkes BlackBoard Interface.
bool is_enabled() const
Get enabled value.
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
A class for handling time.
Definition: time.h:93
std::string get_string() const
Get the string representation of the Uuid.
Definition: uuid.cpp:107
Fawkes library namespace.