Skill.cpp
Go to the documentation of this file.
1 #include "Skill.h"
2 
3 namespace armarx
4 {
5  namespace skills
6  {
7  Skill::Skill(const SkillDescription& desc) : description(desc), constructing(true)
8  {
9  // replace constructor if you want to have a specific logging tag
10  Logging::setTag("armarx::skills::" + description.skillId.toString());
11  }
12 
13  // install a local condition via a lambda
14  void
15  Skill::installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb)
16  {
17  std::scoped_lock l(conditionCallbacksMutex);
18  conditionCallbacks.push_back({f, cb});
19  }
20 
21  std::optional<TerminatedSkillStatusUpdate>
22  Skill::callSubskill(const SkillProxy& proxy)
23  {
24  return callSubskill(proxy, proxy.getRootProfileParameters());
25  }
26 
27  std::optional<TerminatedSkillStatusUpdate>
28  Skill::callSubskill(const SkillProxy& proxy, const aron::data::DictPtr& parameters)
29  {
30  auto executionId = callSubskillAsync(proxy, parameters);
31  auto ret = proxy.join(executionId);
32 
33  // While the sub skill was running, our skill might also have been aborted.
34  // In this case, the correct behavour would be aborting ourselves.
35  // The caller of callSubskill() can catch the thrown error::SkillAbortedException
36  // if necessary.
37  throwIfSkillShouldTerminate();
38  return ret;
39  }
40 
42  Skill::callSubskillAsync(const skills::SkillProxy& prx, const aron::data::DictPtr& params)
43  {
44  std::unique_lock l(subskillsMutex);
45 
46  std::string executorHistory = this->executorName + "->" + getSkillId().toString();
47  auto eid = prx.executeSkillAsync(executorHistory, params);
48  this->subskills.push_back(eid);
49  return eid;
50  }
51 
52  std::optional<TerminatedSkillStatusUpdate>
53  Skill::callSubskill(const SkillID& skillId)
54  {
55  return callSubskill(SkillProxy(manager, skillId));
56  }
57 
58  std::optional<TerminatedSkillStatusUpdate>
59  Skill::callSubskill(const SkillID& skillId, const aron::data::DictPtr& parameters)
60  {
61  return callSubskill(SkillProxy(manager, skillId), parameters);
62  }
63 
64  std::optional<TerminatedSkillStatusUpdate>
65  Skill::callSubskill(const SkillID& skillId,
66  std::function<void(aron::data::DictPtr&)> parametersFunction)
67  {
68  SkillProxy proxy(manager, skillId);
69 
70  aron::data::DictPtr parameters = proxy.getRootProfileParameters();
71  if (not parameters)
72  {
73  parameters = armarx::aron::make_dict();
74  }
75 
76  parametersFunction(parameters);
77 
78  return callSubskill(proxy, parameters);
79  }
80 
81  void
82  Skill::updateParameters(const aron::data::DictPtr& d)
83  {
84  std::scoped_lock l(this->parametersMutex);
85  if (this->parameters == nullptr)
86  {
87  // set params as there has been no update before.
88  this->parameters = d;
89  }
90  else
91  {
92  // merge params into existing. Note that this may update already set params.
93  this->parameters->mergeAndReplaceCopy(d);
94  }
95  }
96 
97  void
98  Skill::setParameters(const aron::data::DictPtr& d)
99  {
100  // we only set the params if the skill is not already running
101  if (running or exiting or finished)
102  {
103  return;
104  }
105 
106  std::scoped_lock l(this->parametersMutex);
107  this->parameters = d;
108  }
109 
111  Skill::getParameters() const
112  {
113  return this->parameters;
114  }
115 
117  Skill::_init()
118  {
119  //ARMARX_IMPORTANT << "Initializing skill '" << description.skillName << "'";
120  this->initializing = true;
121  this->constructing = false;
122  this->preparing = false;
123  this->running = false;
124  this->exiting = false;
125  this->finished = false;
126 
127  // install timeout condition
128  installConditionWithCallback(
129  [&]() {
130  return (armarx::core::time::DateTime::Now() >= (started + description.timeout));
131  },
132  [&]() { notifyTimeoutReached(); });
133 
134  conditionCheckingThread = std::thread(
135  [&]()
136  {
137  armarx::core::time::Metronome metronome(conditionCheckingThreadFrequency);
138  while (initializing or preparing or
139  running) // when the skill ends/aborts this variable will be set to false
140  {
141  {
142  std::scoped_lock l(conditionCallbacksMutex);
143  for (auto& p : conditionCallbacks)
144  {
145  auto& f = p.first;
146  auto& cb = p.second;
147  if (f())
148  {
149  cb();
150  }
151  }
152  }
153 
154  const auto sleepDuration = metronome.waitForNextTick();
155  if (not sleepDuration.isPositive())
156  {
158  << "ConditionCheckingThread: execution took too long ("
159  << -sleepDuration << " vs "
160  << conditionCheckingThreadFrequency.toCycleDuration()
161  << ")";
162  }
163  }
164  });
165  return {.status = TerminatedSkillStatus::Succeeded};
166  }
167 
168  Skill::PrepareResult
169  Skill::_prepare()
170  {
171  this->preparing = true;
172  this->initializing = false;
173  this->constructing = false;
174  this->running = false;
175  this->exiting = false;
176  this->finished = false;
177 
178  if (shouldSkillTerminate())
179  {
180  return {.status = ActiveOrTerminatedSkillStatus::Aborted};
181  }
182 
183  // Default nothing to prepare
184  if (not description.parametersType)
185  {
186  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
187  }
188  if (this->parameters && this->parameters->fullfillsType(description.parametersType))
189  {
190  // wait until parameters fulfill type
191  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
192  }
193 
194  // false if we have to wait for parameters
195  return {.status = ActiveOrTerminatedSkillStatus::Running};
196  }
197 
198  Skill::MainResult
199  Skill::_main()
200  {
201  this->running = true;
202  this->initializing = false;
203  this->constructing = false;
204  this->preparing = false;
205  this->exiting = false;
206  this->finished = false;
207  return {.status = TerminatedSkillStatus::Succeeded};
208  }
209 
210  Skill::ExitResult
211  Skill::_exit()
212  {
213  // ARMARX_IMPORTANT << "Exiting Skill '" << description.skillName << "'";
214  this->exiting = true;
215  this->running = false;
216  this->initializing = false;
217  this->constructing = false;
218  this->preparing = false;
219  this->finished = false;
220 
221  if (conditionCheckingThread.joinable())
222  {
223  conditionCheckingThread.join();
224  }
226 
227  this->finished = true;
228  this->exiting = false;
229  return {.status = TerminatedSkillStatus::Succeeded};
230  }
231 
232  Skill::InitResult
233  Skill::initSkill()
234  {
235  std::scoped_lock l(parametersMutex);
236  auto _res = this->_init();
237  auto res = this->init();
238  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
239  }
240 
242  Skill::prepareSkill()
243  {
244  std::scoped_lock l(parametersMutex);
245  auto _res = this->_prepare();
246  auto res = this->prepare();
247  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
248  }
249 
251  Skill::mainOfSkill()
252  {
253  std::scoped_lock l(parametersMutex);
254  auto _res = this->_main();
255  auto res = this->main();
256  return {.status = skills::mergeSkillStatuseses(_res.status, res.status),
257  .data = res.data};
258  }
259 
261  Skill::exitSkill()
262  {
263  std::scoped_lock l(parametersMutex);
264  auto res = this->exit();
265  auto _res = this->_exit();
266  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
267  }
268 
269  void
270  Skill::throwIfSkillShouldTerminate(const std::function<void()>& do_before,
271  const std::string& abortedMessage)
272  {
273  if (shouldSkillTerminate())
274  {
275  do_before();
276  throwIfSkillShouldTerminate(abortedMessage);
277  }
278  }
279 
280  void
281  Skill::throwIfSkillShouldTerminate(const std::string& abortedMessage)
282  {
283  if (stopped)
284  {
285  std::string message =
286  std::string("The skill '" + getSkillId().toString() + "' was asked to stop.");
287  message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
288 
289  throw error::SkillAbortedException(__PRETTY_FUNCTION__, message);
290  return;
291  }
292 
293  if (timeoutReached)
294  {
295  std::string message =
296  std::string("The skill '" + getSkillId().toString() + "' reached timeout.");
297  message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
298 
300  throw error::SkillFailedException(__PRETTY_FUNCTION__, message);
301  }
302  }
303 
305  Skill::MakeSucceededResult(aron::data::DictPtr data)
306  {
307  return MainResult{
308  .status = TerminatedSkillStatus::Succeeded,
309  .data = data,
310  };
311  }
312 
314  Skill::MakeFailedResult()
315  {
316  return MainResult{
317  .status = TerminatedSkillStatus::Failed,
318  .data = nullptr,
319  };
320  }
321 
323  Skill::MakeAbortedResult()
324  {
325  return MainResult{
326  .status = TerminatedSkillStatus::Aborted,
327  .data = nullptr,
328  };
329  }
330 
331  void
332  Skill::notifySkillToStop()
333  {
334  std::scoped_lock l(subskillsMutex);
335  stopped = true;
336  _onStopRequested();
337  onStopRequested();
338  }
339 
340  void
341  Skill::notifyTimeoutReached()
342  {
343  std::scoped_lock l(subskillsMutex);
344  timeoutReached = true;
345  _onTimeoutReached();
346  onTimeoutReached();
347  }
348 
349  bool
350  Skill::shouldSkillTerminate() const
351  {
352  return stopped || timeoutReached;
353  }
354 
355  // condition effects
356  void
357  Skill::_onTimeoutReached()
358  {
359  if (!manager)
360  {
361  return;
362  }
363  for (const auto& execId : subskills)
364  {
365  manager->abortSkillAsync(execId.toManagerIce());
366  }
367  }
368 
369  void
370  Skill::_onStopRequested()
371  {
372  if (!manager)
373  {
374  return;
375  }
376  for (const auto& execId : subskills)
377  {
378  manager->abortSkillAsync(execId.toManagerIce());
379  }
380  }
381 
382  void
383  Skill::onTimeoutReached()
384  {
385  }
386 
387  void
388  Skill::onStopRequested()
389  {
390  }
391 
392  // always called before prepare (should not take longer than 100ms)
394  Skill::init()
395  {
396  // Default nothing to init
397  return {.status = TerminatedSkillStatus::Succeeded};
398  }
399 
400  // always called before main (should not take longer than 100ms)
402  Skill::prepare()
403  {
404  // Default nothing to prepare
405  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
406  }
407 
408  // always called after main or if skill fails (should not take longer than 100ms)
410  Skill::exit()
411  {
412  // Default nothing to exit
413  return {.status = TerminatedSkillStatus::Succeeded};
414  }
415 
418  {
419  // This is just a dummy implementation
420  ARMARX_IMPORTANT << "Dummy executing skill '" << description.skillId
421  << "'. Please overwrite this method.";
422  return {.status = TerminatedSkillStatus::Succeeded, .data = nullptr};
423  }
424  } // namespace skills
425 } // namespace armarx
armarx::skills::SkillProxy::getRootProfileParameters
aron::data::DictPtr getRootProfileParameters() const
get the default parameters of the skill. TODO: Skill profiles in memory!
Definition: SkillProxy.cpp:123
armarx::skills::Skill::ExitResult::status
TerminatedSkillStatus status
Definition: Skill.h:57
armarx::skills::SkillExecutionID
Definition: SkillExecutionID.h:19
armarx::aron::ret
ReaderT::InputType T & ret
Definition: rw.h:21
skills
This file is part of ArmarX.
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:183
armarx::skills::Skill::PrepareResult
A result struct for skill preparing.
Definition: Skill.h:42
armarx::skills::error::SkillAbortedException
Definition: Exception.h:65
armarx::aron::make_dict
aron::data::DictPtr make_dict(_Args &&... args)
Definition: Dict.h:107
armarx::skills::SkillDescription
Definition: SkillDescription.h:18
armarx::skills::Skill::Skill
Skill()=delete
We completely remove the default constructor! A skill without a desciption cannot exist.
armarx::core::time::DateTime::Now
static DateTime Now()
Definition: DateTime.cpp:55
KITProsthesis::ProsthesisState::Running
@ Running
Definition: KITProstheticHandInterface.ice:43
armarx::skills::mergeSkillStatuseses
TerminatedSkillStatus mergeSkillStatuseses(const TerminatedSkillStatus t1, const TerminatedSkillStatus t2)
Definition: SkillStatusUpdate.cpp:40
message
message(STATUS "Boost-Library-Dir: " "${Boost_LIBRARY_DIRS}") message(STATUS "Boost-LIBRARIES
Definition: CMakeLists.txt:8
armarx::skills::error::SkillFailedException
Definition: Exception.h:76
armarx::skills::SkillProxy
Definition: SkillProxy.h:11
armarx::skills::Skill::PrepareResult::status
ActiveOrTerminatedSkillStatus status
Definition: Skill.h:44
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:72
Skill.h
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::skills::Skill::MainResult
A result struct for th main method of a skill.
Definition: Skill.h:48
armarx::skills::Skill::ExitResult
A result struct for skill exit function.
Definition: Skill.h:55
armarx::aron::data::DictPtr
std::shared_ptr< Dict > DictPtr
Definition: Dict.h:41
armarx::skills::Skill::MainResult::status
TerminatedSkillStatus status
Definition: Skill.h:50
armarx::skills::SkillProxy::executeSkillAsync
SkillExecutionID executeSkillAsync(const std::string &executorName, const aron::data::DictPtr &params=nullptr) const
execute a skill. Do not block during execution
Definition: SkillProxy.cpp:65
armarx::skills::Skill::InitResult::status
TerminatedSkillStatus status
Definition: Skill.h:38
armarx::core::time::Metronome
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition: Metronome.h:35
armarx::skills::Skill::InitResult
A result struct for skill initialization.
Definition: Skill.h:36
armarx::viz::toString
const char * toString(InteractionFeedbackType type)
Definition: Interaction.h:27
armarx::skills::SkillProxy::join
std::optional< TerminatedSkillStatusUpdate > join(const SkillExecutionID &executionId) const
poll execution status and block until its null or terminated
Definition: SkillProxy.cpp:79
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:186
armarx::skills::SkillID
Definition: SkillID.h:17
main
int main(int argc, char *argv[])
Definition: Admin.cpp:45
armarx
This file offers overloads of toIce() and fromIce() functions for STL container types.
Definition: ArmarXTimeserver.cpp:28