Fawkes API  Fawkes Development Version
hardware_models_thread.cpp
1 
2 /***************************************************************************
3  * hardware_models_thread.cpp - Hardware Models
4  *
5  * Created: Sun Mar 24 12:00:06 2019
6  * Copyright 2019 Daniel Habering (daniel@habering.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 "hardware_models_thread.h"
23 
24 #include <config/yaml.h>
25 #include <core/threading/mutex_locker.h>
26 #include <interfaces/SwitchInterface.h>
27 #include <utils/misc/map_skill.h>
28 #include <utils/misc/string_conversions.h>
29 #include <utils/misc/string_split.h>
30 #include <utils/time/wait.h>
31 
32 #include <unistd.h>
33 
34 using namespace fawkes;
35 
36 /** @class HardwareModelsThread "hardware_models_thread.h"
37  * Main thread of Hardware Models Plugin.
38  *
39  * Parses yaml files that are assumend to have the following format:
40  * COMPONENT-NAME:
41  * states:
42  * \verbatim<List of states, the first state is assumed to be the initial state>\endverbatim
43  *
44  * STATE_1:
45  * edges:
46  * \verbatim<List of states the state has an edge to>\endverbatim
47  * edge_1:
48  * transition: \verbatim<name of the action that causes the transition>\endverbatim
49  * (optional)probability: \verbatim<probability of the transition if it is an exogenous action>\endverbatim
50  *
51  * Any component plugin can inform the HardwareModel Plugin about state changes
52  * by sending a HardwareModelInterfaceMessage.
53  * @author Daniel Habering
54  */
55 
56 /** Constructor. */
58 : Thread("HardwareModelsThread", Thread::OPMODE_WAITFORWAKEUP),
59  CLIPSFeature("hardware-models"),
60  CLIPSFeatureAspect(this),
61  BlackBoardInterfaceListener("HardwareModelsThread")
62 
63 {
64 }
65 
66 void
68 {
69  std::string cfg_interface_ = "HardwareModels";
70  if (config->exists("/hardware-models/interface")) {
71  cfg_interface_ = config->get_string("/hardware-models/interface");
72  }
73 
74  hm_if_ = blackboard->open_for_writing<HardwareModelsInterface>(cfg_interface_.c_str());
76  wakeup();
78 }
79 
80 /**
81  * @brief Initializes hardware components from yaml files for a clips environment
82  *
83  * @param env_name Name of clips environment
84  * @param clips Pointer to clips environment
85  */
86 void
87 HardwareModelsThread::clips_context_init(const std::string & env_name,
89 {
90  envs_[env_name] = clips;
91 
92  clips->batch_evaluate(SRCDIR "/hardware_models.clp");
93 
94  components_ = config->get_strings("/hardware-models/components");
95  for (const auto &c : components_) {
96  if (!config->exists(std::string(c + "/states").c_str())) {
97  logger->log_warn(name(), "Component config is missing for %s", c.c_str());
98  continue;
99  }
100 
101  //First state in the config file is assumend to be the initial state
102  std::vector<std::string> states = config->get_strings(std::string(c + "/states").c_str());
103 
104  //Check if any states are defined
105  if (states.empty()) {
106  logger->log_warn(name(), "No states for component %s", c.c_str());
107  continue;
108  }
109 
110  for (const auto &state : states) {
111  // Check if edges are defined
112  if (!(config->is_list(std::string(c + "/" + state + "/edges").c_str()))) {
113  logger->log_warn(name(), "State %s of %s needs edges field", state.c_str(), c.c_str());
114  continue;
115  }
116 
117  std::vector<std::string> edges =
118  config->get_strings(std::string(c + "/" + state + "/edges").c_str());
119  for (const auto &edge : edges) {
120  std::string transition = "";
121 
122  // An edge can have one or mulitple transition conditions
123  if (config->exists(std::string(c + "/" + state + "/" + edge + "/transition"))) {
124  transition =
125  config->get_string(std::string(c + "/" + state + "/" + edge + "/transition").c_str());
126 
127  if (config->exists(std::string(c + "/" + state + "/" + edge + "/probability").c_str())) {
128  clips_add_edge(clips, c, state, edge, transition);
129  }
130 
131  } else if (config->exists(std::string(c + "/" + state + "/" + edge + "/transitions"))) {
132  std::vector<std::string> transitions =
133  config->get_strings(std::string(c + "/" + state + "/" + edge + "/transitions"));
134  for (std::string transition : transitions) {
135  if (config->exists(
136  std::string(c + "/" + state + "/" + edge + "/probability").c_str())) {
137  clips_add_edge(clips, c, state, edge, transition);
138  }
139  }
140  } else {
141  logger->log_warn(name(),
142  "Cant find transition/transitions value in %s",
143  std::string(c + "/" + state + "/" + edge).c_str());
144  continue;
145  }
146  }
147 
148  // Check if the state is a terminal state
149  if (config->exists(std::string(c + "/" + state + "/terminal").c_str())
150  && config->get_bool(c + "/" + state + "/terminal")) {
151  clips_add_terminal_state(clips, c, state);
152  }
153  }
154  clips_add_component(clips, c, states[0]);
155  }
156  hm_if_->set_busy(false);
157  hm_if_->write();
158 }
159 
160 void
162 {
163  envs_.erase(env_name);
164  logger->log_error(name(), "Removing environment %s", env_name.c_str());
165 }
166 
167 void
168 HardwareModelsThread::clips_add_terminal_state(LockPtr<CLIPS::Environment> &clips,
169  const std::string & component,
170  const std::string & state)
171 {
172  CLIPS::Template::pointer temp = clips->get_template("hm-terminal-state");
173  if (temp) {
174  CLIPS::Fact::pointer fact = CLIPS::Fact::create(**clips, temp);
175  fact->set_slot("name", CLIPS::Value(component.c_str(), CLIPS::TYPE_SYMBOL));
176  fact->set_slot("state", CLIPS::Value(state.c_str(), CLIPS::TYPE_SYMBOL));
177 
178  CLIPS::Fact::pointer new_fact = clips->assert_fact(fact);
179 
180  if (!new_fact) {
181  logger->log_warn(name(), "Asserting terminal state %s failed", component.c_str());
182  }
183 
184  } else {
185  logger->log_warn(name(),
186  "Did not get terminal state template, did you load hardware_models.clp?");
187  }
188 }
189 
190 /**
191  * @brief Adds a hardware component fact to the given clips environment
192  *
193  * @param clips Pointer to clips environment
194  * @param component Name of the hardware component
195  * @param init_state Name of the initial state of the component
196  */
197 void
198 HardwareModelsThread::clips_add_component(LockPtr<CLIPS::Environment> &clips,
199  const std::string & component,
200  const std::string & init_state)
201 {
202  CLIPS::Template::pointer temp = clips->get_template("hm-component");
203  if (temp) {
204  CLIPS::Fact::pointer fact = CLIPS::Fact::create(**clips, temp);
205  fact->set_slot("name", CLIPS::Value(component.c_str(), CLIPS::TYPE_SYMBOL));
206  fact->set_slot("initial-state", CLIPS::Value(init_state.c_str(), CLIPS::TYPE_SYMBOL));
207 
208  CLIPS::Fact::pointer new_fact = clips->assert_fact(fact);
209 
210  if (!new_fact) {
211  logger->log_warn(name(), "Asserting component %s failed", component.c_str());
212  }
213 
214  } else {
215  logger->log_warn(name(), "Did not get component template, did you load hardware_models.clp?");
216  }
217 }
218 
219 /**
220  * @brief Adds a hardware component edge fact to the given clips environment
221  *
222  * @param clips Pointer to the clips environment
223  * @param component Name of the component the edge belongs to
224  * @param from Name of the origin state
225  * @param to Name of the destination state
226  * @param trans Label/Transition action that triggers the edge
227  * @param prob Probability of the action if it is an exogenous action
228  */
229 void
230 HardwareModelsThread::clips_add_edge(LockPtr<CLIPS::Environment> &clips,
231  const std::string & component,
232  const std::string & from,
233  const std::string & to,
234  const std::string & trans)
235 {
236  double prob = 0.0;
237  prob = config->get_float(std::string(component + "/" + from + "/" + to + "/probability").c_str());
238 
239  CLIPS::Template::pointer temp = clips->get_template("hm-edge");
240  if (temp) {
241  CLIPS::Fact::pointer fact = CLIPS::Fact::create(**clips, temp);
242  fact->set_slot("component", CLIPS::Value(component.c_str(), CLIPS::TYPE_SYMBOL));
243  fact->set_slot("from", CLIPS::Value(from.c_str(), CLIPS::TYPE_SYMBOL));
244  fact->set_slot("to", CLIPS::Value(to.c_str(), CLIPS::TYPE_SYMBOL));
245  fact->set_slot("transition", CLIPS::Value(trans.c_str(), CLIPS::TYPE_SYMBOL));
246  fact->set_slot("probability", prob);
247 
248  CLIPS::Fact::pointer new_fact = clips->assert_fact(fact);
249 
250  if (!new_fact) {
251  logger->log_warn(name(), "Asserting edge from %s to %s failed", from.c_str(), to.c_str());
252  } else {
253  logger->log_debug(
254  name(), "Edge from %s to %s via %s", from.c_str(), to.c_str(), trans.c_str());
255  }
256 
257  } else {
258  logger->log_warn(name(), "Did not get edge template, did you load hardware_models.clp?");
259  }
260 }
261 
262 /**
263  * @brief Adds a transition fact to all registered clips environments. This represents that
264  * the given component changed its state by executing the transition.
265  *
266  * @param component Name of the component that executed the transition
267  * @param transition Name of the transition action
268  */
269 void
270 HardwareModelsThread::clips_add_transition(const std::string &component,
271  const std::string &transition) throw()
272 {
273  for (const auto e : envs_) {
274  fawkes::LockPtr<CLIPS::Environment> clips = e.second;
275  clips.lock();
276  CLIPS::Template::pointer temp = clips->get_template("hm-transition");
277  if (temp) {
278  CLIPS::Fact::pointer fact = CLIPS::Fact::create(**clips, temp);
279  fact->set_slot("component", component.c_str());
280  fact->set_slot("transition", transition.c_str());
281 
282  CLIPS::Fact::pointer new_fact = clips->assert_fact(fact);
283 
284  if (!new_fact) {
285  logger->log_warn(name(),
286  "Asserting transition of %s: %s failed",
287  component.c_str(),
288  transition.c_str());
289  }
290 
291  } else {
292  logger->log_warn(name(), "Did not get edge template, did you load hardware_models.clp?");
293  }
294  clips.unlock();
295  logger->log_error(name(), "Added transition in env: %s", e.first.c_str());
296  }
297  logger->log_error(name(), "Done");
298 }
299 
300 void
302 {
303 }
304 
305 /**
306  * @brief Loop function to be executed when a new HardwareInterfaceMessage is recieved
307  *
308  */
309 void
311 {
312  hm_if_->read();
313  while (!hm_if_->msgq_empty()) {
316 
317  std::string comp = std::string(msg->component());
318  std::string trans = std::string(msg->transition());
319 
320  logger->log_error(name(),
321  "Component: %s changed state by executing transition: %s",
322  comp.c_str(),
323  trans.c_str());
324 
325  clips_add_transition(comp, trans);
326  } else {
327  logger->log_error(name(), "Recieved unknown message type");
328  }
329  hm_if_->msgq_pop();
330  }
331 }
fawkes::MultiLogger::log_error
virtual void log_error(const char *component, const char *format,...)
Log error message.
Definition: multi.cpp:237
fawkes::Interface::msgq_first_is
bool msgq_first_is()
Check if first message has desired type.
Definition: interface.h:314
fawkes::Interface::msgq_pop
void msgq_pop()
Erase first message from queue.
Definition: interface.cpp:1182
HardwareModelsThread::HardwareModelsThread
HardwareModelsThread()
Constructor.
Definition: hardware_models_thread.cpp:57
fawkes::Interface::msgq_empty
bool msgq_empty()
Check if queue is empty.
Definition: interface.cpp:1029
fawkes::HardwareModelsInterface::StateChangeMessage::transition
char * transition() const
Get transition value.
Definition: HardwareModelsInterface.cpp:251
fawkes::BlackBoard::register_listener
virtual void register_listener(BlackBoardInterfaceListener *listener, ListenerRegisterFlag flag=BBIL_FLAG_ALL)
Register BB event listener.
Definition: blackboard.cpp:185
fawkes::LockPtr< CLIPS::Environment >
fawkes::Interface::read
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:472
fawkes::Thread::wakeup
void wakeup()
Wake up thread.
Definition: thread.cpp:995
fawkes::Configuration::get_bool
virtual bool get_bool(const char *path)=0
Get value from configuration which is of type bool.
fawkes::HardwareModelsInterface::StateChangeMessage::component
char * component() const
Get component value.
Definition: HardwareModelsInterface.cpp:221
fawkes::BlackBoardInterfaceListener
BlackBoard interface listener.
Definition: interface_listener.h:42
fawkes::Configuration::is_list
virtual bool is_list(const char *path)=0
Check if a value is a list.
fawkes::Thread::name
const char * name() const
Get name of thread.
Definition: thread.h:100
HardwareModelsThread::loop
virtual void loop()
Loop function to be executed when a new HardwareInterfaceMessage is recieved.
Definition: hardware_models_thread.cpp:310
fawkes::MultiLogger::log_warn
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:216
fawkes::LoggingAspect::logger
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
fawkes::LockPtr::unlock
void unlock() const
Unlock object mutex.
Definition: lockptr.h:273
fawkes::Logger::log_error
virtual void log_error(const char *component, const char *format,...)=0
Log error message.
fawkes
Fawkes library namespace.
fawkes::HardwareModelsInterface
HardwareModelsInterface Fawkes BlackBoard Interface.
Definition: HardwareModelsInterface.h:34
fawkes::Logger::log_warn
virtual void log_warn(const char *component, const char *format,...)=0
Log warning message.
fawkes::Configuration::get_strings
virtual std::vector< std::string > get_strings(const char *path)=0
Get list of values from configuration which is of type string.
fawkes::BlackBoardInterfaceListener::bbil_add_message_interface
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
Definition: interface_listener.cpp:241
fawkes::ConfigurableAspect::config
Configuration * config
This is the Configuration member used to access the configuration.
Definition: configurable.h:41
fawkes::CLIPSFeature
CLIPS feature maintainer.
Definition: clips_feature.h:42
HardwareModelsThread::finalize
virtual void finalize()
Finalize the thread.
Definition: hardware_models_thread.cpp:301
fawkes::Configuration::get_float
virtual float get_float(const char *path)=0
Get value from configuration which is of type float.
fawkes::Thread
Thread class encapsulation of pthreads.
Definition: thread.h:46
HardwareModelsThread::init
virtual void init()
Initialize the thread.
Definition: hardware_models_thread.cpp:67
fawkes::BlackBoardAspect::blackboard
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
HardwareModelsThread::clips_context_destroyed
virtual void clips_context_destroyed(const std::string &env_name)
Notification that a CLIPS environment has been destroyed.
Definition: hardware_models_thread.cpp:161
fawkes::LockPtr::lock
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:257
fawkes::Configuration::get_string
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
HardwareModelsThread::clips_context_init
virtual void clips_context_init(const std::string &env_name, fawkes::LockPtr< CLIPS::Environment > &clips)
Initializes hardware components from yaml files for a clips environment.
Definition: hardware_models_thread.cpp:87
fawkes::HardwareModelsInterface::set_busy
void set_busy(const bool new_busy)
Set busy value.
Definition: HardwareModelsInterface.cpp:121
fawkes::HardwareModelsInterface::StateChangeMessage
StateChangeMessage Fawkes BlackBoard Interface Message.
Definition: HardwareModelsInterface.h:55
fawkes::Interface::msgq_first
Message * msgq_first()
Get the first message from the message queue.
Definition: interface.cpp:1167
fawkes::Configuration::exists
virtual bool exists(const char *path)=0
Check if a given value exists.
fawkes::CLIPSFeatureAspect
Thread aspect to provide a feature to CLIPS environments.
Definition: clips_feature.h:58
fawkes::Logger::log_debug
virtual void log_debug(const char *component, const char *format,...)=0
Log debug message.
fawkes::Interface::write
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:494
fawkes::BlackBoard::open_for_writing
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.