Fawkes API  Fawkes Development Version
skiller-rest-api.cpp
1 
2 /***************************************************************************
3  * skiller-rest-api.cpp - CLIPS Executive REST API
4  *
5  * Created: Sun Mar 25 01:29:29 2018
6  * Copyright 2006-2018 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 "skiller-rest-api.h"
23 
24 #include <core/threading/mutex_locker.h>
25 #include <interfaces/SkillerDebugInterface.h>
26 #include <interfaces/SkillerInterface.h>
27 #include <utils/time/wait.h>
28 #include <webview/rest_api_manager.h>
29 
30 using namespace fawkes;
31 
32 /** @class SkillerRestApi "skiller-rest-api.h"
33  * REST API backend for the CLIPS executive.
34  * @author Tim Niemueller
35  */
36 
37 /** Constructor. */
38 SkillerRestApi::SkillerRestApi() : Thread("ClipsWebviewThread", Thread::OPMODE_WAITFORWAKEUP)
39 {
40 }
41 
42 /** Destructor. */
44 {
45 }
46 
47 void
49 {
50  skdb_if_ = blackboard->open_for_reading<SkillerDebugInterface>("Skiller");
51  skiller_if_ = blackboard->open_for_reading<SkillerInterface>("Skiller");
52 
53  rest_api_ = new WebviewRestApi("skiller", logger);
54  rest_api_->add_handler<WebviewRestArray<SkillInfo>>(
55  WebRequest::METHOD_GET, "/skills", std::bind(&SkillerRestApi::cb_list_skills, this));
56  rest_api_->add_handler<Skill>(WebRequest::METHOD_GET,
57  "/skills/{id}",
58  std::bind(&SkillerRestApi::cb_get_skill,
59  this,
60  std::placeholders::_1));
61  rest_api_->add_handler(WebRequest::METHOD_DELETE,
62  "/skills/{id}",
63  std::bind(&SkillerRestApi::cb_stop_skill, this, std::placeholders::_1));
64  rest_api_->add_handler<Skill, SkillCall>(WebRequest::METHOD_POST,
65  "/call",
66  std::bind(&SkillerRestApi::cb_exec_skill,
67  this,
68  std::placeholders::_1));
70 }
71 
72 void
74 {
76  delete rest_api_;
77 }
78 
79 void
81 {
82 }
83 
84 void
85 SkillerRestApi::set_and_wait_graph(const char *graph)
86 {
87  if (strcmp(skdb_if_->graph_fsm(), graph) != 0) {
88  // It's not currently the desired graph
90  skdb_if_->msgq_enqueue(m);
92  fawkes::Time now(clock);
93  do {
95  skdb_if_->read();
96  now.stamp();
97  } while (strcmp(skdb_if_->graph_fsm(), graph) != 0 && (now - &start) <= 5.0);
98  }
99 
100  if (strcmp(skdb_if_->graph_fsm(), graph) != 0) {
101  throw WebviewRestException(WebReply::HTTP_REQUEST_TIMEOUT,
102  "Did not receive '%s' in time from skiller",
103  graph);
104  }
105 }
106 
108 SkillerRestApi::cb_list_skills()
109 {
111 
112  skdb_if_->read();
113  if (!skdb_if_->has_writer() || !skiller_if_->has_writer()) {
114  throw WebviewRestException(WebReply::HTTP_SERVICE_UNAVAILABLE,
115  "Behavior Engine plugin is not loaded");
116  }
117 
118  std::string prev_fsm = skdb_if_->graph_fsm();
119 
120  set_and_wait_graph("LIST");
121 
122  std::stringstream ss(skdb_if_->graph());
123  std::string skill_name;
124  while (std::getline(ss, skill_name, '\n')) {
125  SkillInfo s;
126  s.set_kind("SkillInfo");
128  s.set_name(skill_name);
129  rv.push_back(std::move(s));
130  }
131 
133  new SkillerDebugInterface::SetGraphMessage(prev_fsm.c_str());
134  skdb_if_->msgq_enqueue(m);
135 
136  return rv;
137 }
138 
139 Skill
140 SkillerRestApi::cb_get_skill(WebviewRestParams &params)
141 {
142  std::string skill_name{params.path_arg("id")};
143 
144  if (skill_name == "active") {
145  skill_name = "ACTIVE";
146  }
147 
148  skdb_if_->read();
149  skiller_if_->read();
150  if (!skdb_if_->has_writer() || !skiller_if_->has_writer()) {
151  throw WebviewRestException(WebReply::HTTP_SERVICE_UNAVAILABLE,
152  "Behavior Engine plugin is not loaded");
153  }
154 
155  set_and_wait_graph(skill_name.c_str());
156 
157  Skill s;
158  s.set_kind("Skill");
160  s.set_name(skdb_if_->graph_fsm());
161  s.set_graph(skdb_if_->graph());
162 
163  if (skill_name == "ACTIVE") {
164  skiller_if_->read();
165  s.set_skill_string(skiller_if_->skill_string());
166  s.set_error(skiller_if_->error());
167  s.set_msg_id(skiller_if_->msgid());
169  switch (skiller_if_->status()) {
170  case SkillerInterface::S_RUNNING: s.set_status("RUNNING"); break;
171  case SkillerInterface::S_FINAL: s.set_status("FINAL"); break;
172  case SkillerInterface::S_FAILED: s.set_status("FAILED"); break;
173  default: s.set_status("INACTIVE"); break;
174  }
175  }
176 
177  return s;
178 }
179 
180 Skill
181 SkillerRestApi::cb_exec_skill(const SkillCall &call)
182 {
183  if (!call.skill_string()) {
184  throw WebviewRestException(WebReply::HTTP_BAD_REQUEST, "Request lacks skill string");
185  }
186 
187  skiller_if_->read();
188  if (!skiller_if_->has_writer()) {
189  throw WebviewRestException(WebReply::HTTP_SERVICE_UNAVAILABLE,
190  "Behavior Engine plugin is not loaded");
191  }
192 
193  if (skiller_if_->exclusive_controller() != 0) {
194  throw WebviewRestException(WebReply::HTTP_CONFLICT, "Another thread is exclusive controller");
195  }
196 
199  m->ref();
200 
201  try {
202  skiller_if_->msgq_enqueue(m);
203  } catch (Exception &e) {
204  logger->log_error(name(), "Failed to execute skill: %s", e.what_no_backtrace());
205  throw WebviewRestException(WebReply::HTTP_INTERNAL_SERVER_ERROR,
206  "Failed to execute skill: %s",
207  e.what_no_backtrace());
208  }
209 
210  Skill sk;
211  sk.set_kind("Skill");
213  sk.set_name("active");
214  sk.set_msg_id(m->id());
215  m->unref();
216  return sk;
217 }
218 
219 std::unique_ptr<fawkes::WebviewRestReply>
220 SkillerRestApi::cb_stop_skill(WebviewRestParams &params)
221 {
222  std::string skill_name{params.path_arg("id")};
223 
224  if (skill_name != "active") {
225  throw WebviewRestException(WebReply::HTTP_BAD_REQUEST,
226  "Only the 'active' skill can be stopped");
227  }
228 
229  skiller_if_->read();
230  if (!skiller_if_->has_writer()) {
231  throw WebviewRestException(WebReply::HTTP_SERVICE_UNAVAILABLE,
232  "Behavior Engine plugin is not loaded");
233  }
234 
235  if (skiller_if_->exclusive_controller() != 0) {
236  throw WebviewRestException(WebReply::HTTP_CONFLICT, "Another thread is exclusive controller");
237  }
238 
239  try {
241  } catch (Exception &e) {
242  logger->log_error(name(), "Failed to stop skill: %s", e.what_no_backtrace());
243  throw WebviewRestException(WebReply::HTTP_INTERNAL_SERVER_ERROR,
244  "Failed to execute skill: %s",
245  e.what_no_backtrace());
246  }
247 
248  return std::make_unique<WebviewRestReply>(WebReply::HTTP_OK, "OK", "text/plain");
249 }
SkillCall representation for JSON transfer.
Definition: SkillCall.h:29
std::optional< std::string > skill_string() const
Get skill_string value.
Definition: SkillCall.h:127
SkillInfo representation for JSON transfer.
Definition: SkillInfo.h:29
void set_name(const std::string &name)
Set name value.
Definition: SkillInfo.h:136
static std::string api_version()
Get version of implemented API.
Definition: SkillInfo.h:49
void set_apiVersion(const std::string &apiVersion)
Set apiVersion value.
Definition: SkillInfo.h:119
void set_kind(const std::string &kind)
Set kind value.
Definition: SkillInfo.h:102
Skill representation for JSON transfer.
Definition: Skill.h:29
void set_exclusive_controller(const int64_t &exclusive_controller)
Set exclusive_controller value.
Definition: Skill.h:223
void set_graph(const std::string &graph)
Set graph value.
Definition: Skill.h:153
void set_error(const std::string &error)
Set error value.
Definition: Skill.h:189
void set_status(const std::string &status)
Set status value.
Definition: Skill.h:240
void set_kind(const std::string &kind)
Set kind value.
Definition: Skill.h:102
void set_skill_string(const std::string &skill_string)
Set skill-string value.
Definition: Skill.h:171
void set_apiVersion(const std::string &apiVersion)
Set apiVersion value.
Definition: Skill.h:119
static std::string api_version()
Get version of implemented API.
Definition: Skill.h:49
void set_msg_id(const int64_t &msg_id)
Set msg_id value.
Definition: Skill.h:206
void set_name(const std::string &name)
Set name value.
Definition: Skill.h:136
virtual void init()
Initialize the thread.
virtual void finalize()
Finalize the thread.
~SkillerRestApi()
Destructor.
SkillerRestApi()
Constructor.
virtual void loop()
Code to execute in the thread.
Container to return array via REST.
Definition: rest_array.h:36
void push_back(M &m)
Add item at the back of the container.
Definition: rest_array.h:123
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.
Clock * clock
By means of this member access to the clock is given.
Definition: clock.h:42
Base class for exceptions in Fawkes.
Definition: exception.h:36
virtual const char * what_no_backtrace() const
Get primary string (does not implicitly print the back trace).
Definition: exception.cpp:663
void read()
Read from BlackBoard into local copy.
Definition: interface.cpp:472
bool has_writer() const
Check if there is a writer for the interface.
Definition: interface.cpp:817
unsigned int msgq_enqueue(Message *message)
Enqueue message at end of queue.
Definition: interface.cpp:882
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
unsigned int id() const
Get message ID.
Definition: message.cpp:180
void unref()
Decrement reference count and conditionally delete this instance.
Definition: refcount.cpp:95
void ref()
Increment reference count.
Definition: refcount.cpp:67
SetGraphMessage Fawkes BlackBoard Interface Message.
SkillerDebugInterface Fawkes BlackBoard Interface.
char * graph_fsm() const
Get graph_fsm value.
char * graph() const
Get graph value.
ExecSkillMessage Fawkes BlackBoard Interface Message.
StopExecMessage Fawkes BlackBoard Interface Message.
SkillerInterface Fawkes BlackBoard Interface.
char * error() const
Get error value.
uint32_t exclusive_controller() const
Get exclusive_controller value.
SkillStatusEnum status() const
Get status value.
uint32_t msgid() const
Get msgid value.
char * skill_string() const
Get skill_string value.
Thread class encapsulation of pthreads.
Definition: thread.h:46
const char * name() const
Get name of thread.
Definition: thread.h:100
void start(bool wait=true)
Call this method to start the thread.
Definition: thread.cpp:499
void wait_systime()
Wait until minimum loop time has been reached in real time.
Definition: wait.cpp:96
A class for handling time.
Definition: time.h:93
WebviewRestApiManager * webview_rest_api_manager
Webview REST API manager.
Definition: webview.h:55
void unregister_api(WebviewRestApi *api)
Remove a request processor.
void register_api(WebviewRestApi *api)
Add a REST API.
Webview REST API component.
Definition: rest_api.h:221
REST processing exception.
Definition: rest_api.h:71
REST parameters to pass to handlers.
Definition: rest_api.h:125
std::string path_arg(const std::string &what)
Get a path argument.
Definition: rest_api.h:142
Fawkes library namespace.