Skill.cpp
Go to the documentation of this file.
1 #include "Skill.h"
2 
4 
6 
7 namespace armarx
8 {
9  namespace skills
10  {
11  Skill::Skill(const SkillDescription& desc) : description(desc), constructing(true)
12  {
13  // replace constructor if you want to have a specific logging tag
14  Logging::setTag("armarx::skills::" + description.skillId.toString());
15 
17  if (!desc.rootProfileDefaults->fullfillsType(desc.parametersType))
18  {
19  ARMARX_WARNING << "Missing root profile defaults for skill " << desc.skillId;
20  }
21  }
22 
23  // install a local condition via a lambda
24  void
25  Skill::installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb)
26  {
27  std::scoped_lock l(conditionCallbacksMutex);
28  conditionCallbacks.push_back({f, cb});
29  }
30 
31  std::optional<TerminatedSkillStatusUpdate>
32  Skill::callSubskill(const SkillProxy& proxy)
33  {
34  return callSubskill(proxy, proxy.getRootProfileParameters());
35  }
36 
37  std::optional<TerminatedSkillStatusUpdate>
38  Skill::callSubskill(const SkillProxy& proxy, const aron::data::DictPtr& parameters)
39  {
40  auto executionId = callSubskillAsync(proxy, parameters);
41  auto ret = proxy.join(executionId);
42 
43  // While the sub skill was running, our skill might also have been aborted.
44  // In this case, the correct behavour would be aborting ourselves.
45  // The caller of callSubskill() can catch the thrown error::SkillAbortedException
46  // if necessary.
47  throwIfSkillShouldTerminate();
48  return ret;
49  }
50 
52  Skill::callSubskillAsync(const skills::SkillProxy& prx)
53  {
54  return callSubskillAsync(prx, prx.getRootProfileParameters());
55  }
56 
58  Skill::callSubskillAsync(const skills::SkillProxy& prx, const aron::data::DictPtr& params)
59  {
60  std::string executorHistory = this->executorName + "->" + getSkillId().toString();
61 
62  throwIfSkillShouldTerminate();
63  auto eid = prx.executeSkillAsync(executorHistory, params);
64 
65  std::unique_lock l(subskillsMutex);
66  throwIfSkillShouldTerminate(
67  [&]()
68  {
69  prx.abortSkillAsync(eid);
70  }); // also notify newly added skill as it was not added to subskills list yet
71  this->subskills.push_back(eid);
72  return eid;
73  }
74 
75  std::optional<TerminatedSkillStatusUpdate>
76  Skill::callSubskill(const SkillID& skillId)
77  {
78  return callSubskill(SkillProxy(manager, skillId));
79  }
80 
81  std::optional<TerminatedSkillStatusUpdate>
82  Skill::callSubskill(const SkillID& skillId, const aron::data::DictPtr& parameters)
83  {
84  return callSubskill(SkillProxy(manager, skillId), parameters);
85  }
86 
87  std::optional<TerminatedSkillStatusUpdate>
88  Skill::callSubskill(const SkillID& skillId,
89  std::function<void(aron::data::DictPtr&)> parametersFunction)
90  {
91  SkillProxy proxy(manager, skillId);
92 
93  aron::data::DictPtr parameters = proxy.getRootProfileParameters();
94  if (not parameters)
95  {
96  parameters = armarx::aron::make_dict();
97  }
98 
99  parametersFunction(parameters);
100 
101  return callSubskill(proxy, parameters);
102  }
103 
104  void
105  Skill::updateParameters(const aron::data::DictPtr& d)
106  {
107  std::scoped_lock l(this->parametersMutex);
108  if (this->parameters == nullptr)
109  {
110  // set params as there has been no update before.
111  this->parameters = d;
112  }
113  else
114  {
115  // merge params into existing. Note that this may update already set params.
116  this->parameters->mergeAndReplaceCopy(d);
117  }
118  }
119 
120  void
121  Skill::setParameters(const aron::data::DictPtr& d)
122  {
123  // we only set the params if the skill is not already running
124  if (running or exiting or finished)
125  {
126  return;
127  }
128 
129  std::scoped_lock l(this->parametersMutex);
130  this->parameters = d;
131  }
132 
134  Skill::getParameters() const
135  {
136  return this->parameters;
137  }
138 
140  Skill::_init()
141  {
142  //ARMARX_IMPORTANT << "Initializing skill '" << description.skillName << "'";
143  this->initializing = true;
144  this->constructing = false;
145  this->preparing = false;
146  this->running = false;
147  this->exiting = false;
148  this->finished = false;
149 
150  // install timeout condition
151  installConditionWithCallback(
152  [&]()
153  {
154  return (armarx::core::time::DateTime::Now() >= (started + description.timeout));
155  },
156  [&]() { notifyTimeoutReached(); });
157 
158  conditionCheckingThread = std::thread(
159  [&]()
160  {
161  armarx::core::time::Metronome metronome(conditionCheckingThreadFrequency);
162  while (initializing or preparing or
163  running) // when the skill ends/aborts this variable will be set to false
164  {
165  {
166  std::scoped_lock l(conditionCallbacksMutex);
167  for (auto& p : conditionCallbacks)
168  {
169  auto& f = p.first;
170  auto& cb = p.second;
171  if (f())
172  {
173  cb();
174  }
175  }
176  }
177 
178  const auto sleepDuration = metronome.waitForNextTick();
179  if (not sleepDuration.isPositive())
180  {
182  << "ConditionCheckingThread: execution took too long ("
183  << -sleepDuration << " vs "
184  << conditionCheckingThreadFrequency.toCycleDuration()
185  << ")";
186  }
187  }
188  });
189  return {.status = TerminatedSkillStatus::Succeeded};
190  }
191 
192  Skill::PrepareResult
193  Skill::_prepare()
194  {
195  this->preparing = true;
196  this->initializing = false;
197  this->constructing = false;
198  this->running = false;
199  this->exiting = false;
200  this->finished = false;
201 
202  if (shouldSkillTerminate())
203  {
204  return {.status = ActiveOrTerminatedSkillStatus::Aborted};
205  }
206 
207  // Default nothing to prepare
208  if (not description.parametersType)
209  {
210  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
211  }
212  if (this->parameters && this->parameters->fullfillsType(description.parametersType))
213  {
214  // wait until parameters fulfill type
215  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
216  }
217 
218  // false if we have to wait for parameters
219  return {.status = ActiveOrTerminatedSkillStatus::Running};
220  }
221 
222  Skill::MainResult
223  Skill::_main()
224  {
225  this->running = true;
226  this->initializing = false;
227  this->constructing = false;
228  this->preparing = false;
229  this->exiting = false;
230  this->finished = false;
231  return {.status = TerminatedSkillStatus::Succeeded};
232  }
233 
234  Skill::ExitResult
235  Skill::_exit()
236  {
237  // ARMARX_IMPORTANT << "Exiting Skill '" << description.skillName << "'";
238  this->exiting = true;
239  this->running = false;
240  this->initializing = false;
241  this->constructing = false;
242  this->preparing = false;
243  this->finished = false;
244 
245  if (conditionCheckingThread.joinable())
246  {
247  conditionCheckingThread.join();
248  }
250 
251  this->finished = true;
252  this->exiting = false;
253  return {.status = TerminatedSkillStatus::Succeeded};
254  }
255 
256  Skill::InitResult
257  Skill::initSkill()
258  {
259  std::scoped_lock l(parametersMutex);
260  auto _res = this->_init();
261  auto res = this->init();
262  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
263  }
264 
266  Skill::prepareSkill()
267  {
268  std::scoped_lock l(parametersMutex);
269  auto _res = this->_prepare();
270  auto res = this->prepare();
271  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
272  }
273 
275  Skill::mainOfSkill()
276  {
277  std::scoped_lock l(parametersMutex);
278  auto _res = this->_main();
279  auto res = this->main();
280  return {.status = skills::mergeSkillStatuseses(_res.status, res.status),
281  .data = res.data};
282  }
283 
285  Skill::exitSkill()
286  {
287  std::scoped_lock l(parametersMutex);
288  auto res = this->exit();
289  auto _res = this->_exit();
290  return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
291  }
292 
293  void
294  Skill::throwIfSkillShouldTerminate(const std::function<void()>& do_before,
295  const std::string& abortedMessage)
296  {
297  if (shouldSkillTerminate())
298  {
299  do_before();
300  throwIfSkillShouldTerminate(abortedMessage);
301  }
302  }
303 
304  void
305  Skill::throwIfSkillShouldTerminate(const std::string& abortedMessage)
306  {
307  if (stopped)
308  {
309  std::string message =
310  std::string("The skill '" + getSkillId().toString() + "' was asked to stop.");
311  message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
312 
313  throw error::SkillAbortedException(__PRETTY_FUNCTION__, message);
314  return;
315  }
316 
317  if (timeoutReached)
318  {
319  std::string message =
320  std::string("The skill '" + getSkillId().toString() + "' reached timeout.");
321  message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
322 
324  throw error::SkillFailedException(__PRETTY_FUNCTION__, message);
325  }
326  }
327 
329  Skill::MakeSucceededResult(aron::data::DictPtr data)
330  {
331  return MainResult{
332  .status = TerminatedSkillStatus::Succeeded,
333  .data = data,
334  };
335  }
336 
338  Skill::MakeFailedResult()
339  {
340  return MainResult{
341  .status = TerminatedSkillStatus::Failed,
342  .data = nullptr,
343  };
344  }
345 
347  Skill::MakeAbortedResult()
348  {
349  return MainResult{
350  .status = TerminatedSkillStatus::Aborted,
351  .data = nullptr,
352  };
353  }
354 
355  void
356  Skill::notifySkillToStop()
357  {
358  if (stopped)
359  {
360  // skill already got stopped. Ignore
361  return;
362  }
363  std::scoped_lock l(subskillsMutex);
364  stopped = true;
365  _onStopRequested();
366  onStopRequested();
367  }
368 
369  void
370  Skill::notifyTimeoutReached()
371  {
372  if (stopped || timeoutReached)
373  {
374  // skill already got timeoutReached. Ignore
375  return;
376  }
377 
378  std::scoped_lock l(subskillsMutex);
379  timeoutReached = true;
380  _onTimeoutReached();
381  onTimeoutReached();
382  }
383 
384  bool
385  Skill::shouldSkillTerminate() const
386  {
387  return stopped || timeoutReached;
388  }
389 
390  // condition effects
391  void
392  Skill::_onTimeoutReached()
393  {
394  // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
395 
396  if (!manager)
397  {
398  return;
399  }
400 
401  for (const auto& execId : subskills)
402  {
403  manager->abortSkillAsync(execId.toManagerIce());
404  }
405  }
406 
407  void
408  Skill::_onStopRequested()
409  {
410  // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
411 
412  if (!manager)
413  {
414  return;
415  }
416 
417  for (const auto& execId : subskills)
418  {
419  manager->abortSkillAsync(execId.toManagerIce());
420  }
421  }
422 
423  void
424  Skill::onTimeoutReached()
425  {
426  }
427 
428  void
429  Skill::onStopRequested()
430  {
431  }
432 
433  // always called before prepare (should not take longer than 100ms)
435  Skill::init()
436  {
437  // Default nothing to init
438  return {.status = TerminatedSkillStatus::Succeeded};
439  }
440 
441  // always called before main (should not take longer than 100ms)
443  Skill::prepare()
444  {
445  // Default nothing to prepare
446  return {.status = ActiveOrTerminatedSkillStatus::Succeeded};
447  }
448 
449  // always called after main or if skill fails (should not take longer than 100ms)
451  Skill::exit()
452  {
453  // Default nothing to exit
454  return {.status = TerminatedSkillStatus::Succeeded};
455  }
456 
459  {
460  // This is just a dummy implementation
461  ARMARX_IMPORTANT << "Dummy executing skill '" << description.skillId
462  << "'. Please overwrite this method.";
463  return {.status = TerminatedSkillStatus::Succeeded, .data = nullptr};
464  }
465 
466  void
467  Skill::setProviderId(const skills::ProviderID& pid)
468  {
469  description.skillId.providerId = pid;
470  }
471 
472  void
473  Skill::setCallback(const CallbackT& callback)
474  {
475  this->callback = callback;
476  }
477 
478  void
479  Skill::setManager(const manager::dti::SkillManagerInterfacePrx& manager)
480  {
481  this->manager = manager;
482  }
483 
484  void
485  Skill::setExecutorName(const std::string& executorName)
486  {
487  this->executorName = executorName;
488  }
489 
491  Skill::getSkillDescription() const
492  {
493  return description;
494  }
495 
496  SkillID
497  Skill::getSkillId() const
498  {
499  return description.skillId;
500  }
501 
502  Skill::~Skill()
503  {
504  //ARMARX_IMPORTANT << "DESTROY SKILL " << getSkillId();
505  }
506  } // namespace skills
507 } // 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:125
armarx::skills::Skill::ExitResult::status
TerminatedSkillStatus status
Definition: Skill.h:48
armarx::skills::SkillExecutionID
Definition: SkillExecutionID.h:15
armarx::aron::ret
ReaderT::InputType T & ret
Definition: rw.h:13
skills
This file is part of ArmarX.
Exception.h
ARMARX_IMPORTANT
#define ARMARX_IMPORTANT
Definition: Logging.h:190
armarx::skills::Skill::PrepareResult
A result struct for skill preparing.
Definition: Skill.h:33
armarx::skills::error::SkillAbortedException
Definition: Exception.h:53
armarx::aron::make_dict
aron::data::DictPtr make_dict(_Args &&... args)
Definition: Dict.h:107
armarx::skills::SkillDescription
Definition: SkillDescription.h:17
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:51
ARMARX_CHECK_NOT_NULL
#define ARMARX_CHECK_NOT_NULL(ptr)
This macro evaluates whether ptr is not null and if it turns out to be false it will throw an Express...
Definition: ExpressionException.h:206
KITProsthesis::ProsthesisState::Running
@ Running
Definition: KITProstheticHandInterface.ice:43
armarx::skills::mergeSkillStatuseses
TerminatedSkillStatus mergeSkillStatuseses(const TerminatedSkillStatus t1, const TerminatedSkillStatus t2)
Definition: SkillStatusUpdate.cpp:42
armarx::control::common::MPStatus::finished
@ finished
message
message(STATUS "Boost-Library-Dir: " "${Boost_LIBRARY_DIRS}") message(STATUS "Boost-LIBRARIES
Definition: CMakeLists.txt:8
armarx::skills::Skill::CallbackT
std::function< void(const SkillStatus s, const armarx::aron::data::DictPtr &)> CallbackT
Definition: Skill.h:24
armarx::skills::error::SkillFailedException
Definition: Exception.h:61
armarx::skills::SkillProxy
Definition: SkillProxy.h:13
armarx::skills::Skill::PrepareResult::status
ActiveOrTerminatedSkillStatus status
Definition: Skill.h:35
armarx::skills::SkillExecutionID::toString
std::string toString() const
Definition: SkillExecutionID.cpp:50
deactivateSpam
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition: Logging.cpp:75
armarx::skills::ProviderID
Definition: ProviderID.h:12
Skill.h
data
uint8_t data[1]
Definition: EtherCATFrame.h:68
armarx::skills::SkillDescription::skillId
SkillID skillId
Definition: SkillDescription.h:19
Metronome.h
armarx::skills::Skill::MainResult
A result struct for th main method of a skill.
Definition: Skill.h:39
armarx::skills::SkillDescription::rootProfileDefaults
aron::data::DictPtr rootProfileDefaults
Definition: SkillDescription.h:21
armarx::skills::Skill::ExitResult
A result struct for skill exit function.
Definition: Skill.h:46
armarx::aron::data::DictPtr
std::shared_ptr< Dict > DictPtr
Definition: Dict.h:41
armarx::skills::Skill::MainResult::status
TerminatedSkillStatus status
Definition: Skill.h:41
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:67
armarx::skills::Skill::InitResult::status
TerminatedSkillStatus status
Definition: Skill.h:29
armarx::core::time::Metronome
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition: Metronome.h:34
armarx::skills::Skill::InitResult
A result struct for skill initialization.
Definition: Skill.h:27
armarx::viz::toString
const char * toString(InteractionFeedbackType type)
Definition: Interaction.h:28
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:81
ARMARX_WARNING
#define ARMARX_WARNING
Definition: Logging.h:193
armarx::skills::SkillDescription::parametersType
aron::type::ObjectPtr parametersType
Definition: SkillDescription.h:23
armarx::skills::SkillProxy::abortSkillAsync
bool abortSkillAsync(const SkillExecutionID &executionId) const
ask skill to abort ASAP
Definition: SkillProxy.cpp:117
armarx::skills::SkillID
Definition: SkillID.h:14
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:27