SkillManagerComponentPlugin.cpp
Go to the documentation of this file.
2
3#include <experimental/memory>
4#include <list>
5#include <memory>
6#include <mutex>
7#include <optional>
8#include <shared_mutex>
9#include <stdexcept>
10#include <string>
11#include <thread>
12#include <tuple>
13#include <utility>
14#include <vector>
15
16#include <boost/locale.hpp>
17#include <boost/uuid/name_generator.hpp>
18#include <boost/uuid/nil_generator.hpp>
19#include <boost/uuid/string_generator.hpp>
20#include <boost/uuid/uuid.hpp>
21#include <boost/uuid/uuid_generators.hpp>
22#include <boost/uuid/uuid_io.hpp>
23
24#include <IceUtil/UUID.h>
25
26#include <SimoxUtility/algorithm/string/string_tools.h>
27#include <SimoxUtility/json/json.hpp>
28
34
55#include <RobotAPI/interface/aron/Aron.h>
56#include <RobotAPI/interface/skills/SkillManagerInterface.h>
61
62namespace armarx::plugins
63{
64 void
66 {
67 // Create mock root profile
69 root.id = "root";
70 root.name = "root";
71 root.description = "This is the root profile. It is the parent of all other profiles.";
72 root.parentPtr = nullptr;
73
74 std::unique_lock profilesLock(fluxioDC.profilesMutex);
75 fluxioDC.profiles[root.id] = root;
76 profilesLock.unlock();
77
78 // event parameter type
79 const auto& baseTypesPtr = std::make_shared<aron::type::Object>();
80 const std::string& keyName = "BaseTypesNamespace";
81 baseTypesPtr->setObjectName(keyName);
82 const auto& eventTypePtr = std::make_shared<aron::type::Object>();
83 eventTypePtr->setObjectName("Event");
84 baseTypesPtr->addMemberType("Event", eventTypePtr);
85
86 std::unique_lock typesLock(fluxioDC.typesMutex);
87 fluxioDC.types[keyName] = baseTypesPtr;
88 typesLock.unlock();
89 }
90
91 template <typename S, typename T>
92 std::vector<std::experimental::observer_ptr<const T>>
93 SkillManagerComponentPlugin::convertMapValuesToObserverVector(std::map<S, T>& map)
94 {
95 std::vector<std::experimental::observer_ptr<const T>> ret;
96 for (const auto& [k, v] : map)
97 {
98 ret.push_back(std::experimental::make_observer(&v));
99 }
100 return ret;
101 }
102
103 void
109
110 void
114
117 {
118 // NON LOCKING! WE ASSERT THAT THE CALLER HOLDS LOCK
119 for (const auto& [providerName, providerPrx] : skillProviderMap)
120 {
121 auto allSkills = providerPrx->getSkillDescriptions();
122 for (const auto& [currentSkillID, skillDesc] : allSkills)
123 {
124 if (currentSkillID.skillName == skillId.skillName)
125 {
126 return {providerName};
127 }
128 }
129 }
130 return {"INVALID PROVIDER NAME"};
131 }
132
133 void
135 {
136 std::scoped_lock l(skillProviderMapMutex);
137 if (skillProviderMap.find(providerInfo.providerId) == skillProviderMap.end())
138 {
139 ARMARX_INFO << "Adding a provider with name '" << providerInfo.providerId.providerName
140 << "'.";
141 skillProviderMap.insert({providerInfo.providerId, providerInfo.providerInterface});
142 }
143 else
144 {
145 ARMARX_INFO << "Trying to add a provider with name '"
146 << providerInfo.providerId.providerName
147 << "' but the provider already exists. "
148 << "Overwriting the old provider info.";
149 skillProviderMap[providerInfo.providerId] = providerInfo.providerInterface;
150 }
151
152 for (const auto& [skillID, skillDesc] : providerInfo.providedSkills)
153 {
154 descriptionIdToFluxioSkill(skillID, skillDesc);
155 ARMARX_INFO << "Adding skill '" << skillID.skillName << "' as FluxioSkill.";
156 }
157 }
158
159 void
161 {
162 std::scoped_lock l(skillProviderMapMutex);
163 if (auto it = skillProviderMap.find(providerId); it != skillProviderMap.end())
164 {
165 ARMARX_INFO << "Removing a provider with name '" << providerId.providerName << "'.";
166 skillProviderMap.erase(it);
167 }
168 else
169 {
170 ARMARX_INFO << "Trying to remove a provider with name '" << providerId.providerName
171 << "' but it couldn't be found.";
172 }
173 }
174
177 {
178 ARMARX_CHECK(executionRequest.skillId.isFullySpecified())
179 << "Got: " << executionRequest.skillId.toString();
180
181 std::unique_lock l(skillProviderMapMutex);
182
183 skills::ProviderID provderId(*executionRequest.skillId.providerId);
184
185 // TODO: Really support regexes!
186 if (executionRequest.skillId.providerId->providerName == "*")
187 {
188 provderId = getFirstProviderNameThatHasSkill(executionRequest.skillId);
189 }
190
191 if (auto it = skillProviderMap.find(provderId); it != skillProviderMap.end())
192 {
193 const auto& provider = it->second;
194
195 if (!provider)
196 {
197 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
198 << provderId << "'. Removing it from skills.";
199 skillProviderMap.erase(it);
200
202 __PRETTY_FUNCTION__,
203 "Skill execution failed. Could not execute a skill of provider '" +
204 provderId.toString() + "' because the provider does not exist.");
205 }
206
207 try
208 {
209 skills::SkillExecutionRequest provider_executionRequest{
210 .skillId = executionRequest.skillId,
211 .executorName = executionRequest.executorName,
212 .parameters = executionRequest.parameters,
213 .callbackInterface = myPrx};
214
215 auto async =
216 provider->begin_executeSkill(provider_executionRequest.toProviderIce());
217 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
218 auto provider_statusUpdate_ice = provider->end_executeSkill(async);
219
220 // convert to manager view
221 auto statusUpdate =
222 skills::SkillStatusUpdate::FromIce(provider_statusUpdate_ice, provderId);
223 return statusUpdate;
224 }
225 catch (const std::exception& e)
226 {
227 l.lock();
228
229 handleExceptionNonLockingThrow(__PRETTY_FUNCTION__, e, provderId);
230 }
231 }
232 else
233 {
235 __PRETTY_FUNCTION__,
236 "Skill execution failed. Could not execute a skill of provider '" +
237 provderId.toString() + "' because the provider does not exist.");
238 }
239 }
240
243 const skills::SkillExecutionRequest& executionRequest)
244 {
245 ARMARX_CHECK(executionRequest.skillId.isFullySpecified())
246 << "Got: " << executionRequest.skillId.toString();
247
248 std::unique_lock l(skillProviderMapMutex);
249
250 skills::ProviderID provderId(*executionRequest.skillId.providerId);
251
252 // TODO: Really support regexes!
253 if (executionRequest.skillId.providerId->providerName == "*")
254 {
255 provderId = getFirstProviderNameThatHasSkill(executionRequest.skillId);
256 }
257
258 if (auto it = skillProviderMap.find(provderId); it != skillProviderMap.end())
259 {
260 const auto& provider = it->second;
261
262 if (!provider)
263 {
264 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
265 << provderId << "'. Removing it from skills.";
266 skillProviderMap.erase(it);
267
269 __PRETTY_FUNCTION__,
270 "Skill execution failed. Could not execute a skill of provider '" +
271 provderId.toString() + "' because the provider does not exist.");
272 }
273
274 try
275 {
276 skills::SkillExecutionRequest provider_executionRequest{
277 .skillId = executionRequest.skillId,
278 .executorName = executionRequest.executorName,
279 .parameters = executionRequest.parameters,
280 .callbackInterface = myPrx};
281
282 auto async =
283 provider->begin_executeSkillAsync(provider_executionRequest.toProviderIce());
284 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
285 auto provider_executionID_ice = provider->end_executeSkillAsync(async);
286
287 // convert to manager view
288 auto executionId =
289 skills::SkillExecutionID::FromIce(provider_executionID_ice, provderId);
290 executionId.skillId.providerId = provderId;
291 return executionId;
292 }
293 catch (const std::exception& e)
294 {
295 l.lock();
296
297 handleExceptionNonLockingThrow(__PRETTY_FUNCTION__, e, provderId);
298 }
299 }
300 else
301 {
303 __PRETTY_FUNCTION__,
304 "Skill execution failed. Could not execute a skill of provider '" +
305 provderId.toString() + "' because the provider does not exist.");
306 }
307 }
308
309 bool
312 {
313 ARMARX_DEBUG << "updateSkillParameters for skill " << executionId.skillId;
314
316 << "Got: " << executionId.skillId.toString();
317
318 std::unique_lock l(skillProviderMapMutex);
319 if (auto it = skillProviderMap.find(*executionId.skillId.providerId);
320 it != skillProviderMap.end())
321 {
322 const auto& providerId = it->first;
323 const auto& provider = it->second;
324
325 if (!provider)
326 {
327 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
328 << providerId << "'. Removing it from skills.";
329 skillProviderMap.erase(it);
330 return false;
331 }
332
333 try
334 {
335 auto async = provider->begin_updateSkillParameters(executionId.toProviderIce(),
336 data->toAronDictDTO());
337 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
338 auto r = provider->end_updateSkillParameters(async);
339 return r.success;
340 }
341 catch (const std::exception& e)
342 {
343 l.lock();
344
345 handleExceptionNonLocking(__PRETTY_FUNCTION__, e, providerId);
346
347 return false;
348 }
349 }
350 else
351 {
352 return false;
353 }
354 }
355
356 bool
358 {
359 ARMARX_DEBUG << "abortSkill for skill " << executionId.skillId;
360
362 << "Got: " << executionId.skillId.toString();
363
364 std::unique_lock l(skillProviderMapMutex);
365 if (auto it = skillProviderMap.find(*executionId.skillId.providerId);
366 it != skillProviderMap.end())
367 {
368 const auto& providerId = it->first;
369 const auto& provider = it->second;
370
371 if (!provider)
372 {
373 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
374 << providerId << "'. Removing it from skills.";
375 skillProviderMap.erase(it);
376 return false;
377 }
378
379 try
380 {
381 auto async = provider->begin_abortSkill(executionId.toProviderIce());
382 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
383 auto r = provider->end_abortSkill(async);
384 return r.success;
385 }
386 catch (const std::exception& e)
387 {
388 l.lock();
389
390 handleExceptionNonLocking(__PRETTY_FUNCTION__, e, providerId);
391
392 return false;
393 }
394 }
395 else
396 {
397 return false;
398 }
399 }
400
401 bool
403 {
405 << "Got: " << executionId.skillId.toString();
406
407 std::unique_lock l(skillProviderMapMutex);
408 if (auto it = skillProviderMap.find(*executionId.skillId.providerId);
409 it != skillProviderMap.end())
410 {
411 const auto& providerId = it->first;
412 const auto& provider = it->second;
413
414 if (!provider)
415 {
416 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
417 << providerId << "'. Removing it from skills.";
418 skillProviderMap.erase(it);
419 return false;
420 }
421
422 try
423 {
424 auto async = provider->begin_abortSkillAsync(executionId.toProviderIce());
425 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
426 auto r = provider->end_abortSkillAsync(async);
427 return r.success;
428 }
429 catch (const std::exception& e)
430 {
431 l.lock();
432
433 handleExceptionNonLocking(__PRETTY_FUNCTION__, e, providerId);
434
435 return false;
436 }
437 }
438 else
439 {
440 return false;
441 }
442 }
443
444 std::optional<skills::SkillDescription>
446 {
447 ARMARX_DEBUG << "getSkillDescription for skill " << skillId;
448
449 ARMARX_CHECK(skillId.isFullySpecified()) << "Got: " << skillId.toString();
450
451 std::unique_lock l(skillProviderMapMutex);
452 if (auto it = skillProviderMap.find(*skillId.providerId); it != skillProviderMap.end())
453 {
454 const auto& providerId = it->first;
455 const auto& provider = it->second;
456
457 if (!provider)
458 {
459 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
460 << providerId << "'. Removing it from skills.";
461 skillProviderMap.erase(it);
462
463 throw skills::error::SkillException(__PRETTY_FUNCTION__,
464 "Skill execution failed. Could not query a "
465 "status update of a skill of provider '" +
466 providerId.toString() +
467 "' because the provider does not exist.");
468 }
469
470 try
471 {
472 auto async = provider->begin_getSkillDescription(skillId.toProviderIce());
473 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
474 auto provider_desc_ice = provider->end_getSkillDescription(async);
475
476 if (not provider_desc_ice)
477 {
478 return std::nullopt;
479 }
480
481 // convert to manager view
482 auto desc =
483 skills::SkillDescription::FromIce(provider_desc_ice.value(), providerId);
484 return desc;
485 }
486 catch (const std::exception& e)
487 {
488 l.lock();
489
490 handleExceptionNonLockingThrow(__PRETTY_FUNCTION__, e, providerId);
491 }
492 }
493 else
494 {
495 return std::nullopt;
496 }
497 }
498
499 std::map<skills::SkillID, skills::SkillDescription>
501 {
502 std::map<skills::SkillID, skills::SkillDescription> ret;
503
504 std::unique_lock l(skillProviderMapMutex);
505 for (auto it = skillProviderMap.cbegin(); it != skillProviderMap.cend();)
506 {
507 const auto& providerId = it->first;
508 const auto& provider = it->second;
509
510 if (!provider)
511 {
512 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
513 << providerId << "'. Removing it from skills.";
514 it = skillProviderMap.erase(it);
515 continue;
516 }
517
518 try
519 {
520 auto async = provider->begin_getSkillDescriptions();
521 l.unlock();
522 auto m = provider->end_getSkillDescriptions(async);
523 l.lock();
524
525 for (const auto& [provider_skillId_ice, skillDescription_ice] : m)
526 {
527 ret.insert(
528 {skills::SkillID::FromIce(provider_skillId_ice, providerId),
529 skills::SkillDescription::FromIce(skillDescription_ice, providerId)});
530 }
531
532 ++it;
533 }
534 catch (const std::exception& e)
535 {
536 l.lock();
537
538 if (auto _it = handleExceptionNonLocking(__PRETTY_FUNCTION__, e, providerId))
539 {
540 it = _it.value(); // next element
541 }
542 }
543 }
544 return ret;
545 }
546
547 std::optional<skills::SkillStatusUpdate>
549 const skills::SkillExecutionID& executionId)
550 {
552 << "Got: " << executionId.skillId.toString();
553
554 std::unique_lock l(skillProviderMapMutex);
555 if (auto it = skillProviderMap.find(*executionId.skillId.providerId);
556 it != skillProviderMap.end())
557 {
558 const auto& providerId = it->first;
559 const auto& provider = it->second;
560
561 if (!provider)
562 {
563 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
564 << providerId << "'. Removing it from skills.";
565 skillProviderMap.erase(it);
566
567 throw skills::error::SkillException(__PRETTY_FUNCTION__,
568 "Skill execution failed. Could not query a "
569 "status update of a skill of provider '" +
570 providerId.toString() +
571 "' because the provider does not exist.");
572 }
573
574 try
575 {
576 auto async = provider->begin_getSkillExecutionStatus(executionId.toProviderIce());
577 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
578 auto provider_statusUpdate_ice = provider->end_getSkillExecutionStatus(async);
579
580 if (not provider_statusUpdate_ice)
581 {
582 return std::nullopt;
583 }
584
585 // convert to manager view
586 auto statusUpdate = skills::SkillStatusUpdate::FromIce(
587 provider_statusUpdate_ice.value(), providerId);
588
589 // ARMARX_WARNING << "Status update: " << statusUpdate.result;
590 return statusUpdate;
591 }
592 catch (const std::exception& e)
593 {
594 l.lock();
595
596 handleExceptionNonLockingThrow(__PRETTY_FUNCTION__, e, providerId);
597 }
598 }
599 else
600 {
601 return std::nullopt;
602 }
603 }
604
605 std::map<skills::SkillExecutionID, skills::SkillStatusUpdate>
607 {
608 std::map<skills::SkillExecutionID, skills::SkillStatusUpdate> ret;
609
610 std::unique_lock l(skillProviderMapMutex);
611 for (auto it = skillProviderMap.cbegin(); it != skillProviderMap.cend();)
612 {
613 const auto& providerId = it->first;
614 const auto& provider = it->second;
615
616 if (!provider)
617 {
618 ARMARX_WARNING << __PRETTY_FUNCTION__ << ": Found disconnected skill provider '"
619 << providerId << "'. Removing it from skills.";
620 it = skillProviderMap.erase(it);
621 continue;
622 }
623
624 try
625 {
626 auto async = provider->begin_getSkillExecutionStatuses();
627 l.unlock(); // allow parallel e.g. stopping. Otherwise the manager would lock himself in nested calls
628 auto m = provider->end_getSkillExecutionStatuses(async);
629 l.lock();
630
631 for (const auto& [provider_executionId_ice, provider_statusUpdate_ice] : m)
632 {
633 ret.insert(
634 {skills::SkillExecutionID::FromIce(provider_executionId_ice, providerId),
635 skills::SkillStatusUpdate::FromIce(provider_statusUpdate_ice,
636 providerId)});
637 }
638 it++;
639 }
640 catch (const std::exception& e)
641 {
642 if (auto _it = handleExceptionNonLocking(__PRETTY_FUNCTION__, e, providerId))
643 {
644 it = _it.value(); // next element
645 }
646 }
647 }
648 return ret;
649 }
650
651 std::optional<
652 std::map<skills::ProviderID, skills::provider::dti::SkillProviderInterfacePrx>::iterator>
653 SkillManagerComponentPlugin::handleExceptionNonLocking(const char* funcName,
654 const std::exception& e,
655 skills::ProviderID providerId,
656 bool eraseSkillProvider)
657 {
658 // NON LOCKING! WE ASSERT THAT THE CALLER HOLDS LOCK
659 if (auto it = skillProviderMap.find(providerId);
660 eraseSkillProvider and it != skillProviderMap.end())
661 {
662 ARMARX_WARNING << funcName << ": Found disconnected or buggy skill provider '"
663 << providerId << "' during execution. Removing it from skills. "
664 << "Error: " << e.what();
665 return skillProviderMap.erase(it);
666 }
667 else
668 {
669 ARMARX_WARNING << funcName << ": Found disconnected or buggy skill provider '"
670 << providerId
671 << "' during execution. However, it already got removed... "
672 << "Error: " << e.what();
673 return std::nullopt;
674 }
675 }
676
677 void
678 SkillManagerComponentPlugin::handleExceptionNonLockingThrow(const char* funcName,
679 const std::exception& e,
680 skills::ProviderID providerId,
681 bool eraseSkillProvider)
682 {
683 // NON LOCKING! WE ASSERT THAT THE CALLER HOLDS LOCK
684 handleExceptionNonLocking(funcName, e, providerId, eraseSkillProvider);
685
686 throw skills::error::SkillException(
687 funcName,
688 "Skill execution failed. Could not execute a skill of provider '" +
689 providerId.toString() + "' because the provider does not exist.");
690 }
691
692 //****************************//
693 //** Fluxio related methods **//
694 //****************************//
695
696 skills::Result<std::experimental::observer_ptr<skills::FluxioExecutor>,
697 skills::error::FluxioException>
699 const std::string& profileId,
700 const std::string& executorName,
702 {
703 const auto& result = getSkill(skillId);
704 if (!result.isSuccess())
705 {
706 ARMARX_WARNING << "Skill with id '" << skillId << "' not found.";
709 {skillId}),
711 "SkillManagerComponentPlugin",
712 __FUNCTION__,
713 __LINE__)};
714 }
715
716 const auto& skill = result.getResult();
717 if (skill == nullptr)
718 {
719 ARMARX_WARNING << "Unexpected nullptr for skill with id '" << skillId << "'.";
722 {skillId}),
724 "SkillManagerComponentPlugin",
725 __FUNCTION__,
726 __LINE__)};
727 }
728
729 const std::string& executionId = IceUtil::generateUUID();
730 const bool isNative = skill->native;
732
733 if (parameters == nullptr)
734 {
735 ARMARX_WARNING << "No parameters supplied for skill '" << skill->name << "(" << skillId
736 << ")'. Using profile values instead.";
737
738 parameters = std::make_shared<aron::data::Dict>();
739 }
740
742 << "Initializing skill execution with the following parameter values (overrides): "
744
745 if (!isNative)
746 {
747 const auto& executeFluxioSkillFunc =
748 [this](const std::string& skillId,
749 const std::string& profileId,
750 const std::string& executorName,
751 const armarx::aron::data::DictPtr& parameters)
752 {
753 return this->executeFluxioSkill(skillId, profileId, executorName, parameters)
754 .getResult();
755 };
756
757 const auto& executeFluxioSkillFunc2 =
758 [this](const std::string& skillId,
759 const std::string& profileId,
760 const std::string& executorName,
761 const armarx::aron::data::DictPtr& parameters)
762 {
763 return this->executeFluxioSkill(skillId, profileId, executorName, parameters)
764 .getResult();
765 };
766
767 const auto& addMergerExecutorToDCFunc =
768 [this](const std::vector<std::string>& parameterIds)
769 {
770 const auto& executorId = IceUtil::generateUUID();
771 std::unique_lock l(this->fluxioDC.fluxioExecutorsMutex);
772
773 this->fluxioDC.fluxioExecutors[executorId] =
774 std::make_unique<skills::FluxioMergerExecutor>(executorId, parameterIds);
775
776 const auto& ret = std::experimental::make_observer(
777 this->fluxioDC.fluxioExecutors[executorId].get());
778 l.unlock();
779 return ret;
780 };
781
782 const auto& addLoopExecutorToDCFunc =
783 [this, executeFluxioSkillFunc2](
784 const std::string& id, const skills::FluxioSkill& skill, bool isRetry)
785 {
786 const auto& executorId = IceUtil::generateUUID();
787 std::unique_lock l(this->fluxioDC.fluxioExecutorsMutex);
788
789 this->fluxioDC.fluxioExecutors[executorId] =
790 std::make_unique<skills::FluxioLoopExecutor>(
791 id, skill, isRetry, std::move(executeFluxioSkillFunc2));
792
793 const auto& ret = std::experimental::make_observer(
794 this->fluxioDC.fluxioExecutors[executorId].get());
795 l.unlock();
796 return ret;
797 };
798
799 std::shared_lock skillLock(fluxioDC.skillsMutex);
800 const auto& skillRef = fluxioDC.skills[skillId];
801 skillLock.unlock();
802
803 std::unique_lock l(fluxioDC.fluxioExecutorsMutex);
804 fluxioDC.fluxioExecutors[executionId] =
805 std::make_unique<skills::FluxioCompositeExecutor>(
806 executionId,
807 skillRef,
808 std::move(executeFluxioSkillFunc),
809 std::move(addMergerExecutorToDCFunc),
810 std::move(addLoopExecutorToDCFunc));
811
812 executorPtr =
813 std::experimental::make_observer(fluxioDC.fluxioExecutors[executionId].get());
814 l.unlock();
815
816 const auto& profilePtr =
817 std::experimental::make_observer(&(fluxioDC.profiles[profileId]));
818
819 std::thread([executorPtr, executorName, parameters, profilePtr]()
820 { executorPtr->run(executorName, parameters, profilePtr); })
821 .detach();
822 }
823 else
824 {
826 .providerId = skills::ProviderID{.providerName = skill->skillProviderPtr->name},
827 .skillName = skill->name};
828
829 auto skillDescr = getSkillDescription(sID);
830 if (!skillDescr.has_value())
831 {
832 ARMARX_WARNING << "Skill description for skill with id '" << skillId
833 << "' not found. Aborting execution." << sID.toString();
834
837 {skillId}),
839 "SkillManagerComponentPlugin",
840 __FUNCTION__,
841 __LINE__)};
842 }
843
844 const auto& abortSkillFunc = [this](const skills::SkillExecutionID& executionId)
845 { return this->abortSkill(executionId); };
846
847 const auto& executeSkillAsyncFunc = [this](const skills::SkillExecutionRequest& req)
848 { return this->executeSkillAsync(req); };
849
850 const auto& getSkillExecutionStatusFunc =
851 [this](const skills::SkillExecutionID& executionId)
852 { return this->getSkillExecutionStatus(executionId); };
853
854 std::unique_lock l(fluxioDC.fluxioExecutorsMutex);
855 fluxioDC.fluxioExecutors[executionId] = std::make_unique<skills::FluxioNativeExecutor>(
856 executionId,
857 sID,
858 *skill,
859 std::move(abortSkillFunc),
860 std::move(executeSkillAsyncFunc),
861 std::move(getSkillExecutionStatusFunc));
862
863 executorPtr =
864 std::experimental::make_observer(fluxioDC.fluxioExecutors[executionId].get());
865 l.unlock();
866
867 const auto& profilePtr =
868 std::experimental::make_observer(&(fluxioDC.profiles[profileId]));
869
870 std::thread([executorPtr, executorName, parameters, profilePtr]()
871 { executorPtr->run(executorName, parameters, profilePtr); })
872 .detach();
873 }
874
875 return {executorPtr};
876 }
877
878 void
879 SkillManagerComponentPlugin::abortFluxioSkill(const std::string& executionId)
880 {
881 std::shared_lock l(fluxioDC.fluxioExecutorsMutex);
882 const auto& executorIt = fluxioDC.fluxioExecutors.find(executionId);
883 if (executorIt == fluxioDC.fluxioExecutors.end())
884 {
885 ARMARX_WARNING << "Execution with id '" << executionId << "' not found.";
886
887 l.unlock();
890 {executionId}),
892 "SkillManagerComponentPlugin",
893 __FUNCTION__,
894 __LINE__)
895 .toManagerIce();
896 }
897 l.unlock();
898
899 executorIt->second->abort();
900 }
901
904 {
905 std::shared_lock l(fluxioDC.fluxioExecutorsMutex);
906 const auto& executorIt = fluxioDC.fluxioExecutors.find(executionId);
907 if (executorIt == fluxioDC.fluxioExecutors.end())
908 {
909 ARMARX_WARNING << "Execution with id '" << executionId << "' not found.";
910
911 l.unlock();
914 {executionId}),
916 "SkillManagerComponentPlugin",
917 __FUNCTION__,
918 __LINE__)};
919 }
920 l.unlock();
921
922 return {executorIt->second->getStatusUpdate().value()};
923 }
924
925 /**
926 * @brief converts parametersType or resultType from skilldescription to fluxio parameters
927 * @param ret the map to insert the fluxio parameters into
928 * @param ptr the aron object ptr to convert (this is either skillDescription.parametersType or skillDescription.resultType)
929 * @param isInput value for the isInput field of the fluxio parameter(s)
930 */
931 void
932 SkillManagerComponentPlugin::convertAronObjectPtrToFluxioParameters(
933 std::map<std::string, skills::FluxioParameter>& ret,
934 const aron::type::ObjectPtr& ptr,
935 bool isInput,
936 const boost::uuids::uuid& skillId)
937 {
938 for (const auto& [name, typeInfo] : ptr->getMemberTypes())
939 {
941
942 const auto& seed = name + (isInput ? "isInput" : "isOutput");
943 p.id = boost::uuids::to_string(createUuidWithString(seed, skillId));
944 p.name = name;
945 // Full type name may contain some extra info like dimensions etc.
946 p.description = typeInfo->getFullName();
947 p.type = typeInfo;
948 p.typeIdentificator = {boost::uuids::to_string(skillId), p.id};
949 p.required = (typeInfo->getMaybe() == 0);
950 p.isInput = isInput;
951
952 ret[p.id] = p;
953 std::unique_lock l(fluxioDC.typesMutex);
954 fluxioDC.types[boost::uuids::to_string(skillId)]->addMemberType(p.id, typeInfo);
955
956 l.unlock();
957 }
958 }
959
960 boost::uuids::uuid
961 SkillManagerComponentPlugin::createUuidWithString(
962 const std::string& str,
963 std::optional<const boost::uuids::uuid> namespaceUUID)
964 {
965 boost::uuids::uuid nameS;
966 if (namespaceUUID.has_value())
967 {
968 nameS = namespaceUUID.value();
969 }
970 else
971 {
972 // use default null uuid
973 nameS = boost::uuids::nil_uuid();
974 }
975
976 // initialise the name generator with the starting UUID
977 boost::uuids::name_generator generator(nameS);
978
979 // generate a new UUID, seeded with the given string
980 return generator(str);
981 }
982
983 /**
984 * @brief Converts the old skill structure to the new FluxioSkill structure.
985 * @param skillId The id of the skill.
986 * @param skillDescription The description of the skill.
987 */
988 void
989 SkillManagerComponentPlugin::descriptionIdToFluxioSkill(
990 skills::SkillID skillId,
991 skills::SkillDescription skillDescription)
992 {
993 {
994 // generate skill id (idempotent)
995 const auto& providerId = createUuidWithString(skillId.providerId->providerName);
996 const auto& id = createUuidWithString(skillId.skillName, providerId);
997
998 std::unique_lock skillsLock(fluxioDC.skillsMutex);
999 auto skillsEntry = fluxioDC.skills.find(boost::uuids::to_string(id));
1000 if (skillsEntry != fluxioDC.skills.end())
1001 {
1002 skillsLock.unlock();
1003 return;
1004 }
1005
1006 skills::FluxioSkill s;
1007
1008 s.id = boost::uuids::to_string(id);
1009 s.name = skillId.skillName;
1010 s.description = skillDescription.description;
1011 s.timeout = skillDescription.timeout;
1012 s.lastChanged = "";
1013 s.executable = false;
1014 s.native = true;
1015
1016 std::unique_lock providersLock(fluxioDC.providersMutex);
1017 const auto& providersEntry =
1018 fluxioDC.providers.find(boost::uuids::to_string(providerId));
1019 if (providersEntry != fluxioDC.providers.end())
1020 {
1021 s.skillProviderPtr = std::experimental::make_observer(&(providersEntry->second));
1022 providersLock.unlock();
1023 }
1024 else
1025 {
1026 providersLock.unlock();
1027 const auto& res = addFluxioProvider(skillId.providerId->providerName);
1028 if (!res.isSuccess())
1029 {
1030 ARMARX_WARNING << "Failed to add provider with name '"
1031 << skillId.providerId->providerName << "'. Skill: '" << s.name
1032 << " with id: '" << s.id << "' will not be added.";
1033 skillsLock.unlock();
1034 return;
1035 }
1036 const auto p = res.getResult(); // TODO: nullptr check
1037 s.skillProviderPtr = p;
1038 }
1039 std::unique_lock typesLock(fluxioDC.typesMutex);
1040 const auto& eventTypeIt = fluxioDC.types.find("BaseTypesNamespace");
1041 if (eventTypeIt == fluxioDC.types.end())
1042 {
1043 ARMARX_WARNING << "Event type not found. Skill: '" << s.name << " with id: '"
1044 << s.id << "' will not be added.";
1045 typesLock.unlock();
1046 skillsLock.unlock();
1047 return;
1048 }
1049 typesLock.unlock();
1050 const auto& eventType = eventTypeIt->second->getMemberType("Event");
1051 const skills::FluxioTypeIdentificator& eventTypeIdent = {
1052 .skillId = "BaseTypesNamespace", .parameterId = "Event"};
1053 // add default control flow parameters
1054 skills::FluxioParameter start;
1055 start.name = "Start";
1056 start.type = eventType;
1057 start.typeIdentificator = eventTypeIdent;
1058 const auto& startIdStr = start.name + start.type->getFullName() + "isInput";
1059 start.id = boost::uuids::to_string(createUuidWithString(startIdStr, id));
1060 start.description = "Start the skill";
1061 start.required = true;
1062 start.isInput = true;
1063
1064 skills::FluxioParameter succeeded;
1065 succeeded.name = "Succeeded";
1066 succeeded.type = eventType;
1067 succeeded.typeIdentificator = eventTypeIdent;
1068 const auto& successIdStr = succeeded.name + succeeded.type->getFullName() + "isOutput";
1069 succeeded.id = boost::uuids::to_string(createUuidWithString(successIdStr, id));
1070 succeeded.description = "Skill has been executed successfully";
1071 succeeded.required = false;
1072 succeeded.isInput = false;
1073
1074 skills::FluxioParameter failed;
1075 failed.name = "Failed";
1076 failed.type = eventType;
1077 failed.typeIdentificator = eventTypeIdent;
1078 const auto& failedIdStr = failed.name + failed.type->getShortName() + "isOutput";
1079 failed.id = boost::uuids::to_string(createUuidWithString(failedIdStr, id));
1080 failed.description = "Skill has failed";
1081 failed.required = false;
1082 failed.isInput = false;
1083
1084 skills::FluxioParameter aborted;
1085 aborted.name = "Aborted";
1086 aborted.type = eventType;
1087 aborted.typeIdentificator = eventTypeIdent;
1088 const auto& abortedIdStr = aborted.name + aborted.type->getShortName() + "isOutput";
1089 aborted.id = boost::uuids::to_string(createUuidWithString(abortedIdStr, id));
1090 aborted.description = "Skill has been aborted";
1091 aborted.required = false;
1092 aborted.isInput = false;
1093
1094 s.parameters[start.id] = start;
1095 s.parameters[succeeded.id] = succeeded;
1096 s.parameters[failed.id] = failed;
1097 s.parameters[aborted.id] = aborted;
1098
1099 const auto& parameters = std::make_shared<aron::type::Object>();
1100 typesLock.lock();
1101 if (fluxioDC.types.find(s.id) == fluxioDC.types.end())
1102 {
1103 fluxioDC.types[s.id] = parameters;
1104 }
1105 typesLock.unlock();
1106
1107 if (skillDescription.parametersType != nullptr)
1108 {
1109 // input parameters
1110 convertAronObjectPtrToFluxioParameters(
1111 s.parameters, skillDescription.parametersType, true, id);
1112 }
1113
1114 if (skillDescription.resultType != nullptr)
1115 {
1116 // output parameters
1117 convertAronObjectPtrToFluxioParameters(
1118 s.parameters, skillDescription.resultType, false, id);
1119 }
1120
1121 if (skillDescription.rootProfileDefaults != nullptr)
1122 {
1123 auto rootProfilePtr = std::experimental::make_observer(&fluxioDC.profiles["root"]);
1124
1125 for (const auto& [k, v] : skillDescription.rootProfileDefaults->getElements())
1126 {
1127 if (v == nullptr)
1128 {
1129 // nullptr values are not allowed (will lead to problems down the line)
1130 continue;
1131 }
1132
1133 skills::FluxioValue val;
1134 val.profilePtr = rootProfilePtr;
1135 val.content = v;
1136
1137 for (const auto& [pId, param] : s.parameters)
1138 {
1139 if (param.name == k && param.isInput)
1140 {
1141 s.parameters[pId].values.push_back(val);
1142 break;
1143 }
1144 }
1145 }
1146 }
1147 else
1148 {
1149 ARMARX_INFO << "No root profile defaults found for skill '" << s.name << "'.";
1150 }
1151
1152 fluxioDC.skills.emplace(s.id, std::move(s));
1153 skillsLock.unlock();
1154 }
1155 }
1156
1157 skills::Result<std::vector<std::experimental::observer_ptr<const skills::FluxioSkill>>,
1158 skills::error::FluxioException>
1160 {
1161 return {convertMapValuesToObserverVector(fluxioDC.skills)};
1162 }
1163
1167 {
1168 std::shared_lock l(fluxioDC.skillsMutex);
1169 const auto& skillsEntry = fluxioDC.skills.find(id);
1170 if (skillsEntry == fluxioDC.skills.end())
1171 {
1172 ARMARX_WARNING << "Skill with id '" << id << "' not found.";
1173 l.unlock();
1177 "SkillManagerComponentPlugin",
1178 __FUNCTION__,
1179 __LINE__)};
1180 }
1181
1182 l.unlock();
1183 return {std::experimental::make_observer(&skillsEntry->second)};
1184 }
1185
1186 // TODO: Outsource to a helper function or a separate class
1187 std::optional<std::vector<std::experimental::observer_ptr<const skills::FluxioSkill>>>
1189 const std::string& userId,
1190 const bool dryRun)
1191 {
1192 std::shared_lock l(fluxioDC.skillsMutex);
1193 const auto& skillIt = fluxioDC.skills.find(skillId);
1194 if (skillIt == fluxioDC.skills.end())
1195 {
1196 ARMARX_WARNING << "Skill with id '" << skillId << "' not found.";
1197 l.unlock();
1198 return std::nullopt;
1199 }
1200 l.unlock();
1201
1202 if (skillIt->second.native)
1203 {
1204 ARMARX_WARNING << "Skill with id '" << skillId
1205 << "' is a native skill and cannot be deleted.";
1206 return std::nullopt;
1207 }
1208
1209 const auto& skillMutexResult = getSkillMutex(skillId, userId);
1210
1211 if (!skillMutexResult.isSuccess())
1212 {
1213 ARMARX_WARNING << "User '" << userId << "' needs to acquire the mutex for skill '"
1214 << skillId << "' in order to delete it.";
1215 return std::nullopt;
1216 }
1217
1218 if (!skillMutexResult.getResult())
1219 {
1220 ARMARX_WARNING << "User '" << userId << "' needs to acquire the mutex for skill '"
1221 << skillId << "' in order to delete it.";
1222 return std::nullopt;
1223 }
1224
1225 std::vector<std::experimental::observer_ptr<skills::FluxioSkill>> affectedSkills;
1226 std::vector<std::experimental::observer_ptr<const skills::FluxioSkill>> ret;
1227 l.lock();
1228 for (auto& [id, s] : fluxioDC.skills)
1229 {
1230 if (s.native || s.id == skillId)
1231 {
1232 continue; // ignore native skills and the to-delete skill
1233 }
1234
1235 for (const auto& [nodeId, node] : s.nodes)
1236 {
1237 if (node == nullptr)
1238 {
1239 ARMARX_WARNING << "Unexpected nullptr!";
1240 continue;
1241 }
1242
1243 if (node->nodeType == skills::FluxioNodeType::SUBSKILL)
1244 {
1245 // cast to the right node type
1246 const auto& subSkillNodeptr =
1247 dynamic_cast<skills::FluxioSubSkillNode*>(node.get());
1248 if (subSkillNodeptr == nullptr)
1249 {
1250 ARMARX_WARNING << "Error while casting node to FluxioSubSkillNode.";
1251 continue;
1252 }
1253
1254 if (subSkillNodeptr->skillPtr != nullptr &&
1255 subSkillNodeptr->skillPtr->id == skillId)
1256 {
1257 affectedSkills.emplace_back(std::experimental::make_observer(&s));
1258 ret.emplace_back(std::experimental::make_observer(&s));
1259 break;
1260 }
1261 }
1262 }
1263 }
1264 l.unlock();
1265
1266 if (dryRun)
1267 {
1268 return ret;
1269 }
1270
1271 // get mutex of all affected skills
1272 bool mutexesAquired = true;
1273 for (const auto& affectedSkill : affectedSkills)
1274 {
1275 const auto& affectedMutexResult = getSkillMutex(affectedSkill->id, userId);
1276
1277 if (!affectedMutexResult.isSuccess() || !affectedMutexResult.getResult())
1278 {
1279 mutexesAquired = false;
1280 ARMARX_WARNING << "Someone else is editing the skill '" << affectedSkill->name
1281 << "(" << affectedSkill->id << ")' right now.";
1282 }
1283 }
1284
1285 if (mutexesAquired)
1286 {
1287 ARMARX_WARNING << "Updating affected skills.";
1288 for (const auto& affectedSkill : affectedSkills)
1289 {
1290 affectedSkill->removeSubSkillNodesAndEdges(skillId);
1291 }
1292
1293 ARMARX_WARNING << "Deleting skill '" << skillIt->second.name << "' (" << skillId
1294 << ").";
1295 std::unique_lock skillsLock(fluxioDC.skillsMutex);
1296 fluxioDC.skills.erase(skillId);
1297 skillsLock.unlock();
1298 }
1299 else
1300 {
1302 << "Not all Mutexes for affected skills could be aquired. Aborting deletion.";
1303 }
1304
1305 //release mutexes
1306 for (const auto& affectedSkill : affectedSkills)
1307 {
1308 deleteSkillMutex(affectedSkill->id, userId);
1309 }
1310
1311 if (mutexesAquired)
1312 {
1313 return ret;
1314 }
1315 return std::nullopt;
1316 }
1317
1321 const std::string& parameterId,
1322 const std::string& userId,
1323 bool dryRun)
1324 {
1325 return updateFluxioParameter(skillId, parameterId, userId, dryRun, true);
1326 }
1327
1331 const std::string& skillId,
1332 const skills::manager::dto::FluxioParameter& parameter,
1333 const std::string& userId,
1334 bool dryRun)
1335 {
1336 const auto& parameterId = parameter.id;
1337 return updateFluxioParameter(skillId, parameterId, userId, dryRun, false, parameter);
1338 }
1339
1342 SkillManagerComponentPlugin::updateFluxioParameter(
1343 const std::string& skillId,
1344 const std::string& parameterId,
1345 const std::string& userId,
1346 bool dryRun,
1347 bool deleteParameter,
1348 const std::optional<skills::manager::dto::FluxioParameter>& parameterDT)
1349 {
1350 if (!deleteParameter && parameterDT == std::nullopt)
1351 {
1352 ARMARX_WARNING << "Required parameter data transfer object not provided.";
1355 {"parameter data transfer object"}),
1357 "SkillManagerComponentPlugin",
1358 __FUNCTION__,
1359 __LINE__)};
1360 }
1361
1362 // check if skill exists
1363 const auto& res = getSkill(skillId);
1364 if (!res.isSuccess())
1365 {
1368 {skillId}),
1370 "SkillManagerComponentPlugin",
1371 __FUNCTION__,
1372 __LINE__)};
1373 }
1374 const auto& skill = res.getResult();
1375
1376 // check if parameter exists
1377 const auto& parameterIt = skill->parameters.find(parameterId);
1378 if (parameterIt == skill->parameters.end())
1379 {
1382 {parameterId}),
1384 "SkillManagerComponentPlugin",
1385 __FUNCTION__,
1386 __LINE__)};
1387 }
1388 const auto& parameterOld = parameterIt->second;
1389
1390 // check if user has mutex
1391 const auto& mutexResult = getSkillMutex(skillId, userId);
1392 if (!mutexResult.isSuccess())
1393 {
1394 return {mutexResult.getError()};
1395 }
1396
1397 // find affected skills
1398 std::vector<std::experimental::observer_ptr<skills::FluxioSkill>> affectedSkills;
1399 std::vector<std::experimental::observer_ptr<const skills::FluxioSkill>> ret;
1400
1401 // skip this part, if the parameter is updated and neither type nor isInput has changed
1402 if (deleteParameter ||
1403 parameterOld.typeIdentificator.toManagerIce() != parameterDT.value().type ||
1404 parameterOld.isInput != parameterDT.value().isInput)
1405 {
1406 std::shared_lock l(fluxioDC.skillsMutex);
1407 for (auto& [id, s] : fluxioDC.skills)
1408 {
1409 if (s.native || s.id == skillId)
1410 {
1411 continue; // ignore native skills and the skill with the parameter itself
1412 }
1413
1414 for (const auto& e : s.edges)
1415 {
1416 if ((e.fromParameterPtr != nullptr && e.fromParameterPtr->id == parameterId) ||
1417 (e.toParameterPtr != nullptr && e.toParameterPtr->id == parameterId))
1418 {
1419 affectedSkills.emplace_back(std::experimental::make_observer(&s));
1420 ret.emplace_back(std::experimental::make_observer(&s));
1421 break;
1422 }
1423 }
1424 }
1425 l.unlock();
1426 }
1427
1428 if (dryRun)
1429 {
1430 return {ret};
1431 }
1432
1433 // get mutex of all affected skills
1434 bool mutexesAquired = true;
1435 for (const auto& affectedSkill : affectedSkills)
1436 {
1437 const auto& affectedMutexResult = getSkillMutex(affectedSkill->id, userId);
1438
1439 if (!affectedMutexResult.isSuccess() || !affectedMutexResult.getResult())
1440 {
1441 mutexesAquired = false;
1442 ARMARX_WARNING << "Someone else is editing the skill '" << affectedSkill->name
1443 << "(" << affectedSkill->id << ")' right now.";
1444 }
1445 }
1446
1447 // update or delete parameter
1448 if (mutexesAquired)
1449 {
1450 ARMARX_WARNING << "Updating affected skills.";
1451 for (const auto& affectedSkill : affectedSkills)
1452 {
1453 affectedSkill->removeEdgesConnectedToParameter(parameterId);
1454 }
1455
1456 if (deleteParameter)
1457 {
1458 ARMARX_WARNING << "Deleting skill parameter'" << parameterOld.name << "' ("
1459 << parameterId << ").";
1460 std::unique_lock skillsLock(fluxioDC.skillsMutex);
1461 fluxioDC.skills[skillId].deleteParameter(parameterId);
1462 skillsLock.unlock();
1463 }
1464 else
1465 {
1466 ARMARX_WARNING << "Updating skill parameter'" << parameterOld.name << "' ("
1467 << parameterId << ").";
1468 std::scoped_lock l(fluxioDC.skillsMutex,
1469 fluxioDC.profilesMutex,
1470 fluxioDC.providersMutex,
1471 fluxioDC.typesMutex);
1472 auto& profilesMap = fluxioDC.profiles;
1473 auto& typesMap = fluxioDC.types;
1474 fluxioDC.skills[skillId].removeParameterNodesAndEdges(parameterId, true);
1475 fluxioDC.skills[skillId].parameters[parameterId].updateFromIce(
1476 *parameterDT, profilesMap, typesMap);
1477 }
1478 }
1479 else
1480 {
1481 ARMARX_WARNING << "Not all Mutexes for affected skills could be aquired. Aborting "
1482 << (deleteParameter ? "deletion" : "update") << " of parameter.";
1483 }
1484
1485 //release mutexes
1486 for (const auto& affectedSkill : affectedSkills)
1487 {
1488 deleteSkillMutex(affectedSkill->id, userId);
1489 }
1490
1491 if (mutexesAquired)
1492 {
1493 return ret;
1494 }
1495
1498 {userId}),
1500 "SkillManagerComponentPlugin",
1501 __FUNCTION__,
1502 __LINE__)};
1503 }
1504
1505 skills::Result<bool, skills::error::FluxioException>
1507 const std::string& userId)
1508 {
1509 return setEditFluxioSkillMutex(true, userId, skillId);
1510 }
1511
1512 void
1514 const std::string& userId)
1515 {
1516 setEditFluxioSkillMutex(false, userId,
1517 skillId); // ignore return value for now
1518 }
1519
1523 {
1524 std::shared_lock l(fluxioDC.profilesMutex);
1525 const auto& ret = convertMapValuesToObserverVector(fluxioDC.profiles);
1526 l.unlock();
1527 return {ret};
1528 }
1529
1532 {
1533 std::shared_lock l(fluxioDC.profilesMutex);
1534 const auto& profilesEntry = fluxioDC.profiles.find(id);
1535
1536 if (profilesEntry == fluxioDC.profiles.end())
1537 {
1538 l.unlock();
1542 "SkillManagerComponentPlugin",
1543 __FUNCTION__,
1544 __LINE__)};
1545 }
1546
1547 l.unlock();
1548 return {profilesEntry->second};
1549 }
1550
1553 {
1554 if (profile.parentPtr == nullptr)
1555 {
1556 ARMARX_WARNING << "Profile with name '" << profile.name << "' has no parent.";
1559 {profile.name}),
1561 "SkillManagerComponentPlugin",
1562 __FUNCTION__,
1563 __LINE__)};
1564 }
1565
1566 const auto& parentId = profile.parentPtr->id;
1567 const auto& nameLowerCase = simox::alg::to_lower(profile.name);
1568
1569 std::unique_lock l(fluxioDC.profilesMutex);
1570 auto it = std::find_if(
1571 fluxioDC.profiles.begin(),
1572 fluxioDC.profiles.end(),
1573 [&parentId, &nameLowerCase](const std::pair<std::string, skills::FluxioProfile>& p)
1574 {
1575 return p.second.parentPtr != nullptr && p.second.parentPtr->id == parentId &&
1576 simox::alg::to_lower(p.second.name) == nameLowerCase;
1577 });
1578
1579 if (nameLowerCase == "root" || it != fluxioDC.profiles.end())
1580 {
1581 ARMARX_WARNING << "Profile with name '" << profile.name << "' already exists.";
1582 l.unlock();
1585 {profile.name}),
1587 "SkillManagerComponentPlugin",
1588 __FUNCTION__,
1589 __LINE__)};
1590 }
1591
1592 std::string id = profile.id;
1593
1594 bool validId = false;
1595 try
1596 {
1597 const auto& result = boost::uuids::string_generator()(id);
1598 validId = result.version() != boost::uuids::uuid::version_unknown;
1599 }
1600 catch (...)
1601 {
1602 validId = false;
1603 }
1604
1605 if (!validId)
1606 {
1607 id = IceUtil::generateUUID();
1608 }
1609 else if (fluxioDC.profiles.find(profile.id) != fluxioDC.profiles.end())
1610 {
1611 ARMARX_INFO << "The id '" << profile.id
1612 << "' is already taken. A new uuid will be generated.";
1613 id = IceUtil::generateUUID();
1614 }
1615 else
1616 {
1617 id = profile.id;
1618 }
1619
1620 fluxioDC.profiles[id] = profile; // a copy is created when the profile is added to the map
1621 fluxioDC.profiles[id].id = id; // this copy can then be modified
1622 const auto& ret = fluxioDC.profiles[id];
1623
1624 l.unlock();
1625
1626 return {ret};
1627 }
1628
1629 void
1631 {
1632 auto oldProfile = getProfile(profile.id);
1633
1634 if (oldProfile.isSuccess() && oldProfile.getResult().id == profile.id)
1635 {
1636 std::unique_lock l(fluxioDC.profilesMutex);
1637 fluxioDC.profiles[profile.id].name = profile.name;
1638 fluxioDC.profiles[profile.id].description = profile.description;
1639 fluxioDC.profiles[profile.id].parentPtr = profile.parentPtr;
1640 l.unlock();
1641 }
1642 else
1643 {
1644 //TODO: Do we throw the error here?
1645 }
1646 }
1647
1651 {
1652 const std::string& providerId = boost::uuids::to_string(createUuidWithString(name));
1653
1654 std::unique_lock l(fluxioDC.providersMutex);
1655 if (fluxioDC.providers.find(providerId) != fluxioDC.providers.end())
1656 {
1657 ARMARX_WARNING << "Provider with name '" << name << "' already exists.";
1658 l.unlock();
1659 return {std::experimental::make_observer(&fluxioDC.providers[providerId])};
1660 }
1661
1663 p.id = providerId;
1664 p.name = name;
1665
1666 fluxioDC.providers[p.id] = p;
1667 const auto& ret = std::experimental::make_observer(&fluxioDC.providers[p.id]);
1668 l.unlock();
1669
1670 return {ret};
1671 }
1672
1676 {
1677 for (const auto& [providerID, providerPrx] : skillProviderMap)
1678 {
1679 const auto& id = boost::uuids::to_string(createUuidWithString(providerID.providerName));
1680
1681 std::shared_lock l(fluxioDC.providersMutex);
1682 const auto& providersEntry = fluxioDC.providers.find(id);
1683
1684 if (providersEntry != fluxioDC.providers.end())
1685 {
1686 l.unlock();
1687 continue; // provider already exists
1688 }
1689
1690 l.unlock();
1691 addFluxioProvider(providerID.providerName);
1692 }
1693
1694 return {convertMapValuesToObserverVector(fluxioDC.providers)};
1695 }
1696
1699 {
1701
1702 std::shared_lock l(fluxioDC.providersMutex);
1703 const auto& providersEntry = fluxioDC.providers.find(id);
1704
1705 if (providersEntry != fluxioDC.providers.end())
1706 {
1707 l.unlock();
1708 return {providersEntry->second};
1709 }
1710
1711 l.unlock();
1715 "SkillManagerComponentPlugin",
1716 __FUNCTION__,
1717 __LINE__)};
1718 }
1719
1723 {
1725
1726 std::shared_lock l(fluxioDC.providersMutex);
1727 const auto& providersEntry = fluxioDC.providers.find(id);
1728
1729 if (providersEntry == fluxioDC.providers.end())
1730 {
1731 l.unlock();
1735 "SkillManagerComponentPlugin",
1736 __FUNCTION__,
1737 __LINE__)};
1738 }
1739
1740 l.unlock();
1741
1742 auto res = getSkillList();
1743 if (!res.isSuccess())
1744 {
1745 auto e = res.getError();
1746 e.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__);
1747 return {e};
1748 }
1749
1750 auto allSkills = res.getResult();
1751
1752 allSkills.erase(std::remove_if(allSkills.begin(),
1753 allSkills.end(),
1754 [id](const auto& skillPtr)
1755 { return skillPtr->skillProviderPtr->id != id; }),
1756 allSkills.end());
1757
1758 return {allSkills};
1759 }
1760
1764 const std::string& providerId,
1765 skills::FluxioSkill&& skill)
1766 {
1767 std::shared_lock l(fluxioDC.providersMutex);
1768 const auto& providerIt = fluxioDC.providers.find(providerId);
1769 if (providerIt == fluxioDC.providers.end())
1770 {
1771 ARMARX_WARNING << "Provider with id '" << providerId << "' not found.";
1772 l.unlock();
1775 {providerId}),
1777 "SkillManagerComponentPlugin",
1778 __FUNCTION__,
1779 __LINE__)};
1780 }
1781 l.unlock();
1782
1783 if (skill.id.empty())
1784 {
1785 // its a new skill, its a new world, ...
1786 skill.id = IceUtil::generateUUID();
1787 }
1788 else
1789 {
1790 // TODO(me): check if a skill with the same id already exists
1791 }
1792
1793 boost::uuids::uuid uuid;
1794 try
1795 {
1796 boost::uuids::string_generator gen;
1797 uuid = gen(skill.id);
1798 }
1799 catch (std::runtime_error& e)
1800 {
1801 ARMARX_WARNING << "Could not convert skill id to uuid: " << e.what();
1802 skill.id = IceUtil::generateUUID(); // fallback
1803 }
1804
1805 std::unique_lock typesLock(fluxioDC.typesMutex);
1806 const auto& eventType = fluxioDC.types["BaseTypesNamespace"]->getMemberType("Event");
1807 typesLock.unlock();
1808 const skills::FluxioTypeIdentificator& eventTypeIdent = {.skillId = "BaseTypesNamespace",
1809 .parameterId = "Event"};
1810
1811 // add default control flow parameters
1813 start.name = "Start";
1814 start.id = boost::uuids::to_string(createUuidWithString(start.name, uuid));
1815 start.description = "Start the skill";
1816 start.type = eventType;
1817 start.typeIdentificator = eventTypeIdent;
1818 start.required = true;
1819 start.isInput = true;
1820
1821 skills::FluxioParameter succeeded;
1822 succeeded.name = "Succeeded";
1823 succeeded.id = boost::uuids::to_string(createUuidWithString(succeeded.name, uuid));
1824 succeeded.description = "Skill has been executed successfully";
1825 succeeded.type = eventType;
1826 succeeded.typeIdentificator = eventTypeIdent;
1827 succeeded.required = false;
1828 succeeded.isInput = false;
1829
1831 failed.name = "Failed";
1832 failed.id = boost::uuids::to_string(createUuidWithString(failed.name, uuid));
1833 failed.description = "Skill has failed";
1834 failed.type = eventType;
1835 failed.typeIdentificator = eventTypeIdent;
1836 failed.required = false;
1837 failed.isInput = false;
1838
1840 aborted.name = "Aborted";
1841 aborted.id = boost::uuids::to_string(createUuidWithString(aborted.name, uuid));
1842 aborted.description = "Skill has been aborted";
1843 aborted.type = eventType;
1844 aborted.typeIdentificator = eventTypeIdent;
1845 aborted.required = false;
1846 aborted.isInput = false;
1847
1848 skill.parameters[start.id] = start;
1849 skill.parameters[succeeded.id] = succeeded;
1850 skill.parameters[failed.id] = failed;
1851 skill.parameters[aborted.id] = aborted;
1852
1853 skill.lastChanged = armarx::DateTime::Now().toDateTimeString();
1854
1855 // store in data collector
1856 const auto skillId = skill.id;
1857 std::unique_lock skillsLock(fluxioDC.skillsMutex);
1858 fluxioDC.skills.emplace(skillId, std::move(skill));
1859 const auto& ret = std::experimental::make_observer(&(fluxioDC.skills[skillId]));
1860 skillsLock.unlock();
1861
1862 // set mutex
1863 setEditFluxioSkillMutex(true, userId, skill.id);
1864 return {ret};
1865 }
1866
1867 bool
1868 SkillManagerComponentPlugin::setEditFluxioSkillMutex(bool aquireMutex,
1869 const std::string& userId,
1870 const std::string& skillId)
1871 {
1872 std::scoped_lock l(fluxioDC.skillMutexMapMutex);
1873 const auto& skillMutexIt = fluxioDC.skillMutexMap.find(skillId);
1874
1875 // aquire mutex
1876 if (aquireMutex)
1877 {
1878 if (skillMutexIt == fluxioDC.skillMutexMap.end())
1879 {
1880 // skill is free, set mutex
1881 fluxioDC.skillMutexMap[skillId] = std::make_tuple(userId, armarx::DateTime::Now());
1882 return true;
1883 }
1884
1885 // skill is already locked
1886 if (std::get<0>(fluxioDC.skillMutexMap[skillId]) == userId)
1887 {
1888 // user already holds the mutex -> reset timestamp / prolong mutex
1889 std::get<1>(fluxioDC.skillMutexMap[skillId]) = armarx::DateTime::Now();
1890 return true;
1891 }
1892
1893 // another user holds the mutex -> is it still valid ?
1894 armarx::Duration elapsedTime =
1895 armarx::DateTime::Now() - std::get<1>(fluxioDC.skillMutexMap[skillId]);
1896 if (elapsedTime.toMinutes() > fluxioDC.mutexTimeout)
1897 {
1898 // mutex invalid, requesting user gets the mutex instead
1899 fluxioDC.skillMutexMap[skillId] = std::make_tuple(userId, armarx::DateTime::Now());
1900 return true;
1901 }
1902
1903 // mutex could not be aquired
1906 {skillId}),
1908 "SkillManagerComponentPlugin",
1909 __FUNCTION__,
1910 __LINE__)
1911 .toManagerIce();
1912 }
1913
1914 // remove mutex
1915 if (skillMutexIt == fluxioDC.skillMutexMap.end())
1916 {
1917 // skill is already free
1918 return true;
1919 }
1920
1921
1922 // check if the user holds the mutex, as only the user that holds the mutex
1923 // can release it
1924 if (std::get<0>(fluxioDC.skillMutexMap[skillId]) == userId)
1925 {
1926 fluxioDC.skillMutexMap.erase(skillId);
1927 return true;
1928 }
1929
1930 // mutex could not be removed
1933 {skillId}),
1935 "SkillManagerComponentPlugin",
1936 __FUNCTION__,
1937 __LINE__)
1938 .toManagerIce();
1939 }
1940
1943 {
1944 auto ret = std::make_shared<aron::type::Object>();
1945 const skills::Result<
1946 std::vector<std::experimental::observer_ptr<const skills::FluxioSkill>>,
1948
1949 if (!res.isSuccess())
1950 {
1951 ARMARX_WARNING << "Failed to get skill list.";
1952 return nullptr;
1953 }
1954
1955 std::shared_lock l(fluxioDC.typesMutex);
1956 for (const auto& [skillId, skillTypes] : fluxioDC.types)
1957 {
1958 if (skillTypes == nullptr)
1959 {
1960 ARMARX_WARNING << "SkillTypes with id '" << skillId << "' not found.";
1961 continue;
1962 }
1963 ret->addMemberType(skillId, skillTypes);
1964 }
1965 l.unlock();
1966 ret->setObjectName("SkillTypes");
1967 return ret;
1968 }
1969
1970} // namespace armarx::plugins
std::string str(const T &t)
static DateTime Now()
Definition DateTime.cpp:51
static nlohmann::json ConvertToNlohmannJSON(const data::VariantPtr &)
std::string toDateTimeString() const
Definition DateTime.cpp:75
Represents a duration.
Definition Duration.h:17
std::int64_t toMinutes() const
Returns the amount of minutes.
Definition Duration.cpp:108
std::map< skills::SkillID, skills::SkillDescription > getSkillDescriptions()
bool abortSkill(const skills::SkillExecutionID &id)
skills::SkillStatusUpdate executeSkill(const skills::SkillExecutionRequest &req)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioSkill > >, skills::error::FluxioException > getSkillList()
skills::SkillExecutionID executeSkillAsync(const skills::SkillExecutionRequest &req)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioProfile > >, skills::error::FluxioException > getProfileList()
void updateProfile(const skills::FluxioProfile &profile)
skills::ProviderID getFirstProviderNameThatHasSkill(const skills::SkillID &skillid)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioSkill > >, skills::error::FluxioException > deleteSkillParameter(const std::string &skillId, const std::string &parameterId, const std::string &userId, bool dryRun)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioSkill > >, skills::error::FluxioException > updateSkillParameter(const std::string &skillId, const skills::manager::dto::FluxioParameter &parameter, const std::string &userId, bool dryRun)
skills::Result< std::experimental::observer_ptr< skills::FluxioExecutor >, skills::error::FluxioException > executeFluxioSkill(const std::string &skillId, const std::string &profileId, const std::string &executorName, armarx::aron::data::DictPtr parameters=nullptr)
skills::Result< std::experimental::observer_ptr< const skills::FluxioProvider >, skills::error::FluxioException > addFluxioProvider(const std::string &name)
void deleteSkillMutex(const std::string &skillId, const std::string &userId)
skills::Result< std::experimental::observer_ptr< const skills::FluxioSkill >, skills::error::FluxioException > addSkillToProvider(const std::string &userId, const std::string &providerId, skills::FluxioSkill &&skill)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioProvider > >, skills::error::FluxioException > getProviderList()
void postCreatePropertyDefinitions(PropertyDefinitionsPtr &properties) override
skills::Result< std::experimental::observer_ptr< const skills::FluxioSkill >, skills::error::FluxioException > getSkill(const std::string &id)
bool updateSkillParameters(const skills::SkillExecutionID &id, const aron::data::DictPtr &data)
std::optional< skills::SkillStatusUpdate > getSkillExecutionStatus(const skills::SkillExecutionID &id)
skills::Result< std::vector< skills::FluxioSkillStatusUpdate >, skills::error::FluxioException > getFluxioSkillExecutionStatus(const std::string &executionId)
skills::Result< skills::FluxioProvider, skills::error::FluxioException > getProvider(const std::string &id)
bool abortSkillAsync(const skills::SkillExecutionID &id)
skills::Result< skills::FluxioProfile, skills::error::FluxioException > getProfile(const std::string &id)
std::map< skills::SkillExecutionID, skills::SkillStatusUpdate > getSkillExecutionStatuses()
skills::Result< bool, skills::error::FluxioException > getSkillMutex(const std::string &skillId, const std::string &userId)
std::optional< skills::SkillDescription > getSkillDescription(const skills::SkillID &id)
std::optional< std::vector< std::experimental::observer_ptr< const skills::FluxioSkill > > > deleteSkill(const std::string &skillId, const std::string &userId, bool dryRun)
skills::Result< skills::FluxioProfile, skills::error::FluxioException > createProfile(const skills::FluxioProfile &profile)
skills::Result< std::vector< std::experimental::observer_ptr< const skills::FluxioSkill > >, skills::error::FluxioException > getSkillsOfProvider(const std::string &id)
std::string toString() const
provider::dti::SkillProviderInterfacePrx providerInterface
std::map< SkillID, SkillDescription > providedSkills
std::string toString() const
Definition SkillID.cpp:68
std::optional< ProviderID > providerId
Definition SkillID.h:40
bool isFullySpecified() const
Definition SkillID.cpp:78
provider::dto::SkillID toProviderIce() const
Definition SkillID.cpp:62
static SkillID FromIce(const manager::dto::SkillID &)
Definition SkillID.cpp:36
std::string skillName
Definition SkillID.h:41
A base class for skill exceptions.
static FluxioException create(const std::string &message, const FluxioExceptionType &type, const std::string &className, const char *function, int line)
A base class for skill exceptions.
Definition Exception.h:35
#define ARMARX_CHECK(expression)
Shortcut for ARMARX_CHECK_EXPRESSION.
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#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
std::shared_ptr< Object > ObjectPtr
Definition Object.h:36
double s(double t, double s0, double v0, double a0, double j)
Definition CtrlUtil.h:33
double v(double t, double v0, double a0, double j)
Definition CtrlUtil.h:39
This file is part of ArmarX.
std::string createErrorMessage(ErrorCode code, const std::vector< std::string > &args)
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
observer_ptr< _Tp > make_observer(_Tp *__p) noexcept
aron::type::VariantPtr type
FluxioTypeIdentificator typeIdentificator
std::experimental::observer_ptr< const FluxioProfile > parentPtr
static SkillDescription FromIce(const provider::dto::SkillDescription &i, const std::optional< ProviderID > &=std::nullopt)
skills::provider::dto::SkillExecutionID toProviderIce() const
static SkillExecutionID FromIce(const skills::manager::dto::SkillExecutionID &)
static SkillStatusUpdate FromIce(const provider::dto::SkillStatusUpdate &update, const std::optional< skills::ProviderID > &providerId=std::nullopt)