Skill.cpp
Go to the documentation of this file.
1#include "Skill.h"
2
3#include <functional>
4#include <memory>
5#include <mutex>
6#include <optional>
7#include <string>
8#include <thread>
9#include <utility>
10
17
18#include <RobotAPI/interface/skills/SkillManagerInterface.h>
28
29namespace armarx
30{
31 namespace skills
32 {
34 {
35 // replace constructor if you want to have a specific logging tag
36 Logging::setTag("armarx::skills::" + description.skillId.toString());
37
39 if (!desc.rootProfileDefaults->fullfillsType(desc.parametersType))
40 {
41 ARMARX_WARNING << "Missing root profile defaults for skill " << desc.skillId;
42 }
43 }
44
45 // install a local condition via a lambda
46 void
47 Skill::installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb)
48 {
49 std::scoped_lock l(conditionCallbacksMutex);
50 conditionCallbacks.push_back({f, cb});
51 }
52
53 void
55 {
56 double n = duration / interval;
57 auto metronome = armarx::Metronome(interval);
58 while (n > 1 and not shouldSkillTerminate())
59 {
60 n--;
61 metronome.waitForNextTick();
62 }
63 if (n > 0.05 and not shouldSkillTerminate())
64 {
65 // Wait for the remaining time
67 }
68 }
69
70 std::optional<TerminatedSkillStatusUpdate>
71 Skill::callSubskill(SkillProxyPtr proxy)
72 {
73 auto parameters = proxy->getRootProfileParameters();
74 return callSubskill(std::move(proxy), parameters);
75 }
76
77 std::optional<TerminatedSkillStatusUpdate>
79 {
80 auto handle = callSubskillAsync(std::move(proxy), parameters);
82 auto ret = handle->join();
83
84 // While the sub skill was running, our skill might also have been aborted.
85 // In this case, the correct behavour would be aborting ourselves.
86 // The caller of callSubskill() can catch the thrown error::SkillAbortedException
87 // if necessary.
89 return ret;
90 }
91
93 Skill::callSubskillAsync(Skill::SkillProxyPtr prx)
94 {
95 aron::data::DictPtr params = prx->getRootProfileParameters();
96 return callSubskillAsync(std::move(prx), params);
97 }
98
100 Skill::callSubskillAsync(Skill::SkillProxyPtr prx, const aron::data::DictPtr& params)
101 {
102 std::string executorHistory = this->executorName + "->" + getSkillId().toString();
103
105 auto eid = prx->executeSkillAsync(executorHistory, params);
106
107 std::unique_lock l(subskillsMutex);
109 [&]()
110 {
111 prx->abortSkillAsync(eid);
112 }); // also notify newly added skill as it was not added to subskills list yet
113 this->subskills.push_back(eid);
114
115 return std::make_unique<SkillExecutionHandle>(std::move(prx), eid);
116 }
117
118 std::optional<TerminatedSkillStatusUpdate>
120 {
121 return callSubskill(std::make_unique<SkillProxy>(manager, skillId));
122 }
123
124 std::optional<TerminatedSkillStatusUpdate>
126 {
127 return callSubskill(std::make_unique<SkillProxy>(manager, skillId), parameters);
128 }
129
130 std::optional<TerminatedSkillStatusUpdate>
131 Skill::callSubskill(const SkillID& skillId,
132 std::function<void(aron::data::DictPtr&)> parametersFunction)
133 {
134 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
135
136 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
137 if (not parameters)
138 {
140 }
141
142 parametersFunction(parameters);
143
144 return callSubskill(std::move(proxy), parameters);
145 }
146
149 std::function<void(aron::data::DictPtr&)> parametersFunction)
150 {
151 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
152
153 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
154 if (not parameters)
155 {
157 }
158
159 parametersFunction(parameters);
160
161 return callSubskillAsync(std::move(proxy), parameters);
162 }
163
166 {
167 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
168
169 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
170 if (not parameters)
171 {
173 }
174
175 return callSubskillAsync(std::move(proxy), parameters);
176 }
177
178 std::optional<TerminatedSkillStatusUpdate>
179 Skill::executeSkill(
180 const ::armarx::skills::SkillID& skillID,
181 std::function<aron::data::DictPtr(aron::data::DictPtr&)> const& parametersFunction)
182 {
183 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillID);
184
185 aron::data::DictPtr parameter_defaults = proxy->getRootProfileParameters();
186 return callSubskill(std::move(proxy), parametersFunction(parameter_defaults));
187 }
188
189 void
191 {
192 std::scoped_lock l(this->parametersMutex);
193 if (this->parameters == nullptr)
194 {
195 // set params as there has been no update before.
196 this->parameters = d;
197 }
198 else
199 {
200 // merge params into existing. Note that this may update already set params.
201 this->parameters->mergeAndReplaceCopy(d);
202 }
203 }
204
205 void
207 {
208 // we only set the params if the skill is not already running
209 if (running or exiting or finished)
210 {
211 return;
212 }
213
214 std::scoped_lock l(this->parametersMutex);
215 this->parameters = d;
216 }
217
220 {
221 return this->parameters;
222 }
223
225 Skill::_init()
226 {
227 // ARMARX_IMPORTANT << "Initializing skill '" << description.skillName << "'";
228 this->initializing = true;
229 this->constructing = false;
230 this->preparing = false;
231 this->running = false;
232 this->exiting = false;
233 this->finished = false;
234
235 // install timeout condition
237 [&]() {
239 },
240 [&]() { notifyTimeoutReached(); });
241
242 conditionCheckingThread = std::thread(
243 [&]()
244 {
245 armarx::core::time::Metronome metronome(conditionCheckingThreadFrequency);
246 while (initializing or preparing or
247 running) // when the skill ends/aborts this variable will be set to false
248 {
249 {
250 std::scoped_lock l(conditionCallbacksMutex);
251 for (auto& p : conditionCallbacks)
252 {
253 auto& f = p.first;
254 auto& cb = p.second;
255 if (f())
256 {
257 cb();
258 }
259 }
260 }
261
262 const auto sleepDuration = metronome.waitForNextTick();
263 if (not sleepDuration.isPositive())
264 {
266 << "ConditionCheckingThread: execution took too long ("
267 << -sleepDuration << " vs "
268 << conditionCheckingThreadFrequency.toCycleDuration()
269 << ")";
270 }
271 }
272 });
273 return {.status = TerminatedSkillStatus::Succeeded};
274 }
275
277 Skill::_prepare()
278 {
279 this->preparing = true;
280 this->initializing = false;
281 this->constructing = false;
282 this->running = false;
283 this->exiting = false;
284 this->finished = false;
285
287 {
289 }
290
291 // Default nothing to prepare
292 if (not description.parametersType)
293 {
295 }
296 if (this->parameters && this->parameters->fullfillsType(description.parametersType))
297 {
298 // wait until parameters fulfill type
300 }
301
302 // false if we have to wait for parameters
304 }
305
307 Skill::_main()
308 {
309 this->running = true;
310 this->initializing = false;
311 this->constructing = false;
312 this->preparing = false;
313 this->exiting = false;
314 this->finished = false;
315 return {.status = TerminatedSkillStatus::Succeeded};
316 }
317
319 Skill::_exit()
320 {
321 // ARMARX_IMPORTANT << "Exiting Skill '" << description.skillName << "'";
322 this->exiting = true;
323 this->running = false;
324 this->initializing = false;
325 this->constructing = false;
326 this->preparing = false;
327 this->finished = false;
328
329 if (conditionCheckingThread.joinable())
330 {
331 conditionCheckingThread.join();
332 }
334
335 this->finished = true;
336 this->exiting = false;
337 return {.status = TerminatedSkillStatus::Succeeded};
338 }
339
342 {
343 std::scoped_lock l(parametersMutex);
344 auto _res = this->_init();
345 auto res = this->init();
346 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
347 }
348
351 {
352 std::scoped_lock l(parametersMutex);
353 auto _res = this->_prepare();
354 auto res = this->prepare();
355 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
356 }
357
360 {
361 std::scoped_lock l(parametersMutex);
362 auto _res = this->_main();
363 auto res = this->main();
364 return {.status = skills::mergeSkillStatuseses(_res.status, res.status),
365 .data = res.data};
366 }
367
370 {
371 std::scoped_lock l(parametersMutex);
372 auto res = this->exit();
373 auto _res = this->_exit();
374 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
375 }
376
377 void
378 Skill::throwIfSkillShouldTerminate(const std::function<void()>& do_before,
379 const std::string& abortedMessage) const
380 {
382 {
383 do_before();
384 throwIfSkillShouldTerminate(abortedMessage);
385 }
386 }
387
388 void
389 Skill::throwIfSkillShouldTerminate(const std::string& abortedMessage) const
390 {
391 if (stopped)
392 {
393 std::string message =
394 std::string("The skill '" + getSkillId().toString() + "' was asked to stop.");
395 message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
396
397 throw error::SkillAbortedException(__PRETTY_FUNCTION__, message);
398 return;
399 }
400
401 if (timeoutReached)
402 {
403 std::string message =
404 std::string("The skill '" + getSkillId().toString() + "' reached timeout.");
405 message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
406
407 ARMARX_WARNING << message;
408 throw error::SkillFailedException(__PRETTY_FUNCTION__, message);
409 }
410 }
411
414 {
415 return MainResult{
417 .data = data,
418 };
419 }
420
423 {
424 return MainResult{
426 .data = data,
427 };
428 }
429
432 {
433 return MainResult{
435 .data = data,
436 };
437 }
438
439 void
441 {
442 if (stopped)
443 {
444 // skill already got stopped. Ignore
445 return;
446 }
447 std::scoped_lock l(subskillsMutex);
448 stopped = true;
449 _onStopRequested();
451 }
452
453 void
455 {
456 if (stopped || timeoutReached)
457 {
458 // skill already got timeoutReached. Ignore
459 return;
460 }
461
462 std::scoped_lock l(subskillsMutex);
463 timeoutReached = true;
464 _onTimeoutReached();
466 }
467
468 bool
470 {
471 return stopped || timeoutReached;
472 }
473
474 // condition effects
475 void
476 Skill::_onTimeoutReached()
477 {
478 // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
479
480 if (!manager)
481 {
482 return;
483 }
484
485 for (const auto& execId : subskills)
486 {
487 manager->abortSkillAsync(execId.toManagerIce());
488 }
489 }
490
491 void
492 Skill::_onStopRequested()
493 {
494 // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
495
496 if (!manager)
497 {
498 return;
499 }
500
501 for (const auto& execId : subskills)
502 {
503 manager->abortSkillAsync(execId.toManagerIce());
504 }
505 }
506
507 void
511
512 void
516
517 // always called before prepare (should not take longer than 100ms)
520 {
521 // Default nothing to init
522 return {.status = TerminatedSkillStatus::Succeeded};
523 }
524
525 // always called before main (should not take longer than 100ms)
528 {
529 // Default nothing to prepare
531 }
532
533 // always called after main or if skill fails (should not take longer than 100ms)
536 {
537 // Default nothing to exit
538 return {.status = TerminatedSkillStatus::Succeeded};
539 }
540
543 {
544 // This is just a dummy implementation
545 ARMARX_IMPORTANT << "Dummy executing skill '" << description.skillId
546 << "'. Please overwrite this method.";
547 return {.status = TerminatedSkillStatus::Succeeded, .data = nullptr};
548 }
549
550 void
552 {
553 description.skillId.providerId = pid;
554 }
555
556 void
558 {
559 this->callback = callback;
560 }
561
562 void
563 Skill::setManager(const manager::dti::SkillManagerInterfacePrx& manager)
564 {
565 this->manager = manager;
566 }
567
568 void
570 {
571 this->executorName = executorName;
572 }
573
574 void
576 {
577 GlobalSkillUpdateManager.update(statusUpdate);
578 }
579
582 {
583 return description;
584 }
585
586 SkillID
588 {
589 return description.skillId;
590 }
591
593 {
594 // ARMARX_IMPORTANT << "DESTROY SKILL " << getSkillId();
595 }
596
599 {
600 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
601 return callSubskillAsync(std::move(proxy), parameters);
602 }
603
604 } // namespace skills
605} // namespace armarx
static void WaitFor(const Duration &duration)
Wait for a certain duration on the virtual clock.
Definition Clock.cpp:99
SpamFilterDataPtr deactivateSpam(float deactivationDurationSec=10.0f, const std::string &identifier="", bool deactivate=true) const
disables the logging for the current line for the given amount of seconds.
Definition Logging.cpp:99
void setTag(const LogTag &tag)
Definition Logging.cpp:54
static DateTime Now()
Definition DateTime.cpp:51
Represents a duration.
Definition Duration.h:17
Simple rate limiter for use in loops to maintain a certain frequency given a clock.
Definition Metronome.h:57
std::string toString() const
Definition SkillID.cpp:68
virtual PrepareResult prepare()
Override this method with the actual implementation.
Definition Skill.cpp:527
CallbackT callback
Definition Skill.h:362
void installConditionWithCallback(std::function< bool()> &&f, std::function< void()> &&cb)
install a condition which is frequently checked from the conditionCheckingThread
Definition Skill.cpp:47
std::atomic_bool running
Definition Skill.h:381
armarx::core::time::DateTime started
Definition Skill.h:356
void notifyTimeoutReached()
Definition Skill.cpp:454
armarx::core::time::DateTime exited
Definition Skill.h:357
std::optional< TerminatedSkillStatusUpdate > callSubskill(const SkillID &skillId)
Call a subskill with the given ID and its default parameters.
Definition Skill.cpp:119
std::atomic_bool finished
Definition Skill.h:383
std::atomic_bool exiting
Definition Skill.h:382
static MainResult MakeSucceededResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:413
void setManager(const manager::dti::SkillManagerInterfacePrx &manager)
Definition Skill.cpp:563
manager::dti::SkillManagerInterfacePrx manager
Definition Skill.h:363
InitResult initSkill()
Initialization of a skill.
Definition Skill.cpp:341
virtual InitResult init()
Override this method with the actual implementation.
Definition Skill.cpp:519
static MainResult MakeAbortedResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:431
armarx::aron::data::DictPtr parameters
Definition Skill.h:369
ExitResult exitSkill()
Exit method of a skill.
Definition Skill.cpp:369
std::atomic_bool constructing
Definition Skill.h:378
void notifySkillToStop()
Notify the skill from extern to stop.
Definition Skill.cpp:440
PrepareResult prepareSkill()
Prepare a skill once.
Definition Skill.cpp:350
std::atomic_bool timeoutReached
Definition Skill.h:387
SkillDescription description
Definition Skill.h:372
std::mutex parametersMutex
Definition Skill.h:368
virtual void onStopRequested()
Definition Skill.cpp:513
bool shouldSkillTerminate() const override
Returns whether the skill should terminate as soon as possible.
Definition Skill.cpp:469
std::atomic_bool stopped
Definition Skill.h:386
virtual MainResult main()
Override this method with the actual implementation.
Definition Skill.cpp:542
void throwIfSkillShouldTerminate(const std::string &abortedMessage="") const
Definition Skill.cpp:389
SkillID getSkillId() const
Get the id of the skill.
Definition Skill.cpp:587
virtual ExitResult exit()
Override this method with the actual implementation.
Definition Skill.cpp:535
void setCallback(const CallbackT &callback)
Definition Skill.cpp:557
~Skill() override
Virtual destructor of a skill.
Definition Skill.cpp:592
MainResult mainOfSkill()
Main method of a skill.
Definition Skill.cpp:359
aron::data::DictPtr getParameters() const
Get the parameters of a skill that have been set so far.
Definition Skill.cpp:219
SkillExecutionHandlePtr callSubskillAsync(const SkillID &skillId, std::function< void(aron::data::DictPtr &)> parametersFunction)
Definition Skill.cpp:148
void setParameters(const aron::data::DictPtr &d)
Hard set the parameters, ignoring everything that has been set or merged before.
Definition Skill.cpp:206
std::atomic_bool initializing
Definition Skill.h:379
void updateSubSkillStatus(const skills::SkillStatusUpdate &statusUpdate)
Definition Skill.cpp:575
SkillDescription getSkillDescription() const
Get the description of a skill.
Definition Skill.cpp:581
Skill()=delete
We completely remove the default constructor!
virtual void onTimeoutReached()
Override these methods if you want to do something special when notification comes.
Definition Skill.cpp:508
static MainResult MakeFailedResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:422
void updateParameters(const aron::data::DictPtr &d)
Merge parameters to the local parameters of the skill.
Definition Skill.cpp:190
void setProviderId(const skills::ProviderID &pid)
Set the provider id of the description of the skill.
Definition Skill.cpp:551
void waitFor(const armarx::Duration &duration, const armarx::Duration &interval=armarx::Duration::MilliSeconds(200)) const override
Definition Skill.cpp:54
void setExecutorName(const std::string &executorName)
Definition Skill.cpp:569
std::function< void(const SkillStatus s, const armarx::aron::data::DictPtr &)> CallbackT
Definition Skill.h:45
std::string executorName
Definition Skill.h:364
std::atomic_bool preparing
Definition Skill.h:380
#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...
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:193
std::shared_ptr< Dict > DictPtr
Definition Dict.h:42
aron::data::DictPtr make_dict(_Args &&... args)
Definition Dict.h:107
std::unique_ptr< class SkillExecutionHandle > SkillExecutionHandlePtr
Definition Skill.h:35
std::unique_ptr< class SkillProxy > SkillProxyPtr
SkillUpdateManager GlobalSkillUpdateManager
TerminatedSkillStatus mergeSkillStatuseses(const TerminatedSkillStatus t1, const TerminatedSkillStatus t2)
This file offers overloads of toIce() and fromIce() functions for STL container types.
Interval< T > interval(T lo, T hi)
This file is part of ArmarX.
aron::data::DictPtr rootProfileDefaults
aron::type::ObjectPtr parametersType
armarx::core::time::Duration timeout
A result struct for skill exit function.
Definition Skill.h:69
A result struct for skill initialization.
Definition Skill.h:50
A result struct for th main method of a skill.
Definition Skill.h:62
A result struct for skill preparing.
Definition Skill.h:56