From f712c8512d086cd32f008f367e7403eeb92cb325 Mon Sep 17 00:00:00 2001 From: Fabian Peller <fabian.peller-konrad@kit.edu> Date: Tue, 17 Oct 2023 18:17:28 +0200 Subject: [PATCH] added utility to run subskills from within a skill so that automatically onStopRequested and onTimeoutReached are called. Skill Proxy has been moved to core package of skills. split example skills in seperate files remove getLatesNUpdates from skillsMemory interface as the same behavior can be reached through getSkillStatusUpdates(). Added methods to coreSegment. added skillExecutionRequest to memory when skill is asyncronolously called --- .../server/SkillsMemory/SkillsMemory.cpp | 31 ++- .../armem/server/SkillsMemory/SkillsMemory.h | 11 +- .../SkillProviderExample/CMakeLists.txt | 10 + .../skills/SkillProviderExample/Callback.cpp | 39 ++++ .../skills/SkillProviderExample/Callback.h | 41 ++++ .../skills/SkillProviderExample/Chaining.cpp | 39 ++++ .../skills/SkillProviderExample/Chaining.h | 40 ++++ .../SkillProviderExample/HelloWorld.cpp | 49 +++++ .../skills/SkillProviderExample/HelloWorld.h | 42 ++++ .../SkillProviderExample/Incomplete.cpp | 55 ++++++ .../skills/SkillProviderExample/Incomplete.h | 44 +++++ .../SkillProviderExample.cpp | 184 ------------------ .../SkillProviderExample.h | 69 +------ .../skills/SkillProviderExample/Timeout.cpp | 30 +++ .../skills/SkillProviderExample/Timeout.h | 42 ++++ .../SkillManagerMonitorWidgetController.cpp | 30 +-- .../interface/skills/SkillMemoryInterface.ice | 6 +- .../server/segment/SkillEventSegment.cpp | 55 ++++-- .../server/segment/SkillEventSegment.h | 5 +- .../libraries/skills/core/CMakeLists.txt | 2 + .../RobotAPI/libraries/skills/core/Skill.cpp | 131 +++++++++++-- source/RobotAPI/libraries/skills/core/Skill.h | 130 ++++++++----- .../libraries/skills/core/SkillExecutionID.h | 39 ++-- .../libraries/skills/core/SkillProxy.cpp | 118 +++++++++++ .../libraries/skills/core/SkillProxy.h | 63 ++++++ .../manager/SkillManagerComponentPlugin.cpp | 3 - .../libraries/skills/provider/CMakeLists.txt | 2 - .../skills/provider/PeriodicSkill.cpp | 24 +-- .../provider/SkillProviderComponentPlugin.cpp | 35 +++- .../libraries/skills/provider/SkillProxy.cpp | 72 ------- .../libraries/skills/provider/SkillProxy.h | 37 ---- .../skills/provider/SpecializedSkillProxy.cpp | 2 +- .../skills/provider/SpecializedSkillProxy.h | 6 +- .../detail/SkillImplementationWrapper.cpp | 12 +- 34 files changed, 985 insertions(+), 513 deletions(-) create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Callback.cpp create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Callback.h create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Chaining.cpp create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Chaining.h create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.h create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Incomplete.cpp create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Incomplete.h create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Timeout.cpp create mode 100644 source/RobotAPI/components/skills/SkillProviderExample/Timeout.h create mode 100644 source/RobotAPI/libraries/skills/core/SkillProxy.cpp create mode 100644 source/RobotAPI/libraries/skills/core/SkillProxy.h delete mode 100644 source/RobotAPI/libraries/skills/provider/SkillProxy.cpp delete mode 100644 source/RobotAPI/libraries/skills/provider/SkillProxy.h diff --git a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp index 02cbe58ce..d77d73715 100644 --- a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp +++ b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.cpp @@ -156,6 +156,16 @@ namespace armarx return SkillManagerComponentPluginUser::executeSkill(info, current); } + skills::manager::dto::SkillExecutionID + SkillsMemory::executeSkillAsync(const skills::manager::dto::SkillExecutionRequest& info, + const Ice::Current& current) + { + auto e = skills::SkillExecutionRequest::FromIce(info); + skillExecutionRequestCoreSegment.addSkillExecutionRequest(e); + + return SkillManagerComponentPluginUser::executeSkillAsync(info, current); + } + void SkillsMemory::updateStatusForSkill(const skills::provider::dto::SkillStatusUpdate& update, const skills::callback::dto::ProviderID& providerId, @@ -166,14 +176,27 @@ namespace armarx skillEventCoreSegment.addSkillUpdateEvent(u); } + IceUtil::Optional<skills::manager::dto::SkillStatusUpdate> + SkillsMemory::getSkillExecutionStatus(const skills::manager::dto::SkillExecutionID& executionId, + const Ice::Current& current) + { + auto eid = skills::SkillExecutionID::FromIce(executionId); + auto op = this->skillEventCoreSegment.getSkillStatusUpdate(eid); + if (op.has_value()) + { + return op->toManagerIce(); + } + return {}; + } + skills::manager::dto::SkillStatusUpdateMap - SkillsMemory::getLatestSkillExecutionStatuses(int n, const Ice::Current& current) + SkillsMemory::getSkillExecutionStatuses(const Ice::Current& current) { - auto m = skillEventCoreSegment.getLatestSkillEvents(n); skills::manager::dto::SkillStatusUpdateMap ret; - for (const auto& [k, v] : m) + auto updates = this->skillEventCoreSegment.getSkillStatusUpdates(); + for (const auto& [k, v] : updates) { - ret[k.toManagerIce()] = v.toManagerIce(); + ret.insert({k.toManagerIce(), v.toManagerIce()}); } return ret; } diff --git a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.h b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.h index 81109b0bd..ead9f1912 100644 --- a/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.h +++ b/source/RobotAPI/components/armem/server/SkillsMemory/SkillsMemory.h @@ -85,12 +85,21 @@ namespace armarx executeSkill(const skills::manager::dto::SkillExecutionRequest& info, const Ice::Current& current) override; + + skills::manager::dto::SkillExecutionID + executeSkillAsync(const skills::manager::dto::SkillExecutionRequest& info, + const Ice::Current& current) override; + void updateStatusForSkill(const skills::provider::dto::SkillStatusUpdate& update, const skills::callback::dto::ProviderID& id, const Ice::Current& current) override; + IceUtil::Optional<skills::manager::dto::SkillStatusUpdate> + getSkillExecutionStatus(const skills::manager::dto::SkillExecutionID& executionId, + const Ice::Current& current) override; + skills::manager::dto::SkillStatusUpdateMap - getLatestSkillExecutionStatuses(int n, const Ice::Current& current) override; + getSkillExecutionStatuses(const Ice::Current& current) override; // WritingInterface interface armem::data::CommitResult commit(const armem::data::Commit& commit, diff --git a/source/RobotAPI/components/skills/SkillProviderExample/CMakeLists.txt b/source/RobotAPI/components/skills/SkillProviderExample/CMakeLists.txt index afbb7ef2a..b13f663e2 100644 --- a/source/RobotAPI/components/skills/SkillProviderExample/CMakeLists.txt +++ b/source/RobotAPI/components/skills/SkillProviderExample/CMakeLists.txt @@ -13,10 +13,20 @@ set(COMPONENT_LIBS set(SOURCES SkillProviderExample.cpp + HelloWorld.cpp + Incomplete.cpp + Chaining.cpp + Callback.cpp + Timeout.cpp ) set(HEADERS SkillProviderExample.h + HelloWorld.h + Incomplete.h + Chaining.h + Callback.h + Timeout.h ) armarx_add_component("${SOURCES}" "${HEADERS}") diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Callback.cpp b/source/RobotAPI/components/skills/SkillProviderExample/Callback.cpp new file mode 100644 index 000000000..d13f3bb8d --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Callback.cpp @@ -0,0 +1,39 @@ + +#include "Callback.h" + +namespace armarx::skills::provider +{ + CallbackSkill::CallbackSkill() : SimpleSkill(GetSkillDescription()) + { + } + + SkillDescription + CallbackSkill::GetSkillDescription() + { + return SkillDescription{{"ShowMeCallbacks"}, + "This skill does shows callbacks", + {}, + armarx::core::time::Duration::MilliSeconds(1000), + nullptr}; + } + + Skill::MainResult + CallbackSkill::main(const MainInput& in) + { + ARMARX_IMPORTANT << "Logging three updates via the callback"; + auto up1 = std::make_shared<aron::data::Dict>(); + up1->addElement("updateInfo", std::make_shared<aron::data::String>("Update 1")); + + in.callback(skills::SkillStatus::Running, up1); + + auto up2 = std::make_shared<aron::data::Dict>(); + up2->addElement("updateInfo", std::make_shared<aron::data::String>("Update 2")); + in.callback(skills::SkillStatus::Running, up2); + + auto up3 = std::make_shared<aron::data::Dict>(); + up3->addElement("updateInfo", std::make_shared<aron::data::String>("Update 3")); + in.callback(skills::SkillStatus::Running, up3); + + return {TerminatedSkillStatus::Succeeded, nullptr}; + } +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Callback.h b/source/RobotAPI/components/skills/SkillProviderExample/Callback.h new file mode 100644 index 000000000..c29c2b6f3 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Callback.h @@ -0,0 +1,41 @@ + +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + + +// RobotAPI +#include <RobotAPI/libraries/skills/provider/SimpleSkill.h> + +namespace armarx::skills::provider +{ + class CallbackSkill : public SimpleSkill + { + public: + CallbackSkill(); + + static SkillDescription GetSkillDescription(); + + private: + Skill::MainResult main(const MainInput&) final; + }; +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Chaining.cpp b/source/RobotAPI/components/skills/SkillProviderExample/Chaining.cpp new file mode 100644 index 000000000..c40b511a6 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Chaining.cpp @@ -0,0 +1,39 @@ + + +#include "Chaining.h" + +namespace armarx::skills::provider +{ + + ChainingSkill::ChainingSkill() : SimpleSkill(GetSkillDescription()) + { + } + + SkillDescription + ChainingSkill::GetSkillDescription() + { + return SkillDescription{{"ChainingSkill"}, + "This skill calls the Timeout skill three times. The last " + "execution is aborted due to a timeout of this skill.", + {}, + armarx::core::time::Duration::MilliSeconds(5000), + nullptr}; + } + + Skill::MainResult + ChainingSkill::main(const MainInput& in) + { + SkillProxy prx(manager, skills::SkillID(*getSkillId().providerId, "Timeout")); + + ARMARX_INFO << "CALL PROXY FIRST TIME"; + callSubskill(prx); + ARMARX_INFO << "CALL PROXY SECOND TIME"; + callSubskill(prx); + ARMARX_INFO << "CALL PROXY THIRD TIME"; + callSubskill(prx); + + this->throwIfSkillShouldTerminate(); + + return {TerminatedSkillStatus::Succeeded, nullptr}; + } +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Chaining.h b/source/RobotAPI/components/skills/SkillProviderExample/Chaining.h new file mode 100644 index 000000000..991c4a5aa --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Chaining.h @@ -0,0 +1,40 @@ + +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +// RobotAPI +#include <RobotAPI/libraries/skills/provider/SimpleSkill.h> + +namespace armarx::skills::provider +{ + class ChainingSkill : public SimpleSkill + { + public: + ChainingSkill(); + + static SkillDescription GetSkillDescription(); + + private: + Skill::MainResult main(const MainInput&) final; + }; +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp new file mode 100644 index 000000000..ccfa7f31a --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.cpp @@ -0,0 +1,49 @@ + + +#include "HelloWorld.h" + +#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> +#include <RobotAPI/libraries/aron/core/type/variant/container/Object.h> +#include <RobotAPI/libraries/aron/core/type/variant/primitive/String.h> + +namespace armarx::skills::provider +{ + HelloWorldSkill::HelloWorldSkill() : + SimpleSpecializedSkill<skills::Example::HelloWorldAcceptedType>(GetSkillDescription()) + { + } + + SkillDescription + HelloWorldSkill::GetSkillDescription() + { + armarx::skills::Example::HelloWorldAcceptedType root_profile_params; + root_profile_params.some_float = 5; + root_profile_params.some_int = 42; + root_profile_params.some_text = "YOLO"; + root_profile_params.some_list_of_matrices.push_back(Eigen::Matrix3f::Zero()); + root_profile_params.some_matrix = Eigen::Matrix3f::Zero(); + + return SkillDescription{{"HelloWorld"}, + "This skill logs a message on ARMARX_IMPORTANT", + root_profile_params.toAron(), + armarx::core::time::Duration::MilliSeconds(1000), + armarx::skills::Example::HelloWorldAcceptedType::ToAronType()}; + } + + Skill::MainResult + HelloWorldSkill::main(const SpecializedMainInput& in) + { + ARMARX_IMPORTANT << "Hi, from the Hello World Skill.\n" + << "I received the following data: \n" + << aron::data::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON( + in.parameters.toAron()) + .dump(2) + << "\n" + << "Type fulfilled? " + << parameters->fullfillsType( + armarx::skills::Example::HelloWorldAcceptedType::ToAronType()) + << "\n" + << "(executed at: " << IceUtil::Time::now() << ")"; + return {TerminatedSkillStatus::Succeeded, nullptr}; + } +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.h b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.h new file mode 100644 index 000000000..840b058fb --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/HelloWorld.h @@ -0,0 +1,42 @@ + +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +// RobotAPI +#include <RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.aron.generated.h> +#include <RobotAPI/libraries/skills/provider/SimpleSpecializedSkill.h> + +namespace armarx::skills::provider +{ + // Skills: + class HelloWorldSkill : public SimpleSpecializedSkill<skills::Example::HelloWorldAcceptedType> + { + public: + HelloWorldSkill(); + + static SkillDescription GetSkillDescription(); + + private: + Skill::MainResult main(const SpecializedMainInput& in) final; + }; +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.cpp b/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.cpp new file mode 100644 index 000000000..ac9bf9af3 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.cpp @@ -0,0 +1,55 @@ + + +#include "Incomplete.h" + +#include <RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.aron.generated.h> + +#include "HelloWorld.h" + +namespace armarx::skills::provider +{ + + IncompleteSkill::IncompleteSkill() : SimpleSkill(GetSkillDescription()) + { + } + + SkillDescription + IncompleteSkill::GetSkillDescription() + { + auto d = HelloWorldSkill::GetSkillDescription(); + return SkillDescription{{"IncompleteSkill"}, + d.description, + {}, + d.timeout + armarx::core::time::Duration::Seconds(2), + d.parametersType}; + } + + Skill::PrepareResult + IncompleteSkill::prepare() + { + if (!first_prepared) + { + first_prepared = true; + + // set parameters after two seconds... + std::thread foo( + [&]() + { + auto d = HelloWorldSkill::GetSkillDescription(); + std::this_thread::sleep_for(std::chrono::milliseconds(2000)); + this->setParameters(d.rootProfileDefaults); + }); + foo.detach(); + } + + return {.status = ActiveOrTerminatedSkillStatus::Succeeded}; + } + + Skill::MainResult + IncompleteSkill::main(const MainInput& in) + { + auto s = HelloWorldSkill(); + s.setParameters(in.parameters); + return s.mainOfSkill(); + } +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.h b/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.h new file mode 100644 index 000000000..6cf7b6e73 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Incomplete.h @@ -0,0 +1,44 @@ + +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +// RobotAPI +#include <RobotAPI/libraries/skills/provider/SimpleSkill.h> + +namespace armarx::skills::provider +{ + + class IncompleteSkill : public SimpleSkill + { + public: + IncompleteSkill(); + + static SkillDescription GetSkillDescription(); + + private: + Skill::PrepareResult prepare() final; + Skill::MainResult main(const MainInput&) final; + + std::atomic_bool first_prepared = false; + }; +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.cpp b/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.cpp index 4e82f6d7e..4230249f7 100644 --- a/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.cpp +++ b/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.cpp @@ -1,192 +1,8 @@ - #include "SkillProviderExample.h" -#include <RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.aron.generated.h> -#include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> -#include <RobotAPI/libraries/aron/core/type/variant/container/Object.h> -#include <RobotAPI/libraries/aron/core/type/variant/primitive/String.h> - namespace armarx::skills::provider { - HelloWorldSkill::HelloWorldSkill() : - SimpleSpecializedSkill<skills::Example::HelloWorldAcceptedType>(GetSkillDescription()) - { - } - - SkillDescription - HelloWorldSkill::GetSkillDescription() - { - armarx::skills::Example::HelloWorldAcceptedType root_profile_params; - root_profile_params.some_float = 5; - root_profile_params.some_int = 42; - root_profile_params.some_text = "YOLO"; - root_profile_params.some_list_of_matrices.push_back(Eigen::Matrix3f::Zero()); - root_profile_params.some_matrix = Eigen::Matrix3f::Zero(); - - return SkillDescription{{"HelloWorld"}, - "This skill logs a message on ARMARX_IMPORTANT", - root_profile_params.toAron(), - armarx::core::time::Duration::MilliSeconds(1000), - armarx::skills::Example::HelloWorldAcceptedType::ToAronType()}; - } - - Skill::MainResult - HelloWorldSkill::main(const SpecializedMainInput& in) - { - ARMARX_IMPORTANT << "Hi, from the Hello World Skill.\n" - << "I received the following data: \n" - << aron::data::converter::AronNlohmannJSONConverter::ConvertToNlohmannJSON( - in.parameters.toAron()) - .dump(2) - << "\n" - << "Type fulfilled? " - << parameters->fullfillsType( - armarx::skills::Example::HelloWorldAcceptedType::ToAronType()) - << "\n" - << "(executed at: " << IceUtil::Time::now() << ")"; - return {TerminatedSkillStatus::Succeeded, nullptr}; - } - - IncompleteSkill::IncompleteSkill() : SimpleSkill(GetSkillDescription()) - { - } - - SkillDescription - IncompleteSkill::GetSkillDescription() - { - auto d = HelloWorldSkill::GetSkillDescription(); - return SkillDescription{{"IncompleteSkill"}, - d.description, - {}, - d.timeout + armarx::core::time::Duration::Seconds(2), - d.parametersType}; - } - - Skill::PrepareResult - IncompleteSkill::prepare() - { - if (!first_prepared) - { - first_prepared = true; - std::thread foo( - [&]() - { - auto d = HelloWorldSkill::GetSkillDescription(); - std::this_thread::sleep_for(std::chrono::milliseconds(2000)); - this->setParameters(d.rootProfileDefaults); - }); - foo.detach(); - } - - auto s = HelloWorldSkill(); - s.setParameters(this->getParameters()); - s.mainOfSkill(); - - return {.status = ActiveOrTerminatedSkillStatus::Succeeded}; - } - - Skill::MainResult - IncompleteSkill::main(const MainInput& in) - { - auto s = HelloWorldSkill(); - s.setParameters(in.parameters); - return s.mainOfSkill(); - } - - ChainingSkill::ChainingSkill() : SimpleSkill(GetSkillDescription()) - { - } - - SkillDescription - ChainingSkill::GetSkillDescription() - { - return SkillDescription{{"ChainingSkill"}, - "This skill calls the HelloWorld skill three times.", - {}, - armarx::core::time::Duration::MilliSeconds(3000), - nullptr}; - } - - Skill::MainResult - ChainingSkill::main(const MainInput& in) - { - armarx::skills::Example::HelloWorldAcceptedType exec1; - armarx::skills::Example::HelloWorldAcceptedType exec2; - armarx::skills::Example::HelloWorldAcceptedType exec3; - - exec1.some_text = "Hello from the ChainingSkill 1"; - exec2.some_text = "Hello from the ChainingSkill 2"; - exec3.some_text = "Hello from the ChainingSkill 3"; - - SkillProxy skillExecPrx( - manager, skills::SkillID(skills::ProviderID("SkillProviderExample"), "HelloWorld")); - - skillExecPrx.executeSkill(getSkillId().toString(), exec1.toAron()); - skillExecPrx.executeSkill(getSkillId().toString(), exec2.toAron()); - skillExecPrx.executeSkill(getSkillId().toString(), exec3.toAron()); - - return {TerminatedSkillStatus::Succeeded, nullptr}; - } - - TimeoutSkill::TimeoutSkill() : - PeriodicSkill(GetSkillDescription(), armarx::core::time::Frequency::Hertz(5)) - { - } - - SkillDescription - TimeoutSkill::GetSkillDescription() - { - return SkillDescription{{"Timeout"}, - "This fails with timeout reached", - {}, - armarx::core::time::Duration::MilliSeconds(5000), - nullptr}; - } - - PeriodicSkill::StepResult - TimeoutSkill::step() - { - // do heavy work - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - - return {ActiveOrTerminatedSkillStatus::Running, nullptr}; - } - - CallbackSkill::CallbackSkill() : SimpleSkill(GetSkillDescription()) - { - } - - SkillDescription - CallbackSkill::GetSkillDescription() - { - return SkillDescription{{"ShowMeCallbacks"}, - "This skill does shows callbacks", - {}, - armarx::core::time::Duration::MilliSeconds(1000), - nullptr}; - } - - Skill::MainResult - CallbackSkill::main(const MainInput& in) - { - ARMARX_IMPORTANT << "Logging three updates via the callback"; - auto up1 = std::make_shared<aron::data::Dict>(); - up1->addElement("updateInfo", std::make_shared<aron::data::String>("Update 1")); - - in.callback(skills::SkillStatus::Running, up1); - - auto up2 = std::make_shared<aron::data::Dict>(); - up2->addElement("updateInfo", std::make_shared<aron::data::String>("Update 2")); - in.callback(skills::SkillStatus::Running, up2); - - auto up3 = std::make_shared<aron::data::Dict>(); - up3->addElement("updateInfo", std::make_shared<aron::data::String>("Update 3")); - in.callback(skills::SkillStatus::Running, up3); - - return {TerminatedSkillStatus::Succeeded, nullptr}; - } - SkillProviderExample::SkillProviderExample() : SkillProviderComponentPluginUser() { } diff --git a/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.h b/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.h index 24d607450..b8391b088 100644 --- a/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.h +++ b/source/RobotAPI/components/skills/SkillProviderExample/SkillProviderExample.h @@ -27,73 +27,16 @@ #include <ArmarXCore/core/Component.h> // RobotAPI -#include <RobotAPI/components/skills/SkillProviderExample/aron/HelloWorldAcceptedType.aron.generated.h> -#include <RobotAPI/libraries/skills/provider/SimpleSkill.h> -#include <RobotAPI/libraries/skills/provider/SimpleSpecializedSkill.h> #include <RobotAPI/libraries/skills/provider/SkillProviderComponentPlugin.h> -#include <RobotAPI/libraries/skills/provider/SkillProxy.h> + +#include "Callback.h" +#include "Chaining.h" +#include "HelloWorld.h" +#include "Incomplete.h" +#include "Timeout.h" namespace armarx::skills::provider { - // Skills: - class HelloWorldSkill : public SimpleSpecializedSkill<skills::Example::HelloWorldAcceptedType> - { - public: - HelloWorldSkill(); - - static SkillDescription GetSkillDescription(); - - private: - Skill::MainResult main(const SpecializedMainInput& in) final; - }; - - class IncompleteSkill : public SimpleSkill - { - public: - IncompleteSkill(); - - static SkillDescription GetSkillDescription(); - - private: - Skill::PrepareResult prepare() final; - Skill::MainResult main(const MainInput&) final; - - std::atomic_bool first_prepared = false; - }; - - class ChainingSkill : public SimpleSkill - { - public: - ChainingSkill(); - - static SkillDescription GetSkillDescription(); - - private: - Skill::MainResult main(const MainInput&) final; - }; - - class TimeoutSkill : public PeriodicSkill - { - public: - TimeoutSkill(); - - static SkillDescription GetSkillDescription(); - - private: - PeriodicSkill::StepResult step() final; - }; - - class CallbackSkill : public SimpleSkill - { - public: - CallbackSkill(); - - static SkillDescription GetSkillDescription(); - - private: - Skill::MainResult main(const MainInput&) final; - }; - /** * @defgroup Component-ExampleClient ExampleClient * @ingroup RobotAPI-Components diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Timeout.cpp b/source/RobotAPI/components/skills/SkillProviderExample/Timeout.cpp new file mode 100644 index 000000000..270dae019 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Timeout.cpp @@ -0,0 +1,30 @@ + +#include "Timeout.h" + +namespace armarx::skills::provider +{ + + TimeoutSkill::TimeoutSkill() : + PeriodicSkill(GetSkillDescription(), armarx::core::time::Frequency::Hertz(5)) + { + } + + SkillDescription + TimeoutSkill::GetSkillDescription() + { + return SkillDescription{{"Timeout"}, + "This fails with timeout reached", + {}, + armarx::core::time::Duration::MilliSeconds(2000), + nullptr}; + } + + PeriodicSkill::StepResult + TimeoutSkill::step() + { + // do heavy work + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + + return {ActiveOrTerminatedSkillStatus::Running, nullptr}; + } +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/components/skills/SkillProviderExample/Timeout.h b/source/RobotAPI/components/skills/SkillProviderExample/Timeout.h new file mode 100644 index 000000000..347e216a9 --- /dev/null +++ b/source/RobotAPI/components/skills/SkillProviderExample/Timeout.h @@ -0,0 +1,42 @@ + +/* + * This file is part of ArmarX. + * + * ArmarX is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * ArmarX is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + * @author Fabian Reister ( fabian dot reister at kit dot edu ) + * @date 2021 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + + +// RobotAPI +#include <RobotAPI/libraries/skills/provider/PeriodicSkill.h> + +namespace armarx::skills::provider +{ + + class TimeoutSkill : public PeriodicSkill + { + public: + TimeoutSkill(); + + static SkillDescription GetSkillDescription(); + + private: + PeriodicSkill::StepResult step() final; + }; +} // namespace armarx::skills::provider diff --git a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp index b7880b87f..87450efc5 100644 --- a/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp +++ b/source/RobotAPI/gui-plugins/SkillManagerPlugin/SkillManagerMonitorWidgetController.cpp @@ -356,8 +356,9 @@ namespace armarx { std::scoped_lock l(updateMutex); - auto currentManagerStatuses = memory->getLatestSkillExecutionStatuses( - 100); // we assume that there are no more than 100 new skills.. + auto currentManagerStatuses = + memory + ->getSkillExecutionStatuses(); // we assume that there are no more than 100 new skills.. for (const auto& [k, v] : currentManagerStatuses) { @@ -378,19 +379,19 @@ namespace armarx // update values found->setText(3, QString::fromStdString( - statusUpdate.hasBeenConstructed() ? " (\xfb) " : "")); + statusUpdate.hasBeenConstructed() ? " yes " : " no ")); found->setText(4, QString::fromStdString( - statusUpdate.hasBeenInitialized() ? " (\xfb) " : "")); + statusUpdate.hasBeenInitialized() ? " yes " : " no ")); found->setText(5, QString::fromStdString( - statusUpdate.hasBeenPrepared() ? " (\xfb) " : "")); + statusUpdate.hasBeenPrepared() ? " yes " : " no ")); found->setText(6, QString::fromStdString( - statusUpdate.hasBeenRunning() ? " (\xfb) " : "")); + statusUpdate.hasBeenRunning() ? " yes " : " no ")); found->setText(7, QString::fromStdString( - statusUpdate.hasBeenTerminated() ? " (\xfb) " : "")); + statusUpdate.hasBeenTerminated() ? " yes " : " no ")); break; } } @@ -408,18 +409,19 @@ namespace armarx item->setText(2, QString::fromStdString(executionId.skillId.toString())); item->setText(3, QString::fromStdString( - statusUpdate.hasBeenConstructed() ? " (\xfb) " : "")); + statusUpdate.hasBeenConstructed() ? " yes " : " no ")); item->setText(4, QString::fromStdString( - statusUpdate.hasBeenInitialized() ? " (\xfb) " : "")); + statusUpdate.hasBeenInitialized() ? " yes " : " no ")); item->setText( 5, - QString::fromStdString(statusUpdate.hasBeenPrepared() ? " (\xfb) " : "")); + QString::fromStdString(statusUpdate.hasBeenPrepared() ? " yes " : " no ")); item->setText( - 6, QString::fromStdString(statusUpdate.hasBeenRunning() ? " (\xfb) " : "")); - item->setText( - 7, - QString::fromStdString(statusUpdate.hasBeenTerminated() ? " (\xfb) " : "")); + 6, + QString::fromStdString(statusUpdate.hasBeenRunning() ? " yes " : " no ")); + item->setText(7, + QString::fromStdString( + statusUpdate.hasBeenTerminated() ? " yes " : " no ")); } } } diff --git a/source/RobotAPI/interface/skills/SkillMemoryInterface.ice b/source/RobotAPI/interface/skills/SkillMemoryInterface.ice index 934d2faba..3d0d8ee61 100644 --- a/source/RobotAPI/interface/skills/SkillMemoryInterface.ice +++ b/source/RobotAPI/interface/skills/SkillMemoryInterface.ice @@ -36,11 +36,7 @@ module armarx module dti { interface SkillMemoryInterface extends armem::server::MemoryInterface, - dti::StatechartListenerInterface, manager::dti::SkillManagerInterface - { - manager::dto::SkillStatusUpdateMap getLatestSkillExecutionStatuses( - int n); // returns latest n statusupdates of all providers - }; + dti::StatechartListenerInterface, manager::dti::SkillManagerInterface{}; } } } diff --git a/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.cpp b/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.cpp index d50fcba80..11dc8d0df 100644 --- a/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.cpp +++ b/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.cpp @@ -53,39 +53,58 @@ namespace armarx::skills::segment } std::map<skills::SkillExecutionID, skills::SkillStatusUpdate> - SkillEventCoreSegment::getLatestSkillEvents(int n) + SkillEventCoreSegment::getSkillStatusUpdates() { std::map<skills::SkillExecutionID, skills::SkillStatusUpdate> ret; auto coreSegment = this->segmentPtr; ARMARX_CHECK(coreSegment); - // 1. get all events and sort them by timestamp. - std::vector<skills::SkillStatusUpdate> sorted; coreSegment->forEachInstance( - [&sorted](const armem::wm::EntityInstance& i) + [&](const armem::wm::EntityInstance& i) { auto event = i.dataAs<armarx::skills::arondto::SkillStatusUpdate>(); skills::SkillStatusUpdate up; armem::fromAron(event, up); - sorted.emplace_back(std::move(up)); + + if (auto it = ret.find(up.executionId); it != ret.end() && up < it->second) + { + return; + } + + // set or replace + ret[up.executionId] = up; }); - std::sort(sorted.begin(), - sorted.end(), - [](const skills::SkillStatusUpdate& a, const skills::SkillStatusUpdate& b) - { return b < a; }); + // for (const auto& [k, v] : ret) + // { + // ARMARX_IMPORTANT << "Skill " << k.skillId << " has stati: " << int(v.status); + // } - for (const auto& el : sorted) - { - if (ret.size() >= (size_t)n) - { - break; - } + return ret; + } - ret.emplace( - std::piecewise_construct, std::make_tuple(el.executionId), std::make_tuple(el)); - } + std::optional<skills::SkillStatusUpdate> + SkillEventCoreSegment::getSkillStatusUpdate(const skills::SkillExecutionID& id) + { + std::optional<skills::SkillStatusUpdate> ret = std::nullopt; + auto coreSegment = this->segmentPtr; + ARMARX_CHECK(coreSegment); + coreSegment->forEachInstance( + [&](const armem::wm::EntityInstance& i) + { + auto event = i.dataAs<armarx::skills::arondto::SkillStatusUpdate>(); + skills::SkillStatusUpdate up; + armem::fromAron(event, up); + + if (up.executionId == id) + { + if (!ret || (ret.has_value() && *ret < up)) + { + ret = up; + } + } + }); return ret; } } // namespace armarx::skills::segment diff --git a/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.h b/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.h index 492c26090..0537cbdbd 100644 --- a/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.h +++ b/source/RobotAPI/libraries/armem_skills/server/segment/SkillEventSegment.h @@ -27,7 +27,10 @@ namespace armarx::skills::segment void addSkillUpdateEvent(const skills::SkillStatusUpdate& update); - std::map<skills::SkillExecutionID, skills::SkillStatusUpdate> getLatestSkillEvents(int n); + std::map<skills::SkillExecutionID, skills::SkillStatusUpdate> getSkillStatusUpdates(); + + std::optional<skills::SkillStatusUpdate> + getSkillStatusUpdate(const skills::SkillExecutionID& id); private: }; diff --git a/source/RobotAPI/libraries/skills/core/CMakeLists.txt b/source/RobotAPI/libraries/skills/core/CMakeLists.txt index 66795b7ec..29145e60e 100644 --- a/source/RobotAPI/libraries/skills/core/CMakeLists.txt +++ b/source/RobotAPI/libraries/skills/core/CMakeLists.txt @@ -24,6 +24,7 @@ armarx_add_library( SkillPreparationInput.cpp SkillParameterization.cpp Skill.cpp + SkillProxy.cpp SkillDescription.cpp HEADERS error/Exception.h @@ -36,6 +37,7 @@ armarx_add_library( SkillPreparationInput.h SkillParameterization.h Skill.h + SkillProxy.h SkillDescription.h ) diff --git a/source/RobotAPI/libraries/skills/core/Skill.cpp b/source/RobotAPI/libraries/skills/core/Skill.cpp index 7d8d9b9c8..34895c906 100644 --- a/source/RobotAPI/libraries/skills/core/Skill.cpp +++ b/source/RobotAPI/libraries/skills/core/Skill.cpp @@ -14,16 +14,76 @@ namespace armarx void Skill::installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb) { - std::lock_guard l(conditionCallbacksMutex); + std::scoped_lock l(conditionCallbacksMutex); conditionCallbacks.push_back({f, cb}); } + std::optional<TerminatedSkillStatusUpdate> + Skill::callSubskill(const SkillProxy& prx, const aron::data::DictPtr& params) + { + auto eid = callSubskillAsync(prx, params); + auto ret = prx.join(eid); + ARMARX_IMPORTANT << "FINISHED SUBSKILL. RET IS " << ret.has_value(); + return ret; + } + + skills::SkillExecutionID + Skill::callSubskillAsync(const skills::SkillProxy& prx, const aron::data::DictPtr& params) + { + std::unique_lock l(subskillsMutex); + + std::string executorHistory = this->executorName + "->" + getSkillId().toString(); + auto eid = prx.executeSkillAsync(executorHistory, params); + this->subskills.push_back(eid); + return eid; + } + + void + Skill::updateParameters(const aron::data::DictPtr& d) + { + std::scoped_lock l(this->parametersMutex); + if (this->parameters == nullptr) + { + // set params as there has been no update before. + this->parameters = d; + } + else + { + // merge params into existing. Note that this may update already set params. + this->parameters->mergeAndReplaceCopy(d); + } + } + + void + Skill::setParameters(const aron::data::DictPtr& d) + { + // we only set the params if the skill is not already running + if (running or exiting or finished) + { + return; + } + + std::scoped_lock l(this->parametersMutex); + this->parameters = d; + } + + aron::data::DictPtr + Skill::getParameters() const + { + std::scoped_lock l(this->parametersMutex); + return this->parameters; + } + Skill::InitResult Skill::_init() { //ARMARX_IMPORTANT << "Initializing skill '" << description.skillName << "'"; this->initializing = true; this->constructing = false; + this->preparing = false; + this->running = false; + this->exiting = false; + this->finished = false; // install timeout condition installConditionWithCallback( @@ -36,7 +96,7 @@ namespace armarx [&]() { armarx::core::time::Metronome metronome(conditionCheckingThreadFrequency); - while (constructing or initializing or preparing or + while (initializing or preparing or running) // when the skill ends/aborts this variable will be set to false { { @@ -51,13 +111,15 @@ namespace armarx } } } + const auto sleepDuration = metronome.waitForNextTick(); if (not sleepDuration.isPositive()) { - ARMARX_WARNING - << deactivateSpam() << "PeriodicSkill: execution took too long (" - << -sleepDuration << " vs " - << conditionCheckingThreadFrequency.toCycleDuration() << ")"; + ARMARX_WARNING << deactivateSpam() + << "ConditionCheckingThread: execution took too long (" + << -sleepDuration << " vs " + << conditionCheckingThreadFrequency.toCycleDuration() + << ")"; } } }); @@ -69,6 +131,10 @@ namespace armarx { this->preparing = true; this->initializing = false; + this->constructing = false; + this->running = false; + this->exiting = false; + this->finished = false; if (shouldSkillTerminate()) { @@ -94,7 +160,11 @@ namespace armarx Skill::_main() { this->running = true; + this->initializing = false; + this->constructing = false; this->preparing = false; + this->exiting = false; + this->finished = false; return {.status = TerminatedSkillStatus::Succeeded}; } @@ -102,16 +172,21 @@ namespace armarx Skill::_exit() { // ARMARX_IMPORTANT << "Exiting Skill '" << description.skillName << "'"; + this->exiting = true; this->running = false; this->initializing = false; this->constructing = false; this->preparing = false; + this->finished = false; if (conditionCheckingThread.joinable()) { conditionCheckingThread.join(); } exited = armarx::core::time::DateTime::Now(); + + this->finished = true; + this->exiting = false; return {.status = TerminatedSkillStatus::Succeeded}; } @@ -152,13 +227,6 @@ namespace armarx return {.status = skills::mergeSkillStatuseses(_res.status, res.status)}; } - void - Skill::notifyTimeoutReached() - { - timeoutReached = true; - onTimeoutReached(); - } - void Skill::throwIfSkillShouldTerminate(const std::function<void()>& do_before, const std::string& abortedMessage) @@ -224,10 +292,21 @@ namespace armarx void Skill::notifySkillToStop() { + std::scoped_lock l(subskillsMutex); stopped = true; + _onStopRequested(); onStopRequested(); } + void + Skill::notifyTimeoutReached() + { + std::scoped_lock l(subskillsMutex); + timeoutReached = true; + _onTimeoutReached(); + onTimeoutReached(); + } + bool Skill::shouldSkillTerminate() const { @@ -235,6 +314,32 @@ namespace armarx } // condition effects + void + Skill::_onTimeoutReached() + { + if (!manager) + { + return; + } + for (const auto& execId : subskills) + { + manager->abortSkillAsync(execId.toManagerIce()); + } + } + + void + Skill::_onStopRequested() + { + if (!manager) + { + return; + } + for (const auto& execId : subskills) + { + manager->abortSkillAsync(execId.toManagerIce()); + } + } + void Skill::onTimeoutReached() { diff --git a/source/RobotAPI/libraries/skills/core/Skill.h b/source/RobotAPI/libraries/skills/core/Skill.h index ff3cf1e82..5ec80e77d 100644 --- a/source/RobotAPI/libraries/skills/core/Skill.h +++ b/source/RobotAPI/libraries/skills/core/Skill.h @@ -19,6 +19,7 @@ #include "SkillDescription.h" #include "SkillID.h" #include "SkillPreparationInput.h" +#include "SkillProxy.h" #include "SkillStatusUpdate.h" #include "error/Exception.h" @@ -32,95 +33,112 @@ namespace armarx using CallbackT = std::function<void(const SkillStatus s, const armarx::aron::data::DictPtr&)>; + /// A result struct for skill initialization struct InitResult { TerminatedSkillStatus status; }; + /// A result struct for skill preparing struct PrepareResult { ActiveOrTerminatedSkillStatus status; }; + /// A result struct for th main method of a skill struct MainResult { TerminatedSkillStatus status; aron::data::DictPtr data = nullptr; }; + /// A result struct for skill exit function struct ExitResult { TerminatedSkillStatus status; }; + /// We completely remove the default constructor! A skill without a desciption cannot exist Skill() = delete; + + /// Constructor of a skill for inheritance. Every skill must have a skill description Skill(const SkillDescription&); - virtual ~Skill() = default; - /// The id of the skill (combination of provider and name must be unique). + /// Virtual destructor of a skill + virtual ~Skill() + { + //ARMARX_IMPORTANT << "DESTROY SKILL " << getSkillId(); + } + + /// Get the id of the skill SkillID getSkillId() const { return description.skillId; } + /// Get the description of a skill SkillDescription getSkillDescription() const { return description; } + /// Set the provider id of the description of the skill. + /// This method is called when creating a skill in a skill provider void setProviderId(const skills::ProviderID& pid) { description.skillId.providerId = pid; } + void + setCallback(const CallbackT& callback) + { + this->callback = callback; + } + + void + setManager(const manager::dti::SkillManagerInterfacePrx& manager) + { + this->manager = manager; + } + + void + setExecutorName(const std::string& executorName) + { + this->executorName = executorName; + } + + /// Prepare a skill once. This method is called in a loop as long as it returns RUNNING + /// If the loop does not terminate with SUCCEDED the skill execution is failed. PrepareResult prepareSkill(); + /// Initialization of a skill. Called directly after construction. + /// If this method does not return SUCCEEDED the skill execution is failed. InitResult initSkill(); + /// Main method of a skill. MainResult mainOfSkill(); + /// Exit method of a skill. It is guaranteed that exit is always called + /// (unless there is a segfault or similar) ExitResult exitSkill(); - // Condition listeners - // used to notify the skill from extern to stop + /// Notify the skill from extern to stop void notifySkillToStop(); - // returns whether the skill should terminate as soon as possible + /// Returns whether the skill should terminate as soon as possible bool shouldSkillTerminate() const; - // merge parameters to the local parameters of the skill - void - updateParameters(const aron::data::DictPtr& d) - { - std::scoped_lock l(this->parametersMutex); - if (this->parameters == nullptr) - { - this->parameters = d; - } - else - { - this->parameters->mergeAndReplaceCopy(d); - } - } + /// Merge parameters to the local parameters of the skill + void updateParameters(const aron::data::DictPtr& d); - // hard set the parameters, ignoring everything that has been set or merged before - void - setParameters(const aron::data::DictPtr& d) - { - std::scoped_lock l(this->parametersMutex); - this->parameters = d; - } + /// Hard set the parameters, ignoring everything that has been set or merged before + void setParameters(const aron::data::DictPtr& d); - // get the parameters - aron::data::DictPtr - getParameters() const - { - std::scoped_lock l(this->parametersMutex); - return this->parameters; - } + /// Get the parameters of a skill that have been set so far + aron::data::DictPtr getParameters() const; protected: void throwIfSkillShouldTerminate(const std::string& abortedMessage = ""); @@ -134,12 +152,17 @@ namespace armarx // fires if the skill reaches timeout void notifyTimeoutReached(); + private: // helper methods to do all the static initialization stuff InitResult _init(); PrepareResult _prepare(); MainResult _main(); ExitResult _exit(); + void _onTimeoutReached(); + void _onStopRequested(); + + protected: /// Override this method with the actual implementation. virtual InitResult init(); @@ -152,7 +175,7 @@ namespace armarx /// Override this method with the actual implementation. virtual ExitResult exit(); - private: + protected: /// Override these methods if you want to do something special when notification comes virtual void onTimeoutReached(); virtual void onStopRequested(); @@ -162,35 +185,42 @@ namespace armarx void installConditionWithCallback(std::function<bool()>&& f, std::function<void()>&& cb); + /// call a subskill and block until the subskill terminates. + /// If you call a subskill this way it will be stopped if the current skill stops. + std::optional<TerminatedSkillStatusUpdate> + callSubskill(const skills::SkillProxy& prx, const aron::data::DictPtr& = nullptr); + + /// Similar to callSubskill but non-blocking + skills::SkillExecutionID callSubskillAsync(const skills::SkillProxy& prx, + const aron::data::DictPtr& = nullptr); + public: // running params armarx::core::time::DateTime started = armarx::core::time::DateTime::Now(); armarx::core::time::DateTime exited = armarx::core::time::DateTime::Invalid(); - // parameterization. Will be set from implementation wrapper. - // const params after initialization!! - CallbackT callback; + protected: + // parameterization. Will be set from implementation wrapper. No getters available + // const after construction!! + CallbackT callback = CallbackT(); manager::dti::SkillManagerInterfacePrx manager = nullptr; - std::string executorName = "INVALID EXECUTOR NAME"; + std::string executorName = ""; protected: - // non-const params + // non-const params. Const after preparation. Getters are available mutable std::mutex parametersMutex; armarx::aron::data::DictPtr parameters = nullptr; // The descripion of the skill SkillDescription description; - // active conditions. First is condition (bool return func) - mutable std::mutex conditionCallbacksMutex; - std::vector<std::pair<std::function<bool()>, std::function<void()>>> - conditionCallbacks = {}; - - // Status variables. + // Status variables std::atomic_bool constructing = true; std::atomic_bool initializing = false; std::atomic_bool preparing = false; std::atomic_bool running = false; + std::atomic_bool exiting = false; + std::atomic_bool finished = false; // Conditionals to indicate that an event has occured. Use conditions this way std::atomic_bool stopped = false; @@ -198,8 +228,16 @@ namespace armarx private: - std::thread conditionCheckingThread; // A therad that checks the conditions frequently + // active conditions. First is condition (bool return func) + mutable std::mutex conditionCallbacksMutex; + std::vector<std::pair<std::function<bool()>, std::function<void()>>> + conditionCallbacks = {}; + + std::thread conditionCheckingThread; // A thread that checks the conditions frequently armarx::Frequency conditionCheckingThreadFrequency = armarx::Frequency::Hertz(20); + + mutable std::mutex subskillsMutex; + std::vector<skills::SkillExecutionID> subskills; }; template <class T> diff --git a/source/RobotAPI/libraries/skills/core/SkillExecutionID.h b/source/RobotAPI/libraries/skills/core/SkillExecutionID.h index f639b4b4e..6d9975bb4 100644 --- a/source/RobotAPI/libraries/skills/core/SkillExecutionID.h +++ b/source/RobotAPI/libraries/skills/core/SkillExecutionID.h @@ -18,10 +18,6 @@ namespace armarx { struct SkillExecutionID { - SkillID skillId; - std::string executorName; - armarx::core::time::DateTime executionStartedTime; - SkillExecutionID() = default; SkillExecutionID(const SkillID&, const std::string& executorName, @@ -30,26 +26,26 @@ namespace armarx bool operator==(const SkillExecutionID& other) const { - if (skillId != other.skillId) - { - return false; - } - if (executionStartedTime != other.executionStartedTime) - { - return false; - } - if (executorName != other.executorName) - { - return false; - } - return true; + return this->toString() == other.toString(); } bool operator<(const SkillExecutionID& other) const { - // We explicitly do not compare skillIds as we ONLY want to bring the executionids in some (temporal) order - return executionStartedTime < other.executionStartedTime; + return this->toString() < other.toString(); + } + + bool + operator<=(const SkillExecutionID& other) const + { + return this->toString() <= other.toString(); + } + + std::string + toString() const + { + return skillId.toString() + " requested by " + executorName + " at " + + executionStartedTime.toDateTimeString(); } skills::manager::dto::SkillExecutionID toManagerIce() const; @@ -60,6 +56,11 @@ namespace armarx static SkillExecutionID FromIce(const skills::provider::dto::SkillExecutionID&, const std::optional<skills::ProviderID>& providerName); + + + SkillID skillId; + std::string executorName; + armarx::core::time::DateTime executionStartedTime; }; } // namespace skills diff --git a/source/RobotAPI/libraries/skills/core/SkillProxy.cpp b/source/RobotAPI/libraries/skills/core/SkillProxy.cpp new file mode 100644 index 000000000..f73c43dfd --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/SkillProxy.cpp @@ -0,0 +1,118 @@ +#include "SkillProxy.h" + +#include <chrono> +#include <thread> + +namespace armarx +{ + namespace skills + { + SkillProxy::SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, + const SkillID& skillId) : + manager(manager) + { + ARMARX_CHECK_NOT_NULL(manager); + skillDescription = SkillDescription::FromIce( + manager->getSkillDescription(skillId.toManagerIce()).value()); + ARMARX_CHECK(skillDescription.skillId.isFullySpecified()); + } + + SkillProxy::SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, + const SkillDescription& skillDesc) : + manager(manager), skillDescription(skillDesc) + { + ARMARX_CHECK_NOT_NULL(manager); + ARMARX_CHECK(skillDesc.skillId.isFullySpecified()); + } + + SkillDescription + SkillProxy::getSkillDescription() const + { + return skillDescription; + } + + SkillID + SkillProxy::getSkillId() const + { + return skillDescription.skillId; + } + + TerminatedSkillStatusUpdate + SkillProxy::executeSkill(const std::string& executorName, + const aron::data::DictPtr& params) const + { + ARMARX_CHECK_NOT_NULL(manager); + skills::manager::dto::SkillExecutionRequest req; + req.executorName = executorName; + req.parameters = aron::data::Dict::ToAronDictDTO(params); + req.skillId = skillDescription.skillId.toManagerIce(); + + auto terminatingUpdateIce = manager->executeSkill(req); + return TerminatedSkillStatusUpdate::FromIce(terminatingUpdateIce); + } + + SkillExecutionID + SkillProxy::executeSkillAsync(const std::string& executorName, + const aron::data::DictPtr& params) const + { + ARMARX_CHECK_NOT_NULL(manager); + skills::manager::dto::SkillExecutionRequest req; + req.executorName = executorName; + req.parameters = aron::data::Dict::ToAronDictDTO(params); + req.skillId = skillDescription.skillId.toManagerIce(); + + auto execIdIce = manager->executeSkillAsync(req); + return SkillExecutionID::FromIce(execIdIce); + } + + std::optional<TerminatedSkillStatusUpdate> + SkillProxy::join(const SkillExecutionID& executionId) const + { + ARMARX_CHECK_NOT_NULL(manager); + auto s = this->manager->getSkillExecutionStatus(executionId.toManagerIce()); + + while (s) + { + auto statusUpdate = skills::SkillStatusUpdate::FromIce(*s); + + if (statusUpdate.hasBeenTerminated()) + { + break; + } + + std::this_thread::sleep_for(std::chrono::milliseconds(50)); + s = this->manager->getSkillExecutionStatus(executionId.toManagerIce()); + } + + if (!s) + { + // Either the manager already removed the result (then it is unknown) or the execution id does not exist. + return std::nullopt; + } + + return TerminatedSkillStatusUpdate::FromIce(*s); + } + + bool + SkillProxy::abortSkill(const SkillExecutionID& id) const + { + ARMARX_CHECK_NOT_NULL(manager); + auto r = manager->abortSkill(id.toManagerIce()); + return r.success; + } + + bool + SkillProxy::abortSkillAsync(const SkillExecutionID& id) const + { + ARMARX_CHECK_NOT_NULL(manager); + auto r = manager->abortSkillAsync(id.toManagerIce()); + return r.success; + } + + aron::data::DictPtr + SkillProxy::getRootProfileParameters() const + { + return skillDescription.rootProfileDefaults; + } + } // namespace skills +} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/core/SkillProxy.h b/source/RobotAPI/libraries/skills/core/SkillProxy.h new file mode 100644 index 000000000..e20ca01a0 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/SkillProxy.h @@ -0,0 +1,63 @@ +#pragma once + +#include <RobotAPI/libraries/skills/core/SkillDescription.h> +#include <RobotAPI/libraries/skills/core/SkillStatusUpdate.h> + +namespace armarx +{ + namespace skills + { + /* Manages the remote execution of a skill and converts the ice types */ + class SkillProxy : public armarx::Logging + { + public: + /// We remove the default constructor as every skill proxy requires a manager + SkillProxy() = delete; + + /// set the skill proxy using a skillId. Queries the manager to get the description. + SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, + const SkillID& skillId); + + /// set the proxy using a skill description + SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, + const SkillDescription& skillDesc); + + /// copy ctor + SkillProxy(const SkillProxy& o) = default; + + /// get the skill description + SkillDescription getSkillDescription() const; + + /// get the skill id from the skill description + SkillID getSkillId() const; + + // Provide a similar API as the skillprovider + /// execute a skill and block until skill terminates + TerminatedSkillStatusUpdate + executeSkill(const std::string& executorName, + const aron::data::DictPtr& params = nullptr) const; + + /// execute a skill. Do not block during execution + SkillExecutionID executeSkillAsync(const std::string& executorName, + const aron::data::DictPtr& params = nullptr) const; + + /// poll execution status and block until its null or terminated + std::optional<TerminatedSkillStatusUpdate> + join(const SkillExecutionID& executionId) const; + + /// ask skill to abort ASAP. Blocks until skill stopped + bool abortSkill(const SkillExecutionID& executionId) const; + + /// ask skill to abort ASAP + bool abortSkillAsync(const SkillExecutionID& executionId) const; + + // Utiliy methods + /// get the default parameters of the skill. TODO: Skill profiles in memory! + aron::data::DictPtr getRootProfileParameters() const; + + protected: + manager::dti::SkillManagerInterfacePrx manager; + SkillDescription skillDescription; + }; + } // namespace skills +} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp index 4b7cdec1b..233aa841b 100644 --- a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp +++ b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp @@ -580,9 +580,6 @@ namespace armarx this->plugin->removeProvider(i); } - using SkillProviderInterfacePrxMap = - std::map<std::string, skills::provider::dti::SkillProviderInterfacePrx>; - skills::manager::dto::SkillStatusUpdate SkillManagerComponentPluginUser::executeSkill( const skills::manager::dto::SkillExecutionRequest& info, diff --git a/source/RobotAPI/libraries/skills/provider/CMakeLists.txt b/source/RobotAPI/libraries/skills/provider/CMakeLists.txt index 5ccff9120..fba16f3e7 100644 --- a/source/RobotAPI/libraries/skills/provider/CMakeLists.txt +++ b/source/RobotAPI/libraries/skills/provider/CMakeLists.txt @@ -21,7 +21,6 @@ armarx_add_library( LambdaSkill.cpp SimpleSkill.cpp SimpleSpecializedSkill.cpp - SkillProxy.cpp SpecializedSkillProxy.cpp PeriodicSkill.cpp SpecializedSkill.cpp @@ -36,7 +35,6 @@ armarx_add_library( LambdaSkill.h SimpleSkill.h SimpleSpecializedSkill.h - SkillProxy.h SpecializedSkillProxy.h PeriodicSkill.h SpecializedSkill.h diff --git a/source/RobotAPI/libraries/skills/provider/PeriodicSkill.cpp b/source/RobotAPI/libraries/skills/provider/PeriodicSkill.cpp index 02ad14bc1..197159cb7 100644 --- a/source/RobotAPI/libraries/skills/provider/PeriodicSkill.cpp +++ b/source/RobotAPI/libraries/skills/provider/PeriodicSkill.cpp @@ -47,8 +47,10 @@ namespace armarx::skills { armarx::core::time::Metronome metronome(frequency); - while (not Skill::shouldSkillTerminate()) + while (true) { + this->throwIfSkillShouldTerminate(); + const auto res = stepOfSkill(); switch (res.status) { @@ -56,11 +58,11 @@ namespace armarx::skills // nothing to do here break; case ActiveOrTerminatedSkillStatus::Aborted: - return {TerminatedSkillStatus::Aborted, res.data}; + return MakeAbortedResult(); case ActiveOrTerminatedSkillStatus::Succeeded: - return {TerminatedSkillStatus::Succeeded, res.data}; + return MakeSucceededResult(res.data); case ActiveOrTerminatedSkillStatus::Failed: - return {TerminatedSkillStatus::Failed, res.data}; + return MakeFailedResult(); } const auto sleepDuration = metronome.waitForNextTick(); @@ -72,18 +74,8 @@ namespace armarx::skills } } - if (stopped) - { - return {TerminatedSkillStatus::Aborted, nullptr}; - } - - if (timeoutReached) - { - ARMARX_WARNING << "The skill " << getSkillId().toString() << " reached timeout!"; - return {TerminatedSkillStatus::Failed, nullptr}; - } - - throw skills::error::SkillException(__PRETTY_FUNCTION__, "Should not happen!"); + // never happens + return MakeSucceededResult(); } PeriodicSkill::StepResult diff --git a/source/RobotAPI/libraries/skills/provider/SkillProviderComponentPlugin.cpp b/source/RobotAPI/libraries/skills/provider/SkillProviderComponentPlugin.cpp index 1a5b0b0d1..e5c169c04 100644 --- a/source/RobotAPI/libraries/skills/provider/SkillProviderComponentPlugin.cpp +++ b/source/RobotAPI/libraries/skills/provider/SkillProviderComponentPlugin.cpp @@ -120,7 +120,7 @@ namespace armarx::plugins ARMARX_CHECK(execId.skillId.isSkillSpecified()); const std::unique_lock l(skillExecutionsMutex); - if (skillExecutions.find(execId) != skillExecutions.end()) + if (skillExecutions.find(execId) == skillExecutions.end()) { ARMARX_WARNING << "Skill execution for skill '" + execId.skillId.toString() + "' not found!"; @@ -276,6 +276,21 @@ namespace armarx::plugins }); } + // wait until skill is constructed. This assures, that a status update exists. + while (true) + { + { + std::scoped_lock l(wrapper->skillStatusesMutex); + + if (wrapper->statusUpdate.hasBeenConstructed()) + { + break; + } + } + + std::this_thread::sleep_for(std::chrono::milliseconds(20)); + } + return executionId; } @@ -320,11 +335,21 @@ namespace armarx::plugins return false; } - it->second.stopSkill(); + auto& runtime = it->second; + runtime.stopSkill(); - if (auto it = skillExecutions.find(executionId); it != skillExecutions.end()) + while (true) { - skillExecutions.erase(it); + { + std::scoped_lock l(runtime.skillStatusesMutex); + auto status = runtime.statusUpdate; + + if (status.hasBeenTerminated()) + { + break; + } + } + std::this_thread::sleep_for(std::chrono::milliseconds(20)); } return true; @@ -344,7 +369,7 @@ namespace armarx::plugins return false; } - std::thread([&]() { it->second.stopSkill(); }); + it->second.stopSkill(); return true; } diff --git a/source/RobotAPI/libraries/skills/provider/SkillProxy.cpp b/source/RobotAPI/libraries/skills/provider/SkillProxy.cpp deleted file mode 100644 index 50758fde7..000000000 --- a/source/RobotAPI/libraries/skills/provider/SkillProxy.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "SkillProxy.h" - -namespace armarx -{ - namespace skills - { - SkillProxy::SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, - const SkillID& skillId) : - manager(manager), - skillDescription(SkillDescription::FromIce( - manager->getSkillDescription(skillId.toManagerIce()).value())) - { - } - - SkillProxy::SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, - const SkillDescription& skillDesc) : - manager(manager), skillDescription(skillDesc) - { - ARMARX_CHECK(skillDesc.skillId.isFullySpecified()); - } - - TerminatedSkillStatusUpdate - SkillProxy::executeSkill(const std::string& executorName, const aron::data::DictPtr& params) - { - skills::manager::dto::SkillExecutionRequest req; - req.executorName = executorName; - req.parameters = params->toAronDictDTO(); - req.skillId = skillDescription.skillId.toManagerIce(); - - auto terminatingUpdateIce = manager->executeSkill(req); - return TerminatedSkillStatusUpdate::FromIce(terminatingUpdateIce); - } - - SkillExecutionID - SkillProxy::executeSkillAsync(const std::string& executorName, - const aron::data::DictPtr& params) - { - skills::manager::dto::SkillExecutionRequest req; - req.executorName = executorName; - req.parameters = params->toAronDictDTO(); - req.skillId = skillDescription.skillId.toManagerIce(); - - auto execReqIce = manager->executeSkillAsync(req); - return SkillExecutionID::FromIce(execReqIce); - } - - bool - SkillProxy::abortSkill(const SkillExecutionID& id) - { - auto r = manager->abortSkill(id.toManagerIce()); - return r.success; - } - - bool - SkillProxy::abortSkillAsync(const SkillExecutionID& id) - { - auto r = manager->abortSkillAsync(id.toManagerIce()); - return r.success; - } - - aron::data::DictPtr - SkillProxy::getDefaultParameters(const std::string& profileName) - { - if (profileName == "root") - { - return skillDescription.rootProfileDefaults; - } - // TODO @fabianPK - return nullptr; - } - } // namespace skills -} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/provider/SkillProxy.h b/source/RobotAPI/libraries/skills/provider/SkillProxy.h deleted file mode 100644 index 8f0fb34be..000000000 --- a/source/RobotAPI/libraries/skills/provider/SkillProxy.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include <RobotAPI/libraries/skills/core/Skill.h> - -namespace armarx -{ - namespace skills - { - /* Manages the remote execution of a skill and converts the ice types */ - class SkillProxy : public armarx::Logging - { - public: - SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, - const SkillID& skillId); - SkillProxy(const manager::dti::SkillManagerInterfacePrx& manager, - const SkillDescription& skillDesc); - - // Provide a similar API as the skillprovider - TerminatedSkillStatusUpdate executeSkill(const std::string& executorName, - const aron::data::DictPtr& params = nullptr); - - SkillExecutionID executeSkillAsync(const std::string& executorName, - const aron::data::DictPtr& params = nullptr); - - bool abortSkill(const SkillExecutionID& executorName); - - bool abortSkillAsync(const SkillExecutionID& executorName); - - // Utiliy methods - aron::data::DictPtr getDefaultParameters(const std::string& profileName = "root"); - - protected: - manager::dti::SkillManagerInterfacePrx manager; - SkillDescription skillDescription; - }; - } // namespace skills -} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.cpp b/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.cpp index 1074f5499..4f372c5f5 100644 --- a/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.cpp +++ b/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.cpp @@ -1,4 +1,4 @@ -#include "SkillProxy.h" +#include "SpecializedSkillProxy.h" namespace armarx { diff --git a/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.h b/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.h index 37ecd5a77..4dd53171c 100644 --- a/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.h +++ b/source/RobotAPI/libraries/skills/provider/SpecializedSkillProxy.h @@ -1,6 +1,6 @@ #pragma once -#include "SkillProxy.h" +#include <RobotAPI/libraries/skills/core/SkillProxy.h> namespace armarx { @@ -27,9 +27,9 @@ namespace armarx // Utiliy methods AronT - getDefaultParameters(const std::string& profileName = "root") + getRootProfileParameters() { - auto dict = SkillProxy::getDefaultParameters(profileName); + auto dict = SkillProxy::getRootProfileParameters(); if (dict) { return AronT::FromAron(dict); diff --git a/source/RobotAPI/libraries/skills/provider/detail/SkillImplementationWrapper.cpp b/source/RobotAPI/libraries/skills/provider/detail/SkillImplementationWrapper.cpp index 4ac4757e2..5c20567a2 100644 --- a/source/RobotAPI/libraries/skills/provider/detail/SkillImplementationWrapper.cpp +++ b/source/RobotAPI/libraries/skills/provider/detail/SkillImplementationWrapper.cpp @@ -65,6 +65,8 @@ namespace armarx const auto& skillName = skillId.skillName; const auto& providerId = *skillId.providerId; const auto& executorName = statusUpdate.executionId.executorName; + const auto& manager = + skills::manager::dti::SkillManagerInterfacePrx::checkedCast(callback_interface); ARMARX_INFO_S << "Executing skill: " << skillName; @@ -119,12 +121,10 @@ namespace armarx updateStatus(SkillStatus::Constructing); this->skill = this->factory.createSkill(providerId); - this->skill->executorName = executorName; - this->skill->manager = skills::manager::dti::SkillManagerInterfacePrx::checkedCast( - callback_interface); // ugly. Get managerPrx from manager! - - this->skill->callback = [&](const SkillStatus s, const armarx::aron::data::DictPtr& d) - { updateStatus(s, d); }; + this->skill->setExecutorName(executorName); + this->skill->setManager(manager); + this->skill->setCallback([&](const SkillStatus s, const armarx::aron::data::DictPtr& d) + { updateStatus(s, d); }); // set initial parameters that were attached to the execution request (only add as we are not sure whether some updates already arrived) skill->updateParameters(initial_aron_params); -- GitLab