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())
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 }
char * skill_string() const
Get skill_string value.
virtual void log_info(const char *component, const char *format,...)=0
Log informational message.
Fawkes library namespace.
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
Mutex locking helper.
Definition: mutex_locker.h:33
A class for handling time.
Definition: time.h:92
ReleaseControlMessage Fawkes BlackBoard Interface Message.
SkillStatusEnum
This determines the current status of skill execution.
uint32_t exclusive_controller() const
Get exclusive_controller value.
Thread class encapsulation of pthreads.
Definition: thread.h:45
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
AcquireControlMessage Fawkes BlackBoard Interface Message.
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
Thread aspect to use blocked timing.
ClipsAgentThread()
Constructor.
SwitchInterface Fawkes BlackBoard Interface.
Base class for exceptions in Fawkes.
Definition: exception.h:35
unsigned short serial() const
Get instance serial of interface.
Definition: interface.cpp:683
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:472
Mutex * objmutex_ptr() const
Get object mutex.
Definition: lockptr.h:284
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:814
SkillStatusEnum status() const
Get status value.
ExecSkillMessage Fawkes BlackBoard Interface Message.
const char * name() const
Get name of thread.
Definition: thread.h:100
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.
bool is_enabled() const
Get enabled value.
LockPtr< CLIPS::Environment > clips
CLIPS environment for exclusive usage.
Definition: clips.h:50
unsigned int msgq_enqueue(Message *message)
Enqueue message at end of queue.
Definition: interface.cpp:879
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual Interface * open_for_reading(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for reading.
SkillerInterface Fawkes BlackBoard Interface.
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
virtual void init()
Initialize the thread.
virtual void finalize()
Finalize the thread.
virtual float get_float(const char *path)=0
Get value from configuration which is of type float.
Thread aspect to get access to a CLIPS environment.
Definition: clips.h:40
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
virtual void loop()
Code to execute in the thread.
virtual void close(Interface *interface)=0
Close interface.
virtual ~ClipsAgentThread()
Destructor.