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