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
11#include <Ice/LocalException.h>
12
19
20#include <RobotAPI/interface/skills/SkillManagerInterface.h>
30
31namespace armarx
32{
33 namespace skills
34 {
36 {
37 // replace constructor if you want to have a specific logging tag
38 Logging::setTag("armarx::skills::" + description.skillId.toString());
39
41 if (!desc.rootProfileDefaults->fullfillsType(desc.parametersType))
42 {
43 ARMARX_WARNING << "Missing root profile defaults for skill " << desc.skillId;
44 }
45 }
46
47 // install a local condition via a lambda
48 void
49 Skill::installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb)
50 {
51 std::scoped_lock l(conditionCallbacksMutex);
52 conditionCallbacks.push_back({f, cb});
53 }
54
55 void
57 {
58 double n = duration / interval;
59 auto metronome = armarx::Metronome(interval);
60 while (n > 1 and not shouldSkillTerminate())
61 {
62 n--;
63 metronome.waitForNextTick();
64 }
65 if (n > 0.05 and not shouldSkillTerminate())
66 {
67 // Wait for the remaining time
69 }
70 }
71
72 std::optional<TerminatedSkillStatusUpdate>
73 Skill::callSubskill(SkillProxyPtr proxy)
74 {
75 auto parameters = proxy->getRootProfileParameters();
76 return callSubskill(std::move(proxy), parameters);
77 }
79 std::optional<TerminatedSkillStatusUpdate>
81 {
82 auto handle = callSubskillAsync(std::move(proxy), parameters);
84 auto ret = handle->join();
85
86 // While the sub skill was running, our skill might also have been aborted.
87 // In this case, the correct behavour would be aborting ourselves.
88 // The caller of callSubskill() can catch the thrown error::SkillAbortedException
89 // if necessary.
91 return ret;
92 }
93
95 Skill::callSubskillAsync(Skill::SkillProxyPtr prx)
96 {
97 aron::data::DictPtr params = prx->getRootProfileParameters();
98 return callSubskillAsync(std::move(prx), params);
99 }
100
102 Skill::callSubskillAsync(Skill::SkillProxyPtr prx, const aron::data::DictPtr& params)
103 {
104 std::string executorHistory = this->executorName + "->" + getSkillId().toString();
105
107 auto eid = prx->executeSkillAsync(executorHistory, params);
108
109 std::unique_lock l(subskillsMutex);
111 [&]()
112 {
113 prx->abortSkillAsync(eid);
114 }); // also notify newly added skill as it was not added to subskills list yet
115 this->subskills.push_back(eid);
116
117 return std::make_unique<SkillExecutionHandle>(std::move(prx), eid);
118 }
119
120 std::optional<TerminatedSkillStatusUpdate>
122 {
123 return callSubskill(std::make_unique<SkillProxy>(manager, skillId));
124 }
125
126 std::optional<TerminatedSkillStatusUpdate>
128 {
129 return callSubskill(std::make_unique<SkillProxy>(manager, skillId), parameters);
130 }
131
132 std::optional<TerminatedSkillStatusUpdate>
133 Skill::callSubskill(const SkillID& skillId,
134 std::function<void(aron::data::DictPtr&)> parametersFunction)
135 {
136 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
137
138 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
139 if (not parameters)
140 {
142 }
143
144 parametersFunction(parameters);
145
146 return callSubskill(std::move(proxy), parameters);
147 }
148
151 std::function<void(aron::data::DictPtr&)> parametersFunction)
152 {
153 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
154
155 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
156 if (not parameters)
157 {
159 }
160
161 parametersFunction(parameters);
162
163 return callSubskillAsync(std::move(proxy), parameters);
164 }
165
168 {
169 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
170
171 aron::data::DictPtr parameters = proxy->getRootProfileParameters();
172 if (not parameters)
173 {
175 }
176
177 return callSubskillAsync(std::move(proxy), parameters);
178 }
179
180 std::optional<TerminatedSkillStatusUpdate>
181 Skill::executeSkill(
182 const ::armarx::skills::SkillID& skillID,
183 std::function<aron::data::DictPtr(aron::data::DictPtr&)> const& parametersFunction)
184 {
185 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillID);
186
187 aron::data::DictPtr parameter_defaults = proxy->getRootProfileParameters();
188 return callSubskill(std::move(proxy), parametersFunction(parameter_defaults));
189 }
190
191 void
193 {
194 std::scoped_lock l(this->parametersMutex);
195 if (this->parameters == nullptr)
196 {
197 // set params as there has been no update before.
198 this->parameters = d;
199 }
200 else
201 {
202 // merge params into existing. Note that this may update already set params.
203 this->parameters->mergeAndReplaceCopy(d);
204 }
205 }
206
207 void
209 {
210 // we only set the params if the skill is not already running
211 if (running or exiting or finished)
212 {
213 return;
214 }
215
216 std::scoped_lock l(this->parametersMutex);
217 this->parameters = d;
218 }
219
222 {
223 return this->parameters;
224 }
225
227 Skill::_init()
228 {
229 // ARMARX_IMPORTANT << "Initializing skill '" << description.skillName << "'";
230 this->initializing = true;
231 this->constructing = false;
232 this->preparing = false;
233 this->running = false;
234 this->exiting = false;
235 this->finished = false;
236
237 // install timeout condition
239 [&]() {
241 },
242 [&]() { notifyTimeoutReached(); });
243
244 conditionCheckingThread = std::thread(
245 [&]()
246 {
247 armarx::core::time::Metronome metronome(conditionCheckingThreadFrequency);
248 while (initializing or preparing or
249 running) // when the skill ends/aborts this variable will be set to false
250 {
251 {
252 std::scoped_lock l(conditionCallbacksMutex);
253 for (auto& p : conditionCallbacks)
254 {
255 auto& f = p.first;
256 auto& cb = p.second;
257 if (f())
258 {
259 cb();
260 }
261 }
262 }
263
264 const auto sleepDuration = metronome.waitForNextTick();
265 if (not sleepDuration.isPositive())
266 {
268 << "ConditionCheckingThread: execution took too long ("
269 << -sleepDuration << " vs "
270 << conditionCheckingThreadFrequency.toCycleDuration()
271 << ")";
272 }
273 }
274 });
275 return {.status = TerminatedSkillStatus::Succeeded};
276 }
277
279 Skill::_prepare()
280 {
281 this->preparing = true;
282 this->initializing = false;
283 this->constructing = false;
284 this->running = false;
285 this->exiting = false;
286 this->finished = false;
287
289 {
291 }
292
293 // Default nothing to prepare
294 if (not description.parametersType)
295 {
297 }
298 if (this->parameters && this->parameters->fullfillsType(description.parametersType))
299 {
300 // wait until parameters fulfill type
302 }
303
304 // false if we have to wait for parameters
306 }
307
309 Skill::_main()
310 {
311 this->running = true;
312 this->initializing = false;
313 this->constructing = false;
314 this->preparing = false;
315 this->exiting = false;
316 this->finished = false;
317 return {.status = TerminatedSkillStatus::Succeeded};
318 }
319
321 Skill::_exit()
322 {
323 // ARMARX_IMPORTANT << "Exiting Skill '" << description.skillName << "'";
324 this->exiting = true;
325 this->running = false;
326 this->initializing = false;
327 this->constructing = false;
328 this->preparing = false;
329 this->finished = false;
330
331 if (conditionCheckingThread.joinable())
332 {
333 conditionCheckingThread.join();
334 }
336
337 this->finished = true;
338 this->exiting = false;
339 return {.status = TerminatedSkillStatus::Succeeded};
340 }
341
344 {
345 std::scoped_lock l(parametersMutex);
346 auto _res = this->_init();
347 auto res = this->init();
348 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
349 }
350
353 {
354 std::scoped_lock l(parametersMutex);
355 auto _res = this->_prepare();
356 auto res = this->prepare();
357 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
358 }
359
362 {
363 std::scoped_lock l(parametersMutex);
364 auto _res = this->_main();
365 auto res = this->main();
366 return {.status = skills::mergeSkillStatuseses(_res.status, res.status),
367 .data = res.data};
368 }
369
372 {
373 std::scoped_lock l(parametersMutex);
374 auto res = this->exit();
375 auto _res = this->_exit();
376 return {.status = skills::mergeSkillStatuseses(_res.status, res.status)};
377 }
378
379 void
380 Skill::throwIfSkillShouldTerminate(const std::function<void()>& do_before,
381 const std::string& abortedMessage) const
382 {
384 {
385 do_before();
386 throwIfSkillShouldTerminate(abortedMessage);
387 }
388 }
389
390 void
391 Skill::throwIfSkillShouldTerminate(const std::string& abortedMessage) const
392 {
393 if (stopped)
394 {
395 std::string message =
396 std::string("The skill '" + getSkillId().toString() + "' was asked to stop.");
397 message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
398
399 throw error::SkillAbortedException(__PRETTY_FUNCTION__, message);
400 return;
401 }
402
403 if (timeoutReached)
404 {
405 std::string message =
406 std::string("The skill '" + getSkillId().toString() + "' reached timeout.");
407 message += abortedMessage.empty() ? "" : " Additional message: " + abortedMessage;
408
409 ARMARX_WARNING << message;
410 throw error::SkillFailedException(__PRETTY_FUNCTION__, message);
411 }
412 }
413
416 {
417 return MainResult{
419 .data = data,
420 };
421 }
422
425 {
426 return MainResult{
428 .data = data,
429 };
430 }
431
434 {
435 return MainResult{
437 .data = data,
438 };
439 }
440
441 void
443 {
444 if (stopped)
445 {
446 // skill already got stopped. Ignore
447 return;
448 }
449 std::scoped_lock l(subskillsMutex);
450 stopped = true;
451 _onStopRequested();
453 }
454
455 void
457 {
458 if (stopped || timeoutReached)
459 {
460 // skill already got timeoutReached. Ignore
461 return;
462 }
463
464 std::scoped_lock l(subskillsMutex);
465 timeoutReached = true;
466 _onTimeoutReached();
468 }
469
470 bool
472 {
473 return stopped || timeoutReached;
474 }
475
476 // condition effects
477 void
478 Skill::_onTimeoutReached()
479 {
480 // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
481
482 if (!manager)
483 {
484 return;
485 }
486
487 for (const auto& execId : subskills)
488 {
489 try
490 {
491 manager->abortSkillAsync(execId.toManagerIce());
492 }
493 catch (const Ice::LocalException& e)
494 {
495 ARMARX_WARNING << "Failed to abort subskill '" << execId.skillId
496 << "': " << e.what();
497 }
498 }
499 }
500
501 void
502 Skill::_onStopRequested()
503 {
504 // WE ASSUME THAT THE LOCK IS ALREADY TAKEN
505
506 if (!manager)
507 {
508 return;
509 }
510
511 for (const auto& execId : subskills)
512 {
513 try
514 {
515 manager->abortSkillAsync(execId.toManagerIce());
516 }
517 catch (const Ice::LocalException& e)
518 {
519 ARMARX_WARNING << "Failed to abort subskill '" << execId.skillId
520 << "': " << e.what();
521 }
522 }
523 }
524
525 void
529
530 void
534
535 // always called before prepare (should not take longer than 100ms)
538 {
539 // Default nothing to init
540 return {.status = TerminatedSkillStatus::Succeeded};
541 }
542
543 // always called before main (should not take longer than 100ms)
546 {
547 // Default nothing to prepare
549 }
550
551 // always called after main or if skill fails (should not take longer than 100ms)
554 {
555 // Default nothing to exit
556 return {.status = TerminatedSkillStatus::Succeeded};
557 }
558
561 {
562 // This is just a dummy implementation
563 ARMARX_IMPORTANT << "Dummy executing skill '" << description.skillId
564 << "'. Please overwrite this method.";
565 return {.status = TerminatedSkillStatus::Succeeded, .data = nullptr};
566 }
567
568 void
570 {
571 description.skillId.providerId = pid;
572 }
573
574 void
576 {
577 this->callback = callback;
578 }
579
580 void
581 Skill::setManager(const manager::dti::SkillManagerInterfacePrx& manager)
582 {
583 this->manager = manager;
584 }
585
586 void
588 {
589 this->executorName = executorName;
590 }
591
592 void
594 {
595 GlobalSkillUpdateManager.update(statusUpdate);
596 }
597
600 {
601 return description;
602 }
603
604 SkillID
606 {
607 return description.skillId;
608 }
609
611 {
612 // ARMARX_IMPORTANT << "DESTROY SKILL " << getSkillId();
613 }
614
617 {
618 SkillProxyPtr proxy = std::make_unique<SkillProxy>(manager, skillId);
619 return callSubskillAsync(std::move(proxy), parameters);
620 }
621
622 } // namespace skills
623} // 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:545
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:49
std::atomic_bool running
Definition Skill.h:381
armarx::core::time::DateTime started
Definition Skill.h:356
void notifyTimeoutReached()
Definition Skill.cpp:456
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:121
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:415
void setManager(const manager::dti::SkillManagerInterfacePrx &manager)
Definition Skill.cpp:581
manager::dti::SkillManagerInterfacePrx manager
Definition Skill.h:363
InitResult initSkill()
Initialization of a skill.
Definition Skill.cpp:343
virtual InitResult init()
Override this method with the actual implementation.
Definition Skill.cpp:537
static MainResult MakeAbortedResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:433
armarx::aron::data::DictPtr parameters
Definition Skill.h:369
ExitResult exitSkill()
Exit method of a skill.
Definition Skill.cpp:371
std::atomic_bool constructing
Definition Skill.h:378
void notifySkillToStop()
Notify the skill from extern to stop.
Definition Skill.cpp:442
PrepareResult prepareSkill()
Prepare a skill once.
Definition Skill.cpp:352
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:531
bool shouldSkillTerminate() const override
Returns whether the skill should terminate as soon as possible.
Definition Skill.cpp:471
std::atomic_bool stopped
Definition Skill.h:386
virtual MainResult main()
Override this method with the actual implementation.
Definition Skill.cpp:560
void throwIfSkillShouldTerminate(const std::string &abortedMessage="") const
Definition Skill.cpp:391
SkillID getSkillId() const
Get the id of the skill.
Definition Skill.cpp:605
virtual ExitResult exit()
Override this method with the actual implementation.
Definition Skill.cpp:553
void setCallback(const CallbackT &callback)
Definition Skill.cpp:575
~Skill() override
Virtual destructor of a skill.
Definition Skill.cpp:610
MainResult mainOfSkill()
Main method of a skill.
Definition Skill.cpp:361
aron::data::DictPtr getParameters() const
Get the parameters of a skill that have been set so far.
Definition Skill.cpp:221
SkillExecutionHandlePtr callSubskillAsync(const SkillID &skillId, std::function< void(aron::data::DictPtr &)> parametersFunction)
Definition Skill.cpp:150
void setParameters(const aron::data::DictPtr &d)
Hard set the parameters, ignoring everything that has been set or merged before.
Definition Skill.cpp:208
std::atomic_bool initializing
Definition Skill.h:379
void updateSubSkillStatus(const skills::SkillStatusUpdate &statusUpdate)
Definition Skill.cpp:593
SkillDescription getSkillDescription() const
Get the description of a skill.
Definition Skill.cpp:599
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:526
static MainResult MakeFailedResult(aron::data::DictPtr data=nullptr)
Definition Skill.cpp:424
void updateParameters(const aron::data::DictPtr &d)
Merge parameters to the local parameters of the skill.
Definition Skill.cpp:192
void setProviderId(const skills::ProviderID &pid)
Set the provider id of the description of the skill.
Definition Skill.cpp:569
void waitFor(const armarx::Duration &duration, const armarx::Duration &interval=armarx::Duration::MilliSeconds(200)) const override
Definition Skill.cpp:56
void setExecutorName(const std::string &executorName)
Definition Skill.cpp:587
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:188
#define ARMARX_WARNING
The logging level for unexpected behaviour, but not a serious problem.
Definition Logging.h:191
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