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
29using 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
50void
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")) {
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
153void
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
169void
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
253const char *
254ClipsAgentThread::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
264void
265ClipsAgentThread::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.