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
34using 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"),
61 BlackBoardInterfaceListener("HardwareModelsThread")
62
63{
64}
65
66void
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 */
86void
87HardwareModelsThread::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 {
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
160void
162{
163 envs_.erase(env_name);
164 logger->log_error(name(), "Removing environment %s", env_name.c_str());
165}
166
167void
168HardwareModelsThread::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 {
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 */
197void
198HardwareModelsThread::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 */
229void
230HardwareModelsThread::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 {
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 */
269void
270HardwareModelsThread::clips_add_transition(const std::string &component,
271 const std::string &transition) noexcept
272{
273 for (const auto &e : envs_) {
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
300void
302{
303}
304
305/**
306 * @brief Loop function to be executed when a new HardwareInterfaceMessage is recieved
307 *
308 */
309void
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
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}
virtual void finalize()
Finalize the thread.
virtual void loop()
Loop function to be executed when a new HardwareInterfaceMessage is recieved.
virtual void clips_context_destroyed(const std::string &env_name)
Notification that a CLIPS environment has been destroyed.
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.
virtual void init()
Initialize the thread.
BlackBoard * blackboard
This is the BlackBoard instance you can use to interact with the BlackBoard.
Definition: blackboard.h:44
BlackBoard interface listener.
void bbil_add_message_interface(Interface *interface)
Add an interface to the message received watch list.
virtual Interface * open_for_writing(const char *interface_type, const char *identifier, const char *owner=NULL)=0
Open interface for writing.
virtual void register_listener(BlackBoardInterfaceListener *listener, ListenerRegisterFlag flag=BBIL_FLAG_ALL)
Register BB event listener.
Definition: blackboard.cpp:185
Thread aspect to provide a feature to CLIPS environments.
Definition: clips_feature.h:58
CLIPS feature maintainer.
Definition: clips_feature.h:42
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.
virtual bool is_list(const char *path)=0
Check if a value is a list.
virtual bool exists(const char *path)=0
Check if a given value exists.
virtual std::string get_string(const char *path)=0
Get value from configuration which is of type string.
StateChangeMessage Fawkes BlackBoard Interface Message.
HardwareModelsInterface Fawkes BlackBoard Interface.
void set_busy(const bool new_busy)
Set busy value.
bool msgq_first_is()
Check if first message has desired type.
Definition: interface.h:351
void msgq_pop()
Erase first message from queue.
Definition: interface.cpp:1215
Message * msgq_first()
Get the first message from the message queue.
Definition: interface.cpp:1200
void write()
Write from local copy into BlackBoard memory.
Definition: interface.cpp:501
bool msgq_empty()
Check if queue is empty.
Definition: interface.cpp:1062
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:479
void lock() const
Lock access to the encapsulated object.
Definition: lockptr.h:257
void unlock() const
Unlock object mutex.
Definition: lockptr.h:273
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.
Logger * logger
This is the Logger member used to access the logger.
Definition: logging.h:41
virtual void log_warn(const char *component, const char *format,...)
Log warning message.
Definition: multi.cpp:216
virtual void log_error(const char *component, const char *format,...)
Log error message.
Definition: multi.cpp:237
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
void wakeup()
Wake up thread.
Definition: thread.cpp:995
Fawkes library namespace.