Fawkes API Fawkes Development Version
eclipse_thread.cpp
1
2/***************************************************************************
3 * eclipse_thread.cpp - Fawkes ECLiPSe Thread
4 *
5 * Created: Wed Jul 16 10:42:49 2009
6 * Copyright 2009 Daniel Beck
7 * 2013-2014 Gesche Gierse
8 * 2014 Tim Niemueller
9 ****************************************************************************/
10
11/* This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Library General Public License for more details.
20 *
21 * Read the full text in the LICENSE.GPL file in the doc directory.
22 */
23
24#include "eclipse_thread.h"
25
26#include "blackboard_listener_thread.h"
27#include "externals/blackboard.h"
28#include "externals/eclipse_path.h"
29#include "externals/eclipseclp_config.h"
30#include "externals/fawkes_logger.h"
31
32#include <core/exception.h>
33#include <core/threading/mutex_locker.h>
34#include <interfaces/TestInterface.h>
35
36#include <cstdio>
37#include <cstdlib>
38#include <cstring>
39#include <vector>
40
41namespace fawkes {
43};
44
45using namespace std;
46using namespace fawkes;
47
48/** @class EclipseAgentThread "eclipse_thread.h"
49 * This thread creates an ECLiPSe context in which the ECLiPSe
50 * interpreter and the program are loaded.
51 * @author Daniel Beck
52 */
53
54extern "C" int ec_external(dident, int (*)(...), dident);
55
56EclipseAgentThread *EclipseAgentThread::m_instance = NULL;
57
58/** Constructor. */
60: Thread("ECLiPSe thread", fawkes::Thread::OPMODE_CONTINUOUS), m_initialized(false)
61{
63 m_instance = this;
64 mutex = new fawkes::Mutex();
65}
66
67/** Destructor. */
69{
70 if (EclExternalBlackBoard::instance()) {
71 logger->log_info(name(), "Cleaning up");
72 EclExternalBlackBoard::cleanup_instance();
73 }
74 delete mutex;
75}
76
77void
79{
82 // set ECLiPSe installation directory
83 char *eclipse_dir = NULL;
84 try {
85 eclipse_dir = strdup(config->get_string("/eclipse-clp/eclipse_dir").c_str());
86 logger->log_info(name(), "Setting ECLIPSEDIR to %s", eclipse_dir);
87 ec_set_option_ptr(EC_OPTION_ECLIPSEDIR, (void *)eclipse_dir);
88 } catch (...) {
89 // ignore
90 }
91
92 agent = config->get_string("/eclipse-clp/agent");
93
94 try {
95 //set default module in which goals called from the top-level will be executed
96 ec_set_option_ptr(EC_OPTION_DEFAULT_MODULE, (void *)agent.c_str());
97
98 } catch (...) {
99 throw fawkes::Exception("Failed to set default ECLiPSe module");
100 }
101 // initialize ECLiPSe context
102 if (0 != ec_init()) {
103 throw fawkes::Exception("Failed to initialize ECLiPSe context");
104 }
105
106 free(eclipse_dir);
107
108 m_initialized = true;
109
110 std::vector<std::string> paths = config->get_strings("/eclipse-clp/file_path");
111
112 // initialise pathfinding utility
114 EclipsePath::instance()->add_regex(boost::regex("@AGENT@"), agent);
115 for (size_t i = 0; i < paths.size(); ++i) {
116 EclipsePath::instance()->add_path(paths[i]);
117 }
118
120
121 // debug
123
124 // make locate_file/2 available
125 std::string filepath_path = EclipsePath::instance()->locate_file("filepath.ecl");
126 if (filepath_path.empty()) {
127 throw Exception("Failed to determine path to filepath module");
128 }
129 load_file(filepath_path.c_str());
130 char *filepath = ::strdup("filepath");
131 post_goal(term(EC_functor(":", 2),
132 EC_atom(filepath),
133 term(EC_functor("add_library_path", 1),
134 ::list(EC_word(SRCDIR "/externals"),
135 ::list(EC_word(SRCDIR "/utils"),
136 ::list(EC_word(SRCDIR "/consoletool"),
137 ::list(EC_word(SRCDIR "/interpreter"), nil())))))));
138 if (EC_succeed != EC_resume())
139 throw Exception("Failed to add " SRCDIR "/externals to library path");
140
141 // check if navgraph is used and pass config value
142 if (config->get_bool(("/eclipse-clp/" + agent + "/use_graph").c_str())) {
143 graph_path =
144 CONFDIR + config->get_string(("/eclipse-clp/" + agent + "/rel_graph_path").c_str());
145
146 logger->log_info(name(), "Setting graph_path to %s", graph_path.c_str());
147 post_goal(term(EC_functor("load_graph", 1), graph_path.c_str()));
148 if (EC_succeed != EC_resume()) {
149 throw Exception("Error loading graph config to agent");
150 }
151 }
152
153 // load interpreter and agent
154 std::string agent_path = EclipsePath::instance()->locate_file(agent + ".ecl");
155 if (agent_path.empty()) {
156 throw Exception("Failed to determine path to agent module");
157 }
158 load_file(agent_path.c_str());
159
160 // register external predicates
161 if (EC_succeed != ec_external(ec_did("log", 2), p_log, ec_did(agent.c_str(), 0))) {
162 throw Exception("Registering external predicate log/2 failed");
163 }
164}
165
166void
168{
169 ec_cleanup();
170 if (EclExternalBlackBoard::instance())
172}
173
174void
176{
177 post_goal("run");
178 ec_result = EC_resume("init", ec_yield_reason);
179}
180
181void
183{
184 if (ec_result == EC_status::EC_yield) {
185 EC_word bb_updates(::nil());
186 if (EC_word(ec_yield_reason) == EC_atom("exogenous_update")) {
187 while (BlackboardListenerThread::instance()->event_pending())
188 bb_updates = ::list(bb_updates, *BlackboardListenerThread::instance()->event_pop());
189 } else if (BlackboardListenerThread::instance()->event_pending())
190 post_event("event_exogUpdate");
191
192 ec_result = EC_resume(bb_updates, ec_yield_reason);
193 } else {
194 if (ec_result == EC_status::EC_succeed)
195 logger->log_warn(name(), "Agent program terminated successfully.");
196 else
197 logger->log_error(name(), "Agent program failed.");
198
199 logger->log_warn(name(), "Stopping Agent thread.");
200 exit();
201 }
202}
203
204/** Post an event to the ECLiPSe context.
205 * @param event the name of the event
206 */
207void
209{
210 if (!m_initialized) {
211 return;
212 }
213
214 // send event to the interpreter
215 char *atom = strdup(event);
216 ::post_event(EC_atom(atom));
217 free(atom);
218}
219
220/** Load a file into the ECLiPSe context.
221 * @param filename the name of the file
222 * @return false if the ECLiPSe context hasn't been intialized yet
223 */
224bool
225EclipseAgentThread::load_file(const char *filename)
226{
227 if (!m_initialized) {
228 return false;
229 }
230
231 char *ensure_loaded = strdup("ensure_loaded");
232 post_goal(term(EC_functor(ensure_loaded, 1), filename));
233 free(ensure_loaded);
234
235 if (EC_succeed != ec_resume()) {
236 throw Exception("File %s could not be loaded", filename);
237 }
238
239 return true;
240}
241
242/** Get the logger.
243 * @return the logger
244 */
247{
248 return logger;
249}
250
251/** Get the EclipseAgentThread instance.
252 * @return the instance
253 */
256{
257 if (!m_instance) {
258 throw Exception("No instance of type EclipseThread instantiated");
259 }
260
261 return m_instance;
262}
static BlackboardListenerThread * instance()
Get the singleton instance of this thread.
This thread creates an ECLiPSe context in which the ECLiPSe interpreter and the program are loaded.
virtual void finalize()
Finalize the thread.
static EclipseAgentThread * instance()
Get the EclipseAgentThread instance.
void post_event(const char *)
Post an event to the ECLiPSe context.
fawkes::Logger * get_logger()
Get the logger.
virtual void init()
Initialize the thread.
virtual void loop()
Code to execute in the thread.
virtual ~EclipseAgentThread()
Destructor.
virtual void once()
Execute an action exactly once.
EclipseAgentThread()
Constructor.
void add_path(const std::string &path)
Add a new path.
static EclipsePath * instance()
Get the EclipsePath instance.
std::string locate_file(const std::string &filename)
Locate a file by filename.
static void create_initial_object()
Create the initial EclipsePath object.
void print_all_paths()
Debug method to print all path to the command line.
void apply_regexes()
Apply the regexes to all paths.
void add_regex(boost::regex re, const std::string &str)
Add a regex.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
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 std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
Wrapper class for using the blackboard in the implementation of the external predicates.
Definition: blackboard.h:40
static void create_initial_object(BlackBoard *bb, Logger *logger)
Creates the initial EclExternalBlackBoard object.
Definition: blackboard.cpp:64
static void cleanup_instance()
Delete the current EclExternalBlackBoard instance and set it to NULL.
Definition: blackboard.cpp:71
static void create_initial_object(Configuration *config)
Creates the initial EclExternalConfig object.
Base class for exceptions in Fawkes.
Definition: exception.h:36
Interface for logging.
Definition: logger.h:42
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 mutual exclusion lock.
Definition: mutex.h:33
Thread class encapsulation of pthreads.
Definition: thread.h:46
void set_prepfin_conc_loop(bool concurrent=true)
Set concurrent execution of prepare_finalize() and loop().
Definition: thread.cpp:716
const char * name() const
Get name of thread.
Definition: thread.h:100
void exit()
Exit the thread.
Definition: thread.cpp:582
Fawkes library namespace.