diff --git a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt index dbaaf4873bd64b8aede4752f0b8cdc8de3029579..16f091a3ac5ca8ae10f5b812761b179b86efc6ea 100755 --- a/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt +++ b/source/RobotAPI/components/units/RobotUnit/CMakeLists.txt @@ -43,7 +43,9 @@ set(LIB_FILES NJointControllers/NJointController.cpp NJointControllers/NJointTrajectoryController.cpp NJointControllers/NJointKinematicUnitPassThroughController.cpp + NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.cpp NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp + NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.cpp NJointControllers/NJointHolonomicPlatformRelativePositionController.cpp NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp NJointControllers/NJointTCPController.cpp @@ -121,8 +123,11 @@ set(LIB_HEADERS NJointControllers/NJointControllerRegistry.h NJointControllers/NJointControllerWithTripleBuffer.h NJointControllers/NJointTrajectoryController.h + NJointControllers/NJointHolonomicPlatformVelocityControllerTypes.h + NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h NJointControllers/NJointKinematicUnitPassThroughController.h NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h + NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.h NJointControllers/NJointHolonomicPlatformRelativePositionController.h NJointControllers/NJointHolonomicPlatformGlobalPositionController.h NJointControllers/NJointTCPController.h diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.cpp old mode 100755 new mode 100644 diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h old mode 100755 new mode 100644 diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp index bd440c1b0b124795cbe519299bfca7931a64d3bf..892a1ae7e554387630efa35d048ee6cd44f02584 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.cpp @@ -32,8 +32,7 @@ namespace armarx NJointHolonomicPlatformUnitVelocityPassThroughController( RobotUnit* prov, NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr cfg, - const VirtualRobot::RobotPtr&) : - maxCommandDelay(IceUtil::Time::milliSeconds(500)) + const VirtualRobot::RobotPtr&) { target = useControlTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocity) ->asA<ControlTargetHolonomicPlatformVelocity>(); @@ -72,32 +71,6 @@ namespace armarx } } - void - NJointHolonomicPlatformUnitVelocityPassThroughController::setVelocites(float velocityX, - float velocityY, - float velocityRotation) - { - LockGuardType guard{controlDataMutex}; - getWriterControlStruct().velocityX = velocityX; - getWriterControlStruct().velocityY = velocityY; - getWriterControlStruct().velocityRotation = velocityRotation; - getWriterControlStruct().commandTimestamp = IceUtil::Time::now(); - writeControlStruct(); - } - - IceUtil::Time - NJointHolonomicPlatformUnitVelocityPassThroughController::getMaxCommandDelay() const - { - return maxCommandDelay; - } - - void - NJointHolonomicPlatformUnitVelocityPassThroughController::setMaxCommandDelay( - const IceUtil::Time& value) - { - maxCommandDelay = value; - } - NJointControllerRegistration<NJointHolonomicPlatformUnitVelocityPassThroughController> registrationNJointHolonomicPlatformUnitVelocityPassThroughController( "NJointHolonomicPlatformUnitVelocityPassThroughController"); diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h index 37df1f055d7daeb61630e62698585e484ed6cc75..ef08635a5f8329e80020f47b1eb8b49b3a894b7a 100644 --- a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h @@ -24,6 +24,8 @@ #include <VirtualRobot/Robot.h> +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h> + #include "../ControlTargets/ControlTargetHolonomicPlatformVelocity.h" #include "../util.h" #include "NJointControllerWithTripleBuffer.h" @@ -45,24 +47,12 @@ namespace armarx TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformUnitVelocityPassThroughController); - class NJointHolonomicPlatformUnitVelocityPassThroughControllerControlData - { - public: - float velocityX = 0; - float velocityY = 0; - float velocityRotation = 0; - IceUtil::Time commandTimestamp; - }; - - TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformUnitVelocityPassThroughController); - /** * @brief The NJointHolonomicPlatformUnitVelocityPassThroughController class * @ingroup Library-RobotUnit-NJointControllers */ class NJointHolonomicPlatformUnitVelocityPassThroughController : - virtual public NJointControllerWithTripleBuffer< - NJointHolonomicPlatformUnitVelocityPassThroughControllerControlData> + virtual public NJointHolonomicPlatformVelocityControllerInterface { public: using ConfigPtrT = NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr; @@ -74,9 +64,6 @@ namespace armarx void rtRun(const IceUtil::Time&, const IceUtil::Time&) override; - //for the platform unit - void setVelocites(float velocityX, float velocityY, float velocityRotation); - //ice interface std::string getClassName(const Ice::Current& = Ice::emptyCurrent) const override @@ -84,13 +71,8 @@ namespace armarx return "NJointHolonomicPlatformUnitVelocityPassThroughController"; } - IceUtil::Time getMaxCommandDelay() const; - void setMaxCommandDelay(const IceUtil::Time& value); - protected: - IceUtil::Time maxCommandDelay; - ControlTargetHolonomicPlatformVelocity* target; - NJointHolonomicPlatformUnitVelocityPassThroughControllerControlData initialSettings; + NJointHolonomicPlatformVelocityControllerControlData initialSettings; }; } // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0db22289e2d7be967f79ac044a88270c06f4a47f --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.cpp @@ -0,0 +1,63 @@ +/* + * 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/>. + * + * @package RobotAPI::ArmarXObjects::NJointHolonomicPlatformUnitVelocityPassThroughController + * @author Raphael Grimm ( raphael dot grimm at kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "NJointHolonomicPlatformVelocityControllerInterface.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointControllerRegistry.h> + +namespace armarx +{ + NJointHolonomicPlatformVelocityControllerInterface:: + NJointHolonomicPlatformVelocityControllerInterface() : + maxCommandDelay(IceUtil::Time::milliSeconds(500)) + { + } + + void + NJointHolonomicPlatformVelocityControllerInterface::setVelocites(float velocityX, + float velocityY, + float velocityRotation) + { + LockGuardType guard{controlDataMutex}; + getWriterControlStruct().velocityX = velocityX; + getWriterControlStruct().velocityY = velocityY; + getWriterControlStruct().velocityRotation = velocityRotation; + getWriterControlStruct().commandTimestamp = IceUtil::Time::now(); + writeControlStruct(); + } + + IceUtil::Time + NJointHolonomicPlatformVelocityControllerInterface::getMaxCommandDelay() const + { + return maxCommandDelay; + } + + void + NJointHolonomicPlatformVelocityControllerInterface::setMaxCommandDelay( + const IceUtil::Time& value) + { + maxCommandDelay = value; + } + +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h new file mode 100644 index 0000000000000000000000000000000000000000..7a9acbe71cf958b2a1027c639466972221948965 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h @@ -0,0 +1,59 @@ +/* + * 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/>. + * + * @package RobotAPI::NJointHolonomicPlatformVelocityControllerInterface + * @author Tobias Gröger ( tobias dot groeger at student dot kit dot edu ) + * @date 2024 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include "NJointControllerWithTripleBuffer.h" + +namespace armarx +{ + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformVelocityControllerControlData); + + class NJointHolonomicPlatformVelocityControllerControlData + { + public: + float velocityX = 0; + float velocityY = 0; + float velocityRotation = 0; + IceUtil::Time commandTimestamp; + }; + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformVelocityControllerInterface); + + class NJointHolonomicPlatformVelocityControllerInterface : + virtual public NJointControllerWithTripleBuffer< + NJointHolonomicPlatformVelocityControllerControlData> + { + public: + NJointHolonomicPlatformVelocityControllerInterface(); + + + void setVelocites(float velocityX, float velocityY, float velocityRotation); + + IceUtil::Time getMaxCommandDelay() const; + void setMaxCommandDelay(const IceUtil::Time& value); + + protected: + IceUtil::Time maxCommandDelay; + }; +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerTypes.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerTypes.h new file mode 100644 index 0000000000000000000000000000000000000000..c864e700da2ac4c4159893c8099de48e43ee87ae --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerTypes.h @@ -0,0 +1,43 @@ +/* + * 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/>. + * + * @package RobotAPI::NJointHolonomicPlatformVelocityControllerTypes + * @author Tobias Gröger ( tobias dot groeger at student dot kit dot edu ) + * @date 2024 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <SimoxUtility/meta/enum/EnumNames.hpp> + +namespace armarx +{ + enum class NJointHolonomicPlatformVelocityControllerTypes + { + PassThroughController, + ControllerWithRamp + }; + + + inline const simox::meta::EnumNames<NJointHolonomicPlatformVelocityControllerTypes> + NJointHolonomicPlatformVelocityControllerTypesNames{ + {NJointHolonomicPlatformVelocityControllerTypes::PassThroughController, + "PassThroughController"}, + {NJointHolonomicPlatformVelocityControllerTypes::ControllerWithRamp, + "ControllerWithRamp"}}; + +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.cpp b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2e5642d3fb78c95afd81644dec24d4c13f313ff4 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.cpp @@ -0,0 +1,159 @@ +/* + * 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/>. + * + * @package RobotAPI::ArmarXObjects::NJointHolonomicPlatformUnitVelocityPassThroughController + * @author Raphael Grimm ( raphael dot grimm at kit dot edu ) + * @date 2017 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#include "NJointHolonomicPlatformVelocityControllerWithRamp.h" + +#include <ArmarXCore/core/exceptions/local/ExpressionException.h> + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointControllerRegistry.h> + +namespace armarx +{ + NJointHolonomicPlatformVelocityControllerWithRamp:: + NJointHolonomicPlatformVelocityControllerWithRamp(RobotUnit* prov, + ConfigPtrT cfg, + const VirtualRobot::RobotPtr&) : + ramp(cfg->maxPositionAcceleration, cfg->maxOrientationAcceleration) + { + target = useControlTarget(cfg->platformName, ControlModes::HolonomicPlatformVelocity) + ->asA<ControlTargetHolonomicPlatformVelocity>(); + ARMARX_CHECK_EXPRESSION(target) + << "The actuator " << cfg->platformName << " has no control mode " + << ControlModes::HolonomicPlatformVelocity; + + const auto sensor = useSensorValue(cfg->platformName); + ARMARX_CHECK_EXPRESSION(sensor) << "No sensor value for " << cfg->platformName; + velocitySensor = sensor->asA<SensorValueHolonomicPlatformVelocity>(); + ARMARX_CHECK_EXPRESSION(velocitySensor) + << "Sensor value for " << cfg->platformName << " has invalid type"; + } + + void + NJointHolonomicPlatformVelocityControllerWithRamp::rtPreActivateController() + { + activationVelocity(0) = velocitySensor->velocityX; + activationVelocity(1) = velocitySensor->velocityY; + activationVelocity(2) = velocitySensor->velocityRotation; + activationTime = IceUtil::Time::now(); + + // init velocity ramp + ramp.init(activationVelocity); + } + + void + NJointHolonomicPlatformVelocityControllerWithRamp::rtRun( + const IceUtil::Time& sensorValuesTimestamp, + const IceUtil::Time& timeSinceLastIteration) + { + auto commandAge = sensorValuesTimestamp - rtGetControlStruct().commandTimestamp; + + if (commandAge > maxCommandDelay && // command must be recent + (rtGetControlStruct().velocityX != 0.0f || rtGetControlStruct().velocityY != 0.0f || + rtGetControlStruct().velocityRotation != + 0.0f)) // only throw error if any command is not zero + { + throw LocalException( + "Platform target velocity was not set for a too long time: delay: ") + << commandAge.toSecondsDouble() + << " s, max allowed delay: " << maxCommandDelay.toSecondsDouble() << " s"; + } + else + { + Eigen::Vector3f result; + if (activationTime > rtGetControlStruct().commandTimestamp) + { + result = ramp.update(activationVelocity, timeSinceLastIteration.toSecondsDouble()); + } + else + { + Eigen::Vector3f x(rtGetControlStruct().velocityX, + rtGetControlStruct().velocityY, + rtGetControlStruct().velocityRotation); + result = ramp.update(x, timeSinceLastIteration.toSecondsDouble()); + } + + target->velocityX = result(0); + target->velocityY = result(1); + target->velocityRotation = result(2); + } + } + + void + NJointHolonomicPlatformVelocityControllerWithRamp::setMaxAccelerations( + float maxPositionAcceleration, + float maxOrientationAcceleration) + { + ramp.setMaxPositionAcceleration(maxPositionAcceleration); + ramp.setMaxOrientationAcceleration(maxOrientationAcceleration); + } + + NJointControllerRegistration<NJointHolonomicPlatformVelocityControllerWithRamp> + registrationNJointHolonomicPlatformVelocityControllerWithRamp( + "NJointHolonomicPlatformVelocityControllerWithRamp"); + + Cartesian2DimVelocityRamp ::Cartesian2DimVelocityRamp(float maxPositionAcceleration, + float maxOrientationAcceleration) : + maxPositionAcceleration(maxPositionAcceleration), + maxOrientationAcceleration(maxOrientationAcceleration) + { + } + + void + Cartesian2DimVelocityRamp::init(const Eigen::Vector3f& state) + { + this->state = state; + } + + Eigen::Vector3f + Cartesian2DimVelocityRamp::update(const Eigen::Vector3f& target, float dt) + { + if (dt <= 0) + { + return state; + } + Eigen::Vector3f delta = target - state; + float factor = 1; + + Eigen::Vector2f posDelta = delta.block<2, 1>(0, 0); + float posFactor = posDelta.norm() / maxPositionAcceleration / dt; + factor = std::max(factor, posFactor); + + float oriFactor = std::abs(delta(2)) / maxOrientationAcceleration / dt; + factor = std::max(factor, oriFactor); + + state += delta / factor; + return state; + } + + void + Cartesian2DimVelocityRamp::setMaxPositionAcceleration(float maxPositionAcceleration) + { + this->maxPositionAcceleration = maxPositionAcceleration; + } + + void + Cartesian2DimVelocityRamp::setMaxOrientationAcceleration(float maxOrientationAcceleration) + { + this->maxOrientationAcceleration = maxOrientationAcceleration; + } + +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.h b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.h new file mode 100644 index 0000000000000000000000000000000000000000..8cd957b50d7911524c6e5d68a91ad4359213f145 --- /dev/null +++ b/source/RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.h @@ -0,0 +1,102 @@ +/* + * 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/>. + * + * @package RobotAPI::NJointHolonomicPlatformVelocityControllerInterface + * @author Tobias Gröger ( tobias dot groeger at student dot kit dot edu ) + * @date 2024 + * @copyright http://www.gnu.org/licenses/gpl-2.0.txt + * GNU General Public License + */ + +#pragma once + +#include <VirtualRobot/Robot.h> + +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h> + +#include "../ControlTargets/ControlTargetHolonomicPlatformVelocity.h" +#include "NJointControllerWithTripleBuffer.h" + +namespace armarx +{ + + class Cartesian2DimVelocityRamp + { + public: + Cartesian2DimVelocityRamp(float maxPositionAcceleration, float maxOrientationAcceleration); + void init(const Eigen::Vector3f& state); + + Eigen::Vector3f update(const Eigen::Vector3f& target, float dt); + + void setMaxPositionAcceleration(float maxPositionAcceleration); + void setMaxOrientationAcceleration(float maxOrientationAcceleration); + + private: + float maxPositionAcceleration = 0; + float maxOrientationAcceleration = 0; + + Eigen::Vector3f state; // [velX,velY,velRotation] + }; + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformVelocityControllerWithRampConfig); + + class NJointHolonomicPlatformVelocityControllerWithRampConfig : + virtual public NJointControllerConfig + { + public: + std::string platformName; + + float maxPositionAcceleration; + float maxOrientationAcceleration; + }; + + TYPEDEF_PTRS_HANDLE(NJointHolonomicPlatformVelocityControllerWithRamp); + + class NJointHolonomicPlatformVelocityControllerWithRamp : + virtual public NJointHolonomicPlatformVelocityControllerInterface + { + public: + using ConfigPtrT = NJointHolonomicPlatformVelocityControllerWithRampConfigPtr; + + NJointHolonomicPlatformVelocityControllerWithRamp(RobotUnit* prov, + ConfigPtrT config, + const VirtualRobot::RobotPtr&); + + void rtRun(const IceUtil::Time& sensorValuesTimestamp, + const IceUtil::Time& timeSinceLastIteration) override; + + + void setMaxAccelerations(float maxPositionAcceleration, float maxOrientationAcceleration); + + //ice interface + std::string + getClassName(const Ice::Current& = Ice::emptyCurrent) const override + { + return "NJointHolonomicPlatformVelocityControllerWithRamp"; + } + + protected: + void rtPreActivateController() override; + + protected: + Cartesian2DimVelocityRamp ramp; + + Eigen::Vector3f activationVelocity; + IceUtil::Time activationTime; + + ControlTargetHolonomicPlatformVelocity* target; + const SensorValueHolonomicPlatformVelocity* velocitySensor; + }; +} // namespace armarx diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp index 16af4f8c064391718d3aef43efac649030d41ed8..34168e9a242f8c43da13eb43e2a809594ebbc226 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.cpp @@ -28,6 +28,8 @@ #include "RobotAPI/components/units/RobotUnit/SensorValues/SensorValueHolonomicPlatform.h" #include "RobotAPI/components/units/RobotUnit/Units/LocalizationSubUnit.h" #include <RobotAPI/components/units/RobotUnit/Devices/GlobalRobotPoseSensorDevice.h> +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h> +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerWithRamp.h> #include "../RobotUnit.h" #include "../SensorValues/SensorValue1DoFActuator.h" @@ -449,23 +451,62 @@ namespace armarx::RobotUnitModule Component::create<UnitT>(properties, configName, getConfigDomain()); //config ARMARX_TRACE; - NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr config = - new NJointHolonomicPlatformUnitVelocityPassThroughControllerConfig; - config->initialVelocityX = 0; - config->initialVelocityY = 0; - config->initialVelocityRotation = 0; - config->platformName = _module<RobotData>().getRobotPlatformName(); - auto ctrl = NJointHolonomicPlatformUnitVelocityPassThroughControllerPtr::dynamicCast( - _module<ControllerManagement>().createNJointController( - "NJointHolonomicPlatformUnitVelocityPassThroughController", - getName() + "_" + configName + "_VelPTContoller", - config, - false, - true)); - ARMARX_TRACE; - ARMARX_CHECK_EXPRESSION(ctrl); - unit->pt = ctrl; - ARMARX_TRACE; + const NJointHolonomicPlatformVelocityControllerTypes platformControllerType = + getProperty<NJointHolonomicPlatformVelocityControllerTypes>( + "PlatformUnitVelocityControllerType"); + if (platformControllerType == + NJointHolonomicPlatformVelocityControllerTypes::PassThroughController) + { + NJointHolonomicPlatformUnitVelocityPassThroughControllerConfigPtr config = + new NJointHolonomicPlatformUnitVelocityPassThroughControllerConfig; + config->initialVelocityX = 0; + config->initialVelocityY = 0; + config->initialVelocityRotation = 0; + config->platformName = _module<RobotData>().getRobotPlatformName(); + + auto ctrl = NJointHolonomicPlatformUnitVelocityPassThroughControllerPtr::dynamicCast( + _module<ControllerManagement>().createNJointController( + "NJointHolonomicPlatformUnitVelocityPassThroughController", + getName() + "_" + configName + "_VelPTContoller", + config, + false, + true)); + ARMARX_TRACE; + ARMARX_CHECK_EXPRESSION(ctrl); + unit->pt = ctrl; + ARMARX_TRACE; + } + else if (platformControllerType == + NJointHolonomicPlatformVelocityControllerTypes::ControllerWithRamp) + { + NJointHolonomicPlatformVelocityControllerWithRampConfigPtr config = + new NJointHolonomicPlatformVelocityControllerWithRampConfig; + config->platformName = _module<RobotData>().getRobotPlatformName(); + + config->maxPositionAcceleration = + getProperty<float>("PlatformUnitMaximumPositionAcceleration"); + config->maxOrientationAcceleration = + getProperty<float>("PlatformUnitMaximumOrientationAcceleration"); + + auto ctrl = NJointHolonomicPlatformVelocityControllerWithRampPtr::dynamicCast( + _module<ControllerManagement>().createNJointController( + "NJointHolonomicPlatformVelocityControllerWithRamp", + getName() + "_" + configName + "_VelPTContoller", + config, + false, + true)); + ARMARX_TRACE; + ARMARX_CHECK_EXPRESSION(ctrl); + unit->pt = ctrl; + ARMARX_TRACE; + } + else + { + ARMARX_ERROR << "Invalid Platform velocity controller specified '" + << NJointHolonomicPlatformVelocityControllerTypesNames.to_name( + platformControllerType) + << "'"; + } NJointHolonomicPlatformRelativePositionControllerConfigPtr configRelativePositionCtrlCfg = new NJointHolonomicPlatformRelativePositionControllerConfig; @@ -479,7 +520,6 @@ namespace armarx::RobotUnitModule false, true)); ARMARX_CHECK_EXPRESSION(ctrlRelativePosition); - unit->pt = ctrl; unit->relativePosCtrl = ctrlRelativePosition; // unit->platformSensorIndex = _module<Devices>().getSensorDevices().index(_module<RobotData>().getRobotPlatformName()); @@ -496,7 +536,6 @@ namespace armarx::RobotUnitModule true)); ARMARX_TRACE; ARMARX_CHECK_EXPRESSION(ctrlGlobalPosition); - unit->pt = ctrl; unit->globalPosCtrl = ctrlGlobalPosition; ARMARX_TRACE; diff --git a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h index 6a423722690a411ab270477bcacd387118ba8d61..f33c7ee47065d3bb7c5f1302d806d3af8229920a 100644 --- a/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h +++ b/source/RobotAPI/components/units/RobotUnit/RobotUnitModules/RobotUnitModuleUnits.h @@ -22,6 +22,7 @@ #pragma once +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerTypes.h> #include <RobotAPI/interface/units/RobotUnit/RobotUnitInterface.h> #include "../Units/RobotUnitSubUnit.h" @@ -43,6 +44,25 @@ namespace armarx::RobotUnitModule defineOptionalProperty<std::string>( "PlatformUnitName", "PlatformUnit", "The name of the created platform unit"); + auto& types = defineOptionalProperty<NJointHolonomicPlatformVelocityControllerTypes>( + "PlatformUnitVelocityControllerType", + NJointHolonomicPlatformVelocityControllerTypes::PassThroughController, + "Which controller to use for velocity control of the platform"); + for (const auto& [type, name] : + NJointHolonomicPlatformVelocityControllerTypesNames.map()) + { + types.map(name, type); + } + + defineOptionalProperty<float>( + "PlatformUnitMaximumPositionAcceleration", + 800, + "The maximum allowed acceleration for the position of the platform"); + defineOptionalProperty<float>( + "PlatformUnitMaximumOrientationAcceleration", + 80, + "The maximum allowed acceleration for the orientation of the platform"); + defineOptionalProperty<std::string>( "ForceTorqueUnitName", "ForceTorqueUnit", diff --git a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h index 90d5443bfd71a795ba244f650ae97b24988359e2..dd41099f1f79411eb23fcd7e484b9c57b7cd42a8 100755 --- a/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h +++ b/source/RobotAPI/components/units/RobotUnit/Units/PlatformSubUnit.h @@ -31,10 +31,10 @@ #include <RobotAPI/components/units/PlatformUnit.h> #include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformGlobalPositionController.h> #include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformRelativePositionController.h> +#include <RobotAPI/components/units/RobotUnit/NJointControllers/NJointHolonomicPlatformVelocityControllerInterface.h> #include <RobotAPI/interface/core/RobotState.h> #include <RobotAPI/libraries/core/Pose.h> -#include "../NJointControllers/NJointHolonomicPlatformUnitVelocityPassThroughController.h" #include "../SensorValues/SensorValueHolonomicPlatform.h" #include "RobotUnitSubUnit.h" @@ -93,7 +93,7 @@ namespace armarx void stopPlatform(const Ice::Current& c = Ice::emptyCurrent) override; - NJointHolonomicPlatformUnitVelocityPassThroughControllerPtr pt; + NJointHolonomicPlatformVelocityControllerInterfacePtr pt; NJointHolonomicPlatformRelativePositionControllerPtr relativePosCtrl; NJointHolonomicPlatformGlobalPositionControllerPtr globalPosCtrl; diff --git a/source/RobotAPI/interface/skills/SkillManagerInterface.ice b/source/RobotAPI/interface/skills/SkillManagerInterface.ice index 06dda7ed51718dff0061ea71682188aaa22113cb..b6acfc402a85a5fdc016aa84a0ce37fdb37ba003 100644 --- a/source/RobotAPI/interface/skills/SkillManagerInterface.ice +++ b/source/RobotAPI/interface/skills/SkillManagerInterface.ice @@ -154,6 +154,16 @@ module armarx FluxioValueList values; }; + struct FluxioEdge + { + FluxioIdentificator fromNodeId; + FluxioIdentificator fromParameterId; + FluxioIdentificator toNodeId; + FluxioIdentificator toParameterId; + }; + + sequence<FluxioParameter> FluxioParameterList; + struct FluxioNode { string nodeType; // decides which extended attributes are used @@ -162,18 +172,11 @@ module armarx float xPos; float yPos; FluxioIdentificator parameterId; // empty id field if node is not a parameter node - FluxioIdentificator skillId; // empty id field if node is not a subskill node + FluxioIdentificator skillId; // empty id field if node is not a subskill node + string controlType; // empty string if node is not a control node + FluxioParameterList parameters; // empty list if node is not a control node }; - struct FluxioEdge - { - FluxioIdentificator fromNodeId; - FluxioIdentificator fromParameterId; - FluxioIdentificator toNodeId; - FluxioIdentificator toParameterId; - }; - - sequence<FluxioParameter> FluxioParameterList; sequence<FluxioNode> FluxioNodeList; sequence<FluxioEdge> FluxioEdgeList; diff --git a/source/RobotAPI/libraries/aron/core/aron_conversions.h b/source/RobotAPI/libraries/aron/core/aron_conversions.h index 9d067df7ed847624e6a6d07f9ececfe3669ee88d..30f2e949b575e22bf631abc06ca1c4ba8172efaf 100644 --- a/source/RobotAPI/libraries/aron/core/aron_conversions.h +++ b/source/RobotAPI/libraries/aron/core/aron_conversions.h @@ -124,6 +124,7 @@ namespace detail // std::optional template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void toAron(std::optional<DtoT>& dto, const std::optional<BoT>& bo) { if (bo.has_value()) @@ -138,6 +139,7 @@ namespace detail } template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void fromAron(const std::optional<DtoT>& dto, std::optional<BoT>& bo) { if (dto.has_value()) @@ -230,6 +232,7 @@ namespace detail // std::vector template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void toAron(std::vector<DtoT>& dtos, const std::vector<BoT>& bos) { dtos.clear(); @@ -240,6 +243,7 @@ namespace detail } } template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void fromAron(const std::vector<DtoT>& dtos, std::vector<BoT>& bos) { bos.clear(); @@ -339,11 +343,13 @@ namespace armarx // std::optional template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void toAron(std::optional<DtoT>& dto, const std::optional<BoT>& bo) { armarx::aron::toAron(dto, bo); } template <class DtoT, class BoT> + requires (!aron::detail::DtoAndBoAreSame<DtoT, BoT>) void fromAron(const std::optional<DtoT>& dto, std::optional<BoT>& bo) { armarx::aron::fromAron(dto, bo); diff --git a/source/RobotAPI/libraries/skills/core/CMakeLists.txt b/source/RobotAPI/libraries/skills/core/CMakeLists.txt index 7787efe2428e0388d1084585081dc7faed18d333..a45f1f23191b733097856ab30d6292c42c2d1b31 100644 --- a/source/RobotAPI/libraries/skills/core/CMakeLists.txt +++ b/source/RobotAPI/libraries/skills/core/CMakeLists.txt @@ -15,6 +15,10 @@ armarx_add_library( SOURCES error/Exception.cpp + executor/FluxioExecutor.cpp + executor/FluxioNativeExecutor.cpp + executor/FluxioCompositeExecutor.cpp + executor/FluxioMergerExecutor.cpp error/FluxioException.cpp error/FluxioErrorMessages.cpp SkillID.cpp @@ -35,6 +39,7 @@ armarx_add_library( FluxioNode.cpp FluxioEdge.cpp FluxioSkill.cpp + FluxioControlNode.cpp FluxioParameterNode.cpp FluxioSubSkillNode.cpp FluxioSkillStatusUpdate.cpp @@ -44,6 +49,10 @@ armarx_add_library( error/Exception.h error/FluxioErrorMessages.h FluxioResult.h + executor/FluxioExecutor.h + executor/FluxioNativeExecutor.h + executor/FluxioCompositeExecutor.h + executor/FluxioMergerExecutor.h SkillID.h ProviderID.h ProviderInfo.h @@ -62,6 +71,7 @@ armarx_add_library( FluxioNode.h FluxioEdge.h FluxioSkill.h + FluxioControlNode.h FluxioParameterNode.h FluxioSubSkillNode.h FluxioSkillStatusUpdate.h diff --git a/source/RobotAPI/libraries/skills/core/FluxioControlNode.cpp b/source/RobotAPI/libraries/skills/core/FluxioControlNode.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f4cd5ee9285fb8dc2bf21d6f6dc29d8fd5c5bcb1 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/FluxioControlNode.cpp @@ -0,0 +1,115 @@ +#include "FluxioControlNode.h" + +#include <optional> + +#include <ArmarXCore/core/logging/Logging.h> + +#include "RobotAPI/libraries/skills/core/FluxioNode.h" +#include <RobotAPI/interface/skills/SkillManagerInterface.h> + +#include "FluxioParameter.h" + +namespace armarx +{ + namespace skills + { + FluxioControlNodeType + FluxioControlNodeTypeFromString(const std::string& type) + { + if (type == "SPLITTER") + { + return FluxioControlNodeType::SPLITTER; + } + else if (type == "AND_MERGER") + { + return FluxioControlNodeType::AND_MERGER; + } + else + { + ARMARX_WARNING << "Unknown control node type: " << type; + return FluxioControlNodeType::UNKNOWN; + } + } + + std::optional<std::string> + FluxioControlNodeTypeToString(const FluxioControlNodeType& type) + { + switch (type) + { + case FluxioControlNodeType::SPLITTER: + return "SPLITTER"; + case FluxioControlNodeType::AND_MERGER: + return "AND_MERGER"; + case FluxioControlNodeType::UNKNOWN: + default: + ARMARX_WARNING << "Unknown control node type: " << static_cast<int>(type); + return std::nullopt; + } + } + + std::optional<manager::dto::FluxioNode> + FluxioControlNode::toManagerIce() const + { + const auto& nt = FluxioNodeTypeToString(nodeType); + if (!nt.has_value()) + { + return std::nullopt; + } + const auto& ct = skills::FluxioControlNodeTypeToString(controlType); + if (!nt.has_value()) + { + return std::nullopt; + } + + manager::dto::FluxioNode ret; + ret.nodeId = nodeId; + ret.nodeType = nt.value(); + ret.name = name; + ret.xPos = xPos; + ret.yPos = yPos; + ret.controlType = ct.value(); + + + manager::dto::FluxioParameterList params; + for (const auto& [id, parameter] : parametersMap) + { + params.push_back(parameter.toManagerIce()); + } + ret.parameters = params; + + manager::dto::FluxioIdentificator emptyId; + emptyId.id = ""; + emptyId.hint = ""; + + ret.parameterId = emptyId; + ret.skillId = emptyId; + + return ret; + } + + std::optional<FluxioControlNode> + FluxioControlNode::FromIce(const manager::dto::FluxioNode& i, + std::map<std::string, FluxioProfile>& profilesMap) + { + FluxioControlNode ret; + + ret.nodeId = i.nodeId; + ret.nodeType = FluxioNodeTypeFromString(i.nodeType); + ret.name = i.name; + ret.xPos = i.xPos; + ret.yPos = i.yPos; + ret.controlType = skills::FluxioControlNodeTypeFromString(i.controlType); + + std::map<std::string, FluxioParameter> paramsMap; + for (const auto& parameter : i.parameters) + { + const auto& param = FluxioParameter::FromIce(parameter, profilesMap); + paramsMap.insert({parameter.id, param}); + } + + ret.parametersMap = paramsMap; + + return ret; + } + } // namespace skills +} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/core/FluxioControlNode.h b/source/RobotAPI/libraries/skills/core/FluxioControlNode.h new file mode 100644 index 0000000000000000000000000000000000000000..102ed2d5d2bd6ced5894a0aaf8c3b583030fafb7 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/FluxioControlNode.h @@ -0,0 +1,35 @@ +#pragma once + +#include <RobotAPI/interface/skills/SkillManagerInterface.h> + +#include "FluxioNode.h" +#include "FluxioParameter.h" + +namespace armarx +{ + namespace skills + { + + enum class FluxioControlNodeType + { + UNKNOWN = 0, + SPLITTER = 1, + AND_MERGER = 2, + }; + + struct FluxioControlNode : public FluxioNode + { + FluxioControlNodeType controlType; + std::map<std::string, FluxioParameter> parametersMap; + + std::optional<manager::dto::FluxioNode> toManagerIce() const override; + + static std::optional<FluxioControlNode> + FromIce(const manager::dto::FluxioNode& i, + std::map<std::string, FluxioProfile>& profilesMap); + FluxioControlNodeType FluxioControlNodeTypeFromString(const std::string& type); + std::optional<std::string> + FluxioControlNodeTypeToString(const FluxioControlNodeType& type); + }; + } // namespace skills +} // namespace armarx diff --git a/source/RobotAPI/libraries/skills/core/FluxioEdge.cpp b/source/RobotAPI/libraries/skills/core/FluxioEdge.cpp index 1dba082002933b9b732bcb9bc2f9250266359cbc..d81b3f59eb689998a6a9253a4d5908766837c1bb 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioEdge.cpp +++ b/source/RobotAPI/libraries/skills/core/FluxioEdge.cpp @@ -4,6 +4,8 @@ #include <ArmarXCore/core/logging/Logging.h> +#include "RobotAPI/libraries/skills/core/FluxioControlNode.h" + #include "FluxioNode.h" #include "FluxioParameter.h" #include "FluxioSubSkillNode.h" @@ -52,7 +54,7 @@ namespace armarx skills::FluxioParameter* toParameterPtr = nullptr; // Check if fromNode is a SubSkillNode - if (fromNodePtr->nodeType == "SUBSKILL") + if (fromNodePtr->nodeType == FluxioNodeType::SUBSKILL) { const auto* subSkillNodePtr = dynamic_cast<FluxioSubSkillNode*>(fromNodePtr); @@ -68,15 +70,31 @@ namespace armarx return std::nullopt; } } - else + else if (fromNodePtr->nodeType == FluxioNodeType::PARAMETER) { fromParameterPtr = FluxioParameter::FromFluxioIdentificatorIce(i.fromParameterId, parametersMap); } + else if (fromNodePtr->nodeType == FluxioNodeType::CONTROL) + { + auto* controlNodePtr = dynamic_cast<FluxioControlNode*>(fromNodePtr); + if (controlNodePtr == nullptr) + { + ARMARX_WARNING << "Failed to cast node to control node"; + return std::nullopt; + } + fromParameterPtr = FluxioParameter::FromFluxioIdentificatorIce( + i.fromParameterId, controlNodePtr->parametersMap); + } + else + { + ARMARX_WARNING << "Unknown from node type"; + return std::nullopt; + } // Check if toNode is a SubSkillNode - if (toNodePtr->nodeType == "SUBSKILL") + if (toNodePtr->nodeType == FluxioNodeType::SUBSKILL) { const auto* subSkillNodePtr = dynamic_cast<FluxioSubSkillNode*>(toNodePtr); @@ -92,11 +110,28 @@ namespace armarx return std::nullopt; } } - else + else if (toNodePtr->nodeType == FluxioNodeType::CONTROL) + { + auto* controlNodePtr = dynamic_cast<FluxioControlNode*>(toNodePtr); + if (controlNodePtr == nullptr) + { + ARMARX_WARNING << "Failed to cast node to control node"; + return std::nullopt; + } + + toParameterPtr = FluxioParameter::FromFluxioIdentificatorIce( + i.toParameterId, controlNodePtr->parametersMap); + } + else if (toNodePtr->nodeType == FluxioNodeType::PARAMETER) { toParameterPtr = FluxioParameter::FromFluxioIdentificatorIce(i.toParameterId, parametersMap); } + else + { + ARMARX_WARNING << "Unknown to node type"; + return std::nullopt; + } if (fromParameterPtr == nullptr || toParameterPtr == nullptr) { diff --git a/source/RobotAPI/libraries/skills/core/FluxioNode.cpp b/source/RobotAPI/libraries/skills/core/FluxioNode.cpp index 814c6b0c1731e7a77dcf434b0259b2e1b452c125..68601ff86ab6bdbc14ed1346acfde108e153153f 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioNode.cpp +++ b/source/RobotAPI/libraries/skills/core/FluxioNode.cpp @@ -8,26 +8,71 @@ namespace armarx { namespace skills { + FluxioNodeType + FluxioNodeTypeFromString(const std::string& type) + { + if (type == "PARAMETER") + { + return FluxioNodeType::PARAMETER; + } + else if (type == "CONTROL") + { + return FluxioNodeType::CONTROL; + } + else if (type == "SUBSKILL") + { + return FluxioNodeType::SUBSKILL; + } + else + { + ARMARX_WARNING << "Unknown node type: " << type; + return FluxioNodeType::UNKNOWN; + } + } + + std::optional<std::string> + FluxioNodeTypeToString(const FluxioNodeType& type) + { + switch (type) + { + case FluxioNodeType::PARAMETER: + return "PARAMETER"; + case FluxioNodeType::CONTROL: + return "CONTROL"; + case FluxioNodeType::SUBSKILL: + return "SUBSKILL"; + case FluxioNodeType::UNKNOWN: + default: + ARMARX_WARNING << "Unknown node type: " << static_cast<int>(type); + return std::nullopt; + } + } + std::optional<manager::dto::FluxioNode> FluxioNode::toManagerIce() const { manager::dto::FluxioNode ret; - ret.nodeType = nodeType; + const auto& nt = FluxioNodeTypeToString(nodeType); + if (!nt.has_value()) + { + return std::nullopt; + } + + ret.nodeType = nt.value(); ret.nodeId = nodeId; ret.name = name; ret.xPos = xPos; ret.yPos = yPos; + ret.controlType = ""; + ret.parameters = {}; - manager::dto::FluxioIdentificator paramId; - paramId.id = ""; - paramId.hint = ""; - ret.parameterId = paramId; + manager::dto::FluxioIdentificator emptyId; + emptyId.id = ""; + emptyId.hint = ""; - manager::dto::FluxioIdentificator subSId; - subSId.id = ""; - subSId.hint = ""; - ret.skillId = subSId; + ret.parameterId = emptyId; + ret.skillId = emptyId; return ret; } @@ -61,7 +106,7 @@ namespace armarx { FluxioNode ret; - ret.nodeType = i.nodeType; + ret.nodeType = FluxioNodeTypeFromString(i.nodeType); ret.nodeId = i.nodeId; ret.xPos = i.xPos; ret.yPos = i.yPos; diff --git a/source/RobotAPI/libraries/skills/core/FluxioNode.h b/source/RobotAPI/libraries/skills/core/FluxioNode.h index 8d2264ef312ed8b2c29d24027215cb26428a1fa6..d1594664ee768f3fe06ff4df918c9ef782641fef 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioNode.h +++ b/source/RobotAPI/libraries/skills/core/FluxioNode.h @@ -1,7 +1,7 @@ #pragma once -#include <string> #include <optional> +#include <string> #include <RobotAPI/interface/skills/SkillManagerInterface.h> @@ -9,10 +9,18 @@ namespace armarx { namespace skills { + enum class FluxioNodeType + { + UNKNOWN = 0, + PARAMETER = 1, + CONTROL = 2, + SUBSKILL = 3, + }; + struct FluxioNode { virtual ~FluxioNode() = default; - std::string nodeType; + FluxioNodeType nodeType; std::string nodeId; std::string name; float xPos = 0; @@ -26,5 +34,8 @@ namespace armarx std::map<std::string, FluxioNode*>& nodesMap); static FluxioNode FromIce(const manager::dto::FluxioNode& i); }; + + FluxioNodeType FluxioNodeTypeFromString(const std::string& type); + std::optional<std::string> FluxioNodeTypeToString(const FluxioNodeType& type); } // namespace skills } // namespace armarx diff --git a/source/RobotAPI/libraries/skills/core/FluxioParameter.h b/source/RobotAPI/libraries/skills/core/FluxioParameter.h index 124d8690ea5fe63982c8342c943083d65eb45982..d6e1c2d41669905f3906bd39bc5a0cdd6df6c164 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioParameter.h +++ b/source/RobotAPI/libraries/skills/core/FluxioParameter.h @@ -19,7 +19,7 @@ namespace armarx std::string type; bool required = true; bool isInput; - std::list<FluxioValue> values = {}; + std::list<FluxioValue> values; manager::dto::FluxioParameter toManagerIce() const; manager::dto::FluxioIdentificator toFluxioIdentificatorIce() const; diff --git a/source/RobotAPI/libraries/skills/core/FluxioParameterNode.cpp b/source/RobotAPI/libraries/skills/core/FluxioParameterNode.cpp index b88dfcf2188f68ca657de8c18f56970c1bf34878..a814d9380dbbc55ad7c593c64bdb0c406c2d756f 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioParameterNode.cpp +++ b/source/RobotAPI/libraries/skills/core/FluxioParameterNode.cpp @@ -7,6 +7,7 @@ #include <RobotAPI/interface/skills/SkillManagerInterface.h> #include "FluxioParameter.h" +#include "RobotAPI/libraries/skills/core/FluxioNode.h" namespace armarx { @@ -21,9 +22,15 @@ namespace armarx return std::nullopt; } + const auto& nt = FluxioNodeTypeToString(nodeType); + if(!nt.has_value()) + { + return std::nullopt; + } + manager::dto::FluxioNode ret; ret.nodeId = nodeId; - ret.nodeType = nodeType; + ret.nodeType = nt.value(); ret.name = name; ret.xPos = xPos; ret.yPos = yPos; @@ -57,7 +64,7 @@ namespace armarx FluxioParameterNode ret; ret.nodeId = i.nodeId; - ret.nodeType = i.nodeType; + ret.nodeType = FluxioNodeTypeFromString(i.nodeType); ret.name = i.name; ret.xPos = i.xPos; ret.yPos = i.yPos; diff --git a/source/RobotAPI/libraries/skills/core/FluxioSkill.cpp b/source/RobotAPI/libraries/skills/core/FluxioSkill.cpp index 7678016c6b0382585c007eb39069a5fb7792b2f5..b67f7e34b054a698c46717a22c869e5d3f14c7a7 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioSkill.cpp +++ b/source/RobotAPI/libraries/skills/core/FluxioSkill.cpp @@ -10,6 +10,7 @@ #include "FluxioEdge.h" #include "FluxioNode.h" #include "FluxioParameterNode.h" +#include "FluxioControlNode.h" #include "FluxioSubSkillNode.h" namespace armarx @@ -172,7 +173,7 @@ namespace armarx nodes.clear(); // create all nodes anew for (const manager::dto::FluxioNode& node : i.nodes) { - FluxioNode* n = CreateNode(node, parameters, skillsMap); + FluxioNode* n = CreateNode(node, parameters, skillsMap, profilesMap); if (n != nullptr) { @@ -223,7 +224,7 @@ namespace armarx for (const auto& [nodeId, nodePtr] : nodes) { // TODO: check nodePtr for nullptr - if (nodePtr->nodeType != "SUBSKILL") + if (nodePtr->nodeType != FluxioNodeType::SUBSKILL) { continue; } @@ -318,7 +319,7 @@ namespace armarx { for (const manager::dto::FluxioNode& node : i.nodes) { - FluxioNode* n = CreateNode(node, ret.parameters, skillsMap); + FluxioNode* n = CreateNode(node, ret.parameters, skillsMap, profilesMap); if (n != nullptr) { @@ -346,9 +347,12 @@ namespace armarx skills::FluxioNode* FluxioSkill::CreateNode(const manager::dto::FluxioNode& i, std::map<std::string, FluxioParameter>& parametersMap, - std::map<std::string, FluxioSkill>& skillsMap) + std::map<std::string, FluxioSkill>& skillsMap, + std::map<std::string, FluxioProfile>& profilesMap) { - if (i.nodeType == "PARAMETER") + FluxioNodeType nodeType = FluxioNodeTypeFromString(i.nodeType); + + if (nodeType == FluxioNodeType::PARAMETER) { const auto& n = FluxioParameterNode::FromIce(i, parametersMap); @@ -359,7 +363,7 @@ namespace armarx ARMARX_WARNING << "ParameterNode with id " << i.nodeId << " could not be converted"; } - else if (i.nodeType == "SUBSKILL") + else if (nodeType == FluxioNodeType::SUBSKILL) { const auto& n = FluxioSubSkillNode::FromIce(i, skillsMap); @@ -369,6 +373,20 @@ namespace armarx } ARMARX_WARNING << "SubSkillNode with id " << i.nodeId << " could not be converted"; } + else if (nodeType == FluxioNodeType::CONTROL) + { + const auto& n = FluxioControlNode::FromIce(i, profilesMap); + + if (n.has_value()) + { + return new FluxioControlNode(*n); + } + ARMARX_WARNING << "controlNode with id " << i.nodeId << " could not be converted"; + } + else if (nodeType == FluxioNodeType::UNKNOWN) + { + ARMARX_WARNING << "Node with id " << i.nodeId << " has unknown type"; + } else { ARMARX_INFO << "Node type " << i.nodeType << " not supported yet. Ignoring."; diff --git a/source/RobotAPI/libraries/skills/core/FluxioSkill.h b/source/RobotAPI/libraries/skills/core/FluxioSkill.h index d9a9f7e9b9926ae068edc680b266c7d16ed393c1..791357933d8d1fcadc053cf7ad0f6d5ad46af56b 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioSkill.h +++ b/source/RobotAPI/libraries/skills/core/FluxioSkill.h @@ -1,8 +1,8 @@ #pragma once #include <list> -#include <string> #include <optional> +#include <string> #include <RobotAPI/interface/skills/SkillManagerInterface.h> @@ -48,7 +48,8 @@ namespace armarx static skills::FluxioNode* CreateNode(const manager::dto::FluxioNode& i, std::map<std::string, FluxioParameter>& parametersMap, - std::map<std::string, FluxioSkill>& skillsMap); + std::map<std::string, FluxioSkill>& skillsMap, + std::map<std::string, FluxioProfile>& profilesMap); }; } // namespace skills } // namespace armarx diff --git a/source/RobotAPI/libraries/skills/core/FluxioSubSkillNode.cpp b/source/RobotAPI/libraries/skills/core/FluxioSubSkillNode.cpp index 529406a5c7c471d145150b16183c92538900b168..33e81216ff527af83dd16d45f4357aa763bb6f00 100644 --- a/source/RobotAPI/libraries/skills/core/FluxioSubSkillNode.cpp +++ b/source/RobotAPI/libraries/skills/core/FluxioSubSkillNode.cpp @@ -5,6 +5,7 @@ #include <RobotAPI/interface/skills/SkillManagerInterface.h> #include "FluxioSkill.h" +#include "RobotAPI/libraries/skills/core/FluxioNode.h" namespace armarx { @@ -19,9 +20,15 @@ namespace armarx return std::nullopt; } + const auto& nt = FluxioNodeTypeToString(nodeType); + if (!nt.has_value()) + { + return std::nullopt; + } + manager::dto::FluxioNode ret; ret.nodeId = nodeId; - ret.nodeType = nodeType; + ret.nodeType = nt.value(); ret.name = name; ret.xPos = xPos; ret.yPos = yPos; @@ -56,7 +63,7 @@ namespace armarx FluxioSubSkillNode ret; ret.nodeId = i.nodeId; - ret.nodeType = i.nodeType; + ret.nodeType = FluxioNodeTypeFromString(i.nodeType); ret.name = i.name; ret.xPos = i.xPos; ret.yPos = i.yPos; diff --git a/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.cpp b/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d60649b2fe5e301264c5f5cc7fcba1ab58be9b6d --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.cpp @@ -0,0 +1,650 @@ +#include "FluxioCompositeExecutor.h" + +#include <algorithm> +#include <optional> +#include <string> +#include <thread> +#include <vector> + +#include <IceUtil/UUID.h> + +#include <ArmarXCore/core/logging/Logging.h> + +#include "../FluxioControlNode.h" +#include "../FluxioEdge.h" +#include "../FluxioParameter.h" +#include "FluxioMergerExecutor.h" + +namespace armarx::skills +{ + FluxioCompositeExecutor::FluxioCompositeExecutor( + std::string& id, + skills::FluxioSkill* skill, + const std::function<void(const std::string& executionId)>&& abortFluxioSkillFunc, + const std::function<FluxioExecutor*(const std::string& skillId, + const std::string& executorName)>&& + executeFluxioSkillFunc) : + FluxioExecutor(id, false), + skill(skill), + abortFluxioSkill(abortFluxioSkillFunc), + executeFluxioSkill(executeFluxioSkillFunc) + { + } + + void + FluxioCompositeExecutor::run(const std::string executorName, + armarx::aron::data::DictPtr parameters) + { + + ARMARX_INFO << "Running skill " << skill->name; + setStatus(skills::SkillStatus::Constructing); + + skills::FluxioEdge startEdge; + if (!validateSkill(startEdge)) + { + + ARMARX_WARNING << "Skill execution cancelled."; + return; + } + + setStatus(skills::SkillStatus::Initializing); + subExecutionsMap.clear(); + setStatus(skills::SkillStatus::Preparing); + + std::atomic_bool skillRunning = true; + + // thread for polling status updates + std::thread( + [this, &skillRunning] + { + while (skillRunning) + { + this->pollSubStatuses(); + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + } + }) + .detach(); + + // thread for the sub routines + const std::string newExecutorName = executorName + "/" + skill->name; + std::thread( + [this, &startEdge, &skillRunning, &newExecutorName] + { + this->startSubRoutine( + startEdge.toNodePtr, startEdge.toParameterPtr, skillRunning, newExecutorName); + }) + .detach(); + + setStatus(skills::SkillStatus::Running); + + // main loop + while (skillRunning) + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + if (status->status == skills::SkillStatus::Aborted || + status->status == skills::SkillStatus::Failed || + status->status == skills::SkillStatus::Succeeded) + { + + skillRunning = false; + + // the skill was aborted with the abort method,no need to abort the + // SubExecutions twice + if (status->status != skills::SkillStatus::Aborted) + { + abortSubExecutions(); + } + + return; + } + } + } + + void + FluxioCompositeExecutor::startSubRoutine(const skills::FluxioNode* startNode, + const skills::FluxioParameter* startParameter, + std::atomic_bool& running, + const std::string& executorName) + { + if (!running) + { + return; + } + + if (startNode == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr"; + setStatus(skills::SkillStatus::Failed); + return; + } + + if (startNode->nodeType == skills::FluxioNodeType::PARAMETER) + { + // cast to parameter node + const auto* paramNodePtr = dynamic_cast<const skills::FluxioParameterNode*>(startNode); + if (paramNodePtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr.Dynamic cast from FluxioNodePtr to " + "FluxioParameterNodePtr failed."; + setStatus(skills::SkillStatus::Failed); + return; + } + + this->handleParameterRoutine(paramNodePtr, running, executorName); + } + else if (startNode->nodeType == skills::FluxioNodeType::CONTROL) + { + // cast to control node + const auto* controlNodePtr = dynamic_cast<const skills::FluxioControlNode*>(startNode); + + if (controlNodePtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr. Dynamic cast from FluxioNodePtr to " + "FluxioControlNodePtr failed."; + setStatus(skills::SkillStatus::Failed); + return; + } + + this->handleControlRoutine(controlNodePtr, startParameter, running, executorName); + } + else if (startNode->nodeType == skills::FluxioNodeType::SUBSKILL) + { + // cast to subskill node + const auto* subSkillNodePtr = + dynamic_cast<const skills::FluxioSubSkillNode*>(startNode); + if (subSkillNodePtr == nullptr || subSkillNodePtr->skillPtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr. Dynamic cast from FluxioNodePtr to " + "FluxioSubSkillNodePtr failed."; + setStatus(skills::SkillStatus::Failed); + return; + } + + this->handleSubSkillRoutine(subSkillNodePtr, running, executorName); + } + else + { + ARMARX_WARNING + << "Unexpected node type '" + << skills::FluxioNodeTypeToString(startNode->nodeType).value_or("UNKNOWN") + << "' for Node with id " << startNode->nodeId; + setStatus(skills::SkillStatus::Failed); + return; + } + } + + void + FluxioCompositeExecutor::handleParameterRoutine( + const skills::FluxioParameterNode* parameterNode, + std::atomic_bool& running, + const std::string& /*unused*/) + { + if (parameterNode == nullptr || parameterNode->parameterPtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr"; + setStatus(skills::SkillStatus::Failed); + return; + } + + // make sure it is an event parameter + if (parameterNode->parameterPtr->type != "Event") + { + ARMARX_WARNING << "Unexpected parameter type " << parameterNode->parameterPtr->type; + setStatus(skills::SkillStatus::Failed); + return; + } + + // get the event type and set the status accordingly + const std::string& eventType = parameterNode->parameterPtr->name; + if (eventType == "Succeeded") + { + setStatus(skills::SkillStatus::Succeeded); + } + else if (eventType == "Failed") + { + setStatus(skills::SkillStatus::Failed); + } + else if (eventType == "Aborted") + { + setStatus(skills::SkillStatus::Aborted); + } + else + { + ARMARX_WARNING << "Unexpected event type " << eventType << " for parameter " + << parameterNode->parameterPtr->name; + setStatus(skills::SkillStatus::Failed); + } + } + + void + FluxioCompositeExecutor::handleSubSkillRoutine(const skills::FluxioSubSkillNode* subSkillNode, + std::atomic_bool& running, + const std::string& executorName) + { + if (subSkillNode == nullptr || subSkillNode->skillPtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr"; + setStatus(skills::SkillStatus::Failed); + return; + } + + // start skill execution + auto* executorPtr = executeFluxioSkill(subSkillNode->skillPtr->id, executorName); + if (executorPtr == nullptr) + { + ARMARX_WARNING << "Failed to execute subskill " << subSkillNode->skillPtr->id; + setStatus(skills::SkillStatus::Failed); + return; + } + + subExecutionsMap.insert({subSkillNode->nodeId, executorPtr}); + + // wait until the skill has finished (or the super skill is finished) + skills::FluxioSkillStatusUpdate statusUpdate; + while (running) + { + // sleep for a while + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + executorPtr->getStatusUpdate(); // FIXME: bad design + const auto& statusUpdateIt = executorPtr->getStatus(); + + if (!statusUpdateIt.has_value()) + { + ARMARX_INFO << "No status update from skill " << subSkillNode->skillPtr->name + << " yet. Waiting..."; + continue; + } + + statusUpdate = statusUpdateIt.value(); + // did the status change? update statusUpdates list + const auto& lastUpdate = + std::find_if(statusUpdates.begin(), + statusUpdates.end(), + [&subSkillNode](const skills::FluxioSkillStatusUpdate& statusUpdate) + { return statusUpdate.subSkillNodeId == subSkillNode->nodeId; }); + + if (lastUpdate == statusUpdates.end() || lastUpdate->status != statusUpdate.status) + { + statusUpdates.push_front({armarx::DateTime::Now(), + executorPtr->id, + subSkillNode->nodeId, + statusUpdate.status}); + } + + // check subskill is finished + if (statusUpdate.status == skills::SkillStatus::Succeeded || + statusUpdate.status == skills::SkillStatus::Failed || + statusUpdate.status == skills::SkillStatus::Aborted) + { + break; + } + } + + if (!running) + { + return; + } + + // check the final skill status get the output event parameter + const std::string& outputEventName = + statusUpdate.status == skills::SkillStatus::Succeeded ? "Succeeded" + : statusUpdate.status == skills::SkillStatus::Failed ? "Failed" + : statusUpdate.status == skills::SkillStatus::Aborted ? "Aborted" + : "Undefined"; + const auto& outputParam = std::find_if( + subSkillNode->skillPtr->parameters.begin(), + subSkillNode->skillPtr->parameters.end(), + [&outputEventName](const std::pair<std::string, skills::FluxioParameter>& param) + { + return (param.second.type == "Event" && !param.second.isInput && + param.second.name == outputEventName); + }); + + if (outputParam == subSkillNode->skillPtr->parameters.end()) + { + ARMARX_WARNING << "Skill " << subSkillNode->skillPtr->name + << " is missing the output event parameter " << outputEventName; + setStatus(skills::SkillStatus::Failed); + return; + } + + // find the connected edge + const auto& edge = + std::find_if(skill->edges.begin(), + skill->edges.end(), + [&subSkillNode, &outputParam](const skills::FluxioEdge& edge) + { + return (edge.fromNodePtr->nodeId == subSkillNode->nodeId && + edge.fromParameterPtr->id == outputParam->second.id); + }); + + if (edge == skill->edges.end()) + { + ARMARX_WARNING << "Skill " << skill->name + << " has no edge connected to the output event parameter " + << outputEventName; + setStatus(skills::SkillStatus::Failed); + return; + } + + // start new subroutine + const std::string& nextExecutorName = executorName + "/" + subSkillNode->skillPtr->name; + startSubRoutine(edge->toNodePtr, edge->toParameterPtr, running, nextExecutorName); + } + + void + FluxioCompositeExecutor::handleControlRoutine(const skills::FluxioControlNode* controlNode, + const skills::FluxioParameter* startParameter, + std::atomic_bool& running, + const std::string& executorName) + { + if (controlNode == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr"; + setStatus(skills::SkillStatus::Failed); + return; + } + + // check the controlType + if (controlNode->controlType == skills::FluxioControlNodeType::SPLITTER) + { + // find connected nodes and store the relevant edges + std::vector<const skills::FluxioEdge*> edgePtrs = {}; + for (const auto& [id, param] : controlNode->parametersMap) + { + // ignore the input parameters + if (param.isInput) + { + continue; + } + + for (const auto& edge : skill->edges) + { + if (edge.fromParameterPtr != nullptr && edge.fromParameterPtr->id == id) + { + edgePtrs.push_back(&edge); + } + } + } + + const std::string newExecutorName = executorName + "/Splitter"; + + // start subroutines in separate threads + for (const skills::FluxioEdge* edgePtr : edgePtrs) + { + std::thread( + [this, edgePtr, &running, &newExecutorName] { + startSubRoutine( + edgePtr->toNodePtr, edgePtr->toParameterPtr, running, newExecutorName); + }) + .detach(); + } + } + else if (controlNode->controlType == skills::FluxioControlNodeType::AND_MERGER) + { + // check the list of subexecutions for the node id + FluxioMergerExecutor* mergerExecutorPtr = nullptr; + const auto& executorPtr = subExecutionsMap.find(controlNode->nodeId); + if (executorPtr == subExecutionsMap.end()) + { + // assemble paramId vector + std::vector<std::string> paramIds = {}; + for (const auto& [id, param] : controlNode->parametersMap) + { + if (param.isInput) + { + paramIds.push_back(id); + } + } + + // there is no execution for the merger yet, let´s start one + subExecutionsMap[controlNode->nodeId] = + new FluxioMergerExecutor(IceUtil::generateUUID(), paramIds); + + mergerExecutorPtr = + dynamic_cast<FluxioMergerExecutor*>(subExecutionsMap[controlNode->nodeId]); + } + else + { + mergerExecutorPtr = dynamic_cast<FluxioMergerExecutor*>(executorPtr->second); + } + + if (mergerExecutorPtr == nullptr) + { + ARMARX_WARNING << "Unexpected nullptr. Dynamic cast from FluxioExecutorPtr to " + "FluxioMergerExecutorPtr failed."; + setStatus(skills::SkillStatus::Failed); + return; + } + + // check in the token + mergerExecutorPtr->checkInToken(startParameter->id); + + if (mergerExecutorPtr->getStatus() != std::nullopt) + { + return; + } + + // reuse this thread + mergerExecutorPtr->run(executorName, nullptr); + + const auto& outputParam = + std::find_if(controlNode->parametersMap.begin(), + controlNode->parametersMap.end(), + [](const std::pair<std::string, skills::FluxioParameter>& param) + { return !param.second.isInput; }); + + if (outputParam == controlNode->parametersMap.end()) + { + ARMARX_WARNING << "Control node " << controlNode->nodeId + << " has no output parameter"; + setStatus(skills::SkillStatus::Failed); + return; + } + + // find the connected edge + const auto& edge = + std::find_if(skill->edges.begin(), + skill->edges.end(), + [&controlNode, &outputParam](const skills::FluxioEdge& edge) + { + return (edge.fromNodePtr->nodeId == controlNode->nodeId && + edge.fromParameterPtr->id == outputParam->second.id); + }); + + if (edge == skill->edges.end()) + { + ARMARX_WARNING + << "Skill " << skill->name + << " has no edge connected to the output event parameter of the AND merger"; + setStatus(skills::SkillStatus::Failed); + return; + } + + // start new subroutine + startSubRoutine(edge->toNodePtr, edge->toParameterPtr, running, executorName); + } + else + { + ARMARX_WARNING << "Unexpected control type "; + setStatus(skills::SkillStatus::Failed); + return; + } + } + + void + FluxioCompositeExecutor::abort() + { + ARMARX_INFO << "Aborting skill " << skill->name; + setStatus(skills::SkillStatus::Aborted); + abortSubExecutions(); + } + + void + FluxioCompositeExecutor::abortSubExecutions() + { + for (const auto& [nodeId, executorPtr] : subExecutionsMap) + { + auto s = executorPtr->getStatus(); + if (!s.has_value() || s->status == skills::SkillStatus::Succeeded || + s->status == skills::SkillStatus::Failed || + s->status == skills::SkillStatus::Aborted) + { + continue; + } + + executorPtr->abort(); + statusUpdates.push_front( + {armarx::DateTime::Now(), executorPtr->id, nodeId, skills::SkillStatus::Aborted}); + } + } + + std::optional<std::vector<skills::FluxioSkillStatusUpdate>> + FluxioCompositeExecutor::getStatusUpdate() + { + ARMARX_INFO << "Getting status updates for skill " << skill->name; + // convert statusupdates list to vector + auto ret = std::vector<skills::FluxioSkillStatusUpdate>(statusUpdates.begin(), + statusUpdates.end()); + + return ret; + } + + void + FluxioCompositeExecutor::pollSubStatuses() + { + for (const auto& [nodeId, executorPtr] : subExecutionsMap) + { + executorPtr->getStatusUpdate(); + auto s = executorPtr->getStatus(); + if (!s.has_value()) + { + continue; + } + + const auto& lastStatus = + find_if(statusUpdates.begin(), + statusUpdates.end(), + [&](const skills::FluxioSkillStatusUpdate& statusUpdate) + { return statusUpdate.subSkillNodeId == nodeId; }) + ->status; + if (lastStatus != s->status) + { + statusUpdates.push_front( + {armarx::DateTime::Now(), executorPtr->id, nodeId, s->status}); + } + } + } + + bool + FluxioCompositeExecutor::validateSkill(skills::FluxioEdge& ret) + { + // check if skill is not null + if (skill == nullptr) + { + ARMARX_WARNING << "Skill is not set"; + return false; + } + + // get start parameter + const auto& startParam = + std::find_if(skill->parameters.begin(), + skill->parameters.end(), + [](const std::pair<std::string, skills::FluxioParameter>& param) { + return (param.second.type == "Event" && param.second.isInput && + param.second.name == "Start"); + }); + + if (startParam == skill->parameters.end()) + { + ARMARX_WARNING << "Skill has no start parameter"; + return false; + } + + // get all parameter nodes for the start parameter + const auto& startNode = + std::find_if(skill->nodes.begin(), + skill->nodes.end(), + [startParam](const std::pair<std::string, skills::FluxioNode*>& nodeEntry) + { + if (nodeEntry.second->nodeType != skills::FluxioNodeType::PARAMETER) + { + return false; + } + + const auto* paramNode = + dynamic_cast<const skills::FluxioParameterNode*>(nodeEntry.second); + return (paramNode->parameterPtr->id == startParam->second.id); + }); + + // there can only be one + if (startNode == skill->nodes.end()) + { + ARMARX_WARNING << "Skill has no start node"; + return false; + } + + // check if the start node is connected + const auto& startEdge = + std::find_if(skill->edges.begin(), + skill->edges.end(), + [startNode](const skills::FluxioEdge& edge) + { return (edge.fromNodePtr->nodeId == startNode->second->nodeId); }); + + // there can only be one + if (startEdge == skill->edges.end()) + { + ARMARX_WARNING << "Skill has noedge connected to the start node"; + return false; + } + + // get the output event parameters + const auto& outputParamsSuccess = + std::find_if(skill->parameters.begin(), + skill->parameters.end(), + [](const std::pair<std::string, skills::FluxioParameter>& param) + { + return (param.second.type == "Event" && !param.second.isInput && + param.second.name == "Succeeded"); + }); + const auto& outputParamsFailed = + std::find_if(skill->parameters.begin(), + skill->parameters.end(), + [](const std::pair<std::string, skills::FluxioParameter>& param) + { + return (param.second.type == "Event" && !param.second.isInput && + param.second.name == "Failed"); + }); + const auto& outputParamsAborted = + std::find_if(skill->parameters.begin(), + skill->parameters.end(), + [](const std::pair<std::string, skills::FluxioParameter>& param) + { + return (param.second.type == "Event" && !param.second.isInput && + param.second.name == "Aborted"); + }); + + if (outputParamsSuccess == skill->parameters.end() || + outputParamsFailed == skill->parameters.end() || + outputParamsAborted == skill->parameters.end()) + { + ARMARX_WARNING << "Skill is missing one or more output event parameters"; + return false; + } + + // TODO: the rest + + ARMARX_INFO << "Skill validation is not fully implemented yet."; + ret = *startEdge; + return true; + } + + void + FluxioCompositeExecutor::setStatus(skills::SkillStatus status) + { + const skills::FluxioSkillStatusUpdate update = { + armarx::DateTime::Now(), id, skill->id, status}; + this->status = update; + this->statusUpdates.push_front(update); + } +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.h b/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.h new file mode 100644 index 0000000000000000000000000000000000000000..5fc27bb61cfd921c895d5c99fb4a11bee044c753 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.h @@ -0,0 +1,62 @@ +#pragma once + +#include <atomic> +#include <optional> +#include <string> + +#include "../FluxioControlNode.h" +#include "../FluxioParameter.h" +#include "../FluxioParameterNode.h" +#include "../FluxioSkill.h" +#include "../FluxioSkillStatusUpdate.h" +#include "../FluxioSubSkillNode.h" +#include "../SkillExecutionID.h" +#include "../SkillStatusUpdate.h" +#include "FluxioExecutor.h" + +namespace armarx::skills +{ + class FluxioCompositeExecutor : public FluxioExecutor + { + public: + FluxioCompositeExecutor( + std::string& id, + skills::FluxioSkill* skill, + const std::function<void(const std::string& executionId)>&& abortFluxioSkillFunc, + const std::function<FluxioExecutor*(const std::string& skillId, + const std::string& executorName)>&& + executeFluxioSkillFunc); + void run(const std::string executorName, armarx::aron::data::DictPtr parameters) override; + void abort() override; + std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() override; + bool validateSkill(skills::FluxioEdge& ret); + + private: + void startSubRoutine(const skills::FluxioNode* startNode, + const skills::FluxioParameter* startParameter, + std::atomic_bool& running, + const std::string& executorName); + + void handleParameterRoutine(const skills::FluxioParameterNode* parameterNode, + std::atomic_bool& running, + const std::string& executorName); + void handleSubSkillRoutine(const skills::FluxioSubSkillNode* subSkillNode, + std::atomic_bool& running, + const std::string& executorName); + void handleControlRoutine(const skills::FluxioControlNode* controlNode, + const skills::FluxioParameter* startParameter, + std::atomic_bool& running, + const std::string& executorName); + + void abortSubExecutions(); + std::optional<skills::SkillExecutionID> executionId = std::nullopt; + skills::FluxioSkill* skill; + void setStatus(skills::SkillStatus status) override; + void pollSubStatuses(); + std::map<std::string, FluxioExecutor*> subExecutionsMap; // key is node id + const std::function<void(const std::string& executionId)> abortFluxioSkill; + const std::function<FluxioExecutor*(const std::string& skillId, + const std::string& executorName)> + executeFluxioSkill; + }; +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/manager/FluxioExecutor.cpp b/source/RobotAPI/libraries/skills/core/executor/FluxioExecutor.cpp similarity index 100% rename from source/RobotAPI/libraries/skills/manager/FluxioExecutor.cpp rename to source/RobotAPI/libraries/skills/core/executor/FluxioExecutor.cpp diff --git a/source/RobotAPI/libraries/skills/manager/FluxioExecutor.h b/source/RobotAPI/libraries/skills/core/executor/FluxioExecutor.h similarity index 52% rename from source/RobotAPI/libraries/skills/manager/FluxioExecutor.h rename to source/RobotAPI/libraries/skills/core/executor/FluxioExecutor.h index 9701c5c48d2dab0a5807bba985d7f10b77ff3b75..9ca0992451f5725ccd6353b967c5ad4fb8e7b09b 100644 --- a/source/RobotAPI/libraries/skills/manager/FluxioExecutor.h +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioExecutor.h @@ -1,25 +1,21 @@ #pragma once +#include <list> #include <string> #include <vector> -#include "RobotAPI/libraries/skills/core/FluxioSkillStatusUpdate.h" +#include "../FluxioSkillStatusUpdate.h" -#include "SkillManagerComponentPlugin.h" - -namespace armarx::plugins +namespace armarx::skills { - class SkillManagerComponentPlugin; // forward declaration - class FluxioExecutor { public: virtual ~FluxioExecutor(){}; - FluxioExecutor(std::string& id, SkillManagerComponentPlugin* plugin, bool native) : - id(id), native(native), plugin(plugin){}; - virtual void run(std::string executorName, armarx::aron::data::DictPtr parameters){}; - virtual void abort(){}; + FluxioExecutor(const std::string& id, bool native) : id(id), native(native){}; + virtual void run(const std::string executorName, armarx::aron::data::DictPtr parameters) {}; + virtual void abort() {}; virtual std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() @@ -37,10 +33,9 @@ namespace armarx::plugins const bool native; protected: - std::list<skills::FluxioSkillStatusUpdate> statusUpdates = {}; + std::list<skills::FluxioSkillStatusUpdate> statusUpdates; std::string* executorName = nullptr; - SkillManagerComponentPlugin* plugin = nullptr; std::optional<skills::FluxioSkillStatusUpdate> status = std::nullopt; - virtual void setStatus(skills::SkillStatus status){}; + virtual void setStatus(skills::SkillStatus status) {}; }; -} // namespace armarx::plugins +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.cpp b/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f8f6728479e34e56d24943de75ac48e785fd3596 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.cpp @@ -0,0 +1,97 @@ +#include "FluxioMergerExecutor.h" + +#include <optional> +#include <thread> +#include <vector> + +#include <ArmarXCore/core/logging/Logging.h> + +#include "../SkillStatusUpdate.h" + +namespace armarx::skills +{ + FluxioMergerExecutor::FluxioMergerExecutor(const std::string& id, + const std::vector<std::string>& parameterIds) : + FluxioExecutor(id, false) + { + for (const auto& id : parameterIds) + { + tokenHasArrivedMap[id] = false; + } + ARMARX_WARNING << "Fluxio Merger is waiting for the following tokens to arrive: " + << parameterIds; + } + + void + FluxioMergerExecutor::run(const std::string /*unused*/, armarx::aron::data::DictPtr /*unused*/) + { + // set status running + setStatus(skills::SkillStatus::Running); + + // wait for all tokens to arrive + while (true) + { + std::this_thread::sleep_for(std::chrono::milliseconds(250)); + + // check if all tokens have arrived + bool allTokensArrived = true; + for (const auto& [id, hasArrived] : tokenHasArrivedMap) + { + if (!hasArrived) + { + allTokensArrived = false; + break; + } + } + + if (status->status == skills::SkillStatus::Aborted || + status->status == skills::SkillStatus::Failed || + status->status == skills::SkillStatus::Succeeded) + { + return; + } + + if (allTokensArrived) + { + setStatus(skills::SkillStatus::Succeeded); + return; + } + } + } + + void + FluxioMergerExecutor::abort() + { + setStatus(skills::SkillStatus::Aborted); + } + + void + FluxioMergerExecutor::checkInToken(const std::string& parameterId) + { + const auto it = tokenHasArrivedMap.find(parameterId); + if (it == tokenHasArrivedMap.end()) + { + ARMARX_WARNING << "Unexpected parameterId: " << parameterId; + } + else + { + ARMARX_WARNING << "Fluxio Merger received token for parameterId: " << parameterId; + tokenHasArrivedMap[parameterId] = true; + } + } + + std::optional<std::vector<skills::FluxioSkillStatusUpdate>> + FluxioMergerExecutor::getStatusUpdate() + { + // unused method + return std::nullopt; + } + + void + FluxioMergerExecutor::setStatus(skills::SkillStatus status) + { + const skills::FluxioSkillStatusUpdate update = { + armarx::DateTime::Now(), this->id, "noIdNeeded", status}; + this->status = update; + } +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.h b/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.h new file mode 100644 index 0000000000000000000000000000000000000000..5f8bcfff89649aec9f9747d4a588705a498af163 --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioMergerExecutor.h @@ -0,0 +1,25 @@ +#pragma once + +#include <optional> +#include <string> + +#include "../FluxioSkillStatusUpdate.h" +#include "FluxioExecutor.h" + +namespace armarx::skills +{ + class FluxioMergerExecutor : public FluxioExecutor + { + public: + FluxioMergerExecutor(const std::string& id, const std::vector<std::string>& parameterIds); + + void run(const std::string executorName, armarx::aron::data::DictPtr parameters) override; + void abort() override; + void checkInToken(const std::string& parameterId); + std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() override; + + private: + void setStatus(skills::SkillStatus status) override; + std::map<std::string, bool> tokenHasArrivedMap; // keys are parameterIds + }; +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.cpp b/source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.cpp similarity index 55% rename from source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.cpp rename to source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.cpp index 89c09c6977942ba317e85204e03b907369e03d58..e3b2f03fbd531c571b4b5d36349b49b250b1ec15 100644 --- a/source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.cpp +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.cpp @@ -4,30 +4,40 @@ #include <ArmarXCore/core/logging/Logging.h> -#include "RobotAPI/libraries/skills/core/FluxioSkillStatusUpdate.h" -#include "RobotAPI/libraries/skills/core/ProviderID.h" -#include "RobotAPI/libraries/skills/core/SkillExecutionID.h" -#include "RobotAPI/libraries/skills/core/SkillExecutionRequest.h" -#include "RobotAPI/libraries/skills/core/SkillID.h" +#include "../ProviderID.h" +#include "../SkillExecutionRequest.h" +#include "../SkillID.h" -namespace armarx::plugins +namespace armarx::skills { - FluxioNativeExecutor::FluxioNativeExecutor(std::string& id, - SkillManagerComponentPlugin* plugin, - skills::SkillID& skillId) : - FluxioExecutor(id, plugin, true), skillId(skillId) + FluxioNativeExecutor::FluxioNativeExecutor( + std::string& id, + skills::SkillID& skillId, + std::string& fluxioUUID, + const std::function<bool(const skills::SkillExecutionID&)>&& abortSkillFunc, + const std::function<skills::SkillExecutionID(const skills::SkillExecutionRequest&)>&& + executeSkillAsyncFunc, + const std::function<std::optional<skills::SkillStatusUpdate>( + const skills::SkillExecutionID&)>&& getSkillExecutionStatusFunc) : + FluxioExecutor(id, true), + skillId(skillId), + fluxioUUID(fluxioUUID), + abortSkill(abortSkillFunc), + executeSkillAsync(executeSkillAsyncFunc), + getSkillExecutionStatus(getSkillExecutionStatusFunc) { } void - FluxioNativeExecutor::run(std::string executorName, armarx::aron::data::DictPtr parameters) + FluxioNativeExecutor::run(const std::string executorName, + armarx::aron::data::DictPtr parameters) { skills::SkillExecutionRequest req; req.skillId = skillId; req.parameters = parameters; req.executorName = executorName; - auto eid = this->plugin->executeSkillAsync(req); + auto eid = executeSkillAsync(req); this->executionId = eid; } @@ -35,7 +45,7 @@ namespace armarx::plugins void FluxioNativeExecutor::abort() { - if (!this->executionId.has_value() || this->plugin == nullptr) + if (!this->executionId.has_value()) { // error ARMARX_WARNING << "Execution ID or plugin is not set"; @@ -43,13 +53,13 @@ namespace armarx::plugins } // TODO: check last execution status - this->plugin->abortSkill(this->executionId.value()); + abortSkill(this->executionId.value()); } std::optional<std::vector<skills::FluxioSkillStatusUpdate>> FluxioNativeExecutor::getStatusUpdate() { - if (!this->executionId.has_value() || this->plugin == nullptr) + if (!this->executionId.has_value()) { // error ARMARX_WARNING << "Execution ID or plugin is not set"; @@ -58,7 +68,7 @@ namespace armarx::plugins auto executionId = this->executionId.value(); - auto status = this->plugin->getSkillExecutionStatus(executionId); + auto status = getSkillExecutionStatus(executionId); if (!status.has_value()) { @@ -69,7 +79,7 @@ namespace armarx::plugins } skills::FluxioSkillStatusUpdate update = { - armarx::DateTime::Now(), this->id, this->skillId.skillName, status.value().status}; + armarx::DateTime::Now(), this->id, this->fluxioUUID, status.value().status}; // newest statusupdate is aways at the end of the vector if (this->statusUpdates.empty() || update.status != this->statusUpdates.front().status) @@ -85,8 +95,8 @@ namespace armarx::plugins FluxioNativeExecutor::setStatus(skills::SkillStatus status) { const skills::FluxioSkillStatusUpdate update = { - armarx::DateTime::Now(), this->id, this->skillId.skillName, status}; + armarx::DateTime::Now(), this->id, this->fluxioUUID, status}; this->status = update; this->statusUpdates.push_front(update); } -} // namespace armarx::plugins +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.h b/source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.h new file mode 100644 index 0000000000000000000000000000000000000000..3b8288cac2887a7fdb446a7152f4e32912dc4b3f --- /dev/null +++ b/source/RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.h @@ -0,0 +1,45 @@ +#pragma once + +#include <optional> +#include <string> + +#include "RobotAPI/libraries/skills/core/SkillExecutionRequest.h" +#include "RobotAPI/libraries/skills/core/SkillID.h" +#include "RobotAPI/libraries/skills/core/SkillStatusUpdate.h" + +#include "../FluxioSkillStatusUpdate.h" +#include "../SkillExecutionID.h" +#include "FluxioExecutor.h" + +namespace armarx::skills +{ + class FluxioNativeExecutor : public FluxioExecutor + { + public: + FluxioNativeExecutor( + std::string& id, + skills::SkillID& skillId, + std::string& fluxioUUID, + const std::function<bool(const skills::SkillExecutionID&)>&& abortSkillFunc, + const std::function<skills::SkillExecutionID(const skills::SkillExecutionRequest&)>&& + executeSkillAsyncFunc, + const std::function<std::optional<skills::SkillStatusUpdate>( + const skills::SkillExecutionID&)>&& getSkillExecutionStatusFunc); + + void run(const std::string executorName, armarx::aron::data::DictPtr parameters) override; + void abort() override; + std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() override; + + private: + std::optional<skills::SkillExecutionID> executionId = std::nullopt; + skills::SkillID skillId; + std::string fluxioUUID; + void setStatus(skills::SkillStatus status) override; + const std::function<bool(const skills::SkillExecutionID&)> abortSkill; + const std::function<skills::SkillExecutionID(const skills::SkillExecutionRequest&)> + executeSkillAsync; + const std::function<std::optional<skills::SkillStatusUpdate>( + const skills::SkillExecutionID&)> + getSkillExecutionStatus; + }; +} // namespace armarx::skills diff --git a/source/RobotAPI/libraries/skills/manager/CMakeLists.txt b/source/RobotAPI/libraries/skills/manager/CMakeLists.txt index 14400219e0d7a06468d9873eeeb387e47ef2b3e9..34494a91f8cc27d289aa8d28773c10250a4bbb4a 100644 --- a/source/RobotAPI/libraries/skills/manager/CMakeLists.txt +++ b/source/RobotAPI/libraries/skills/manager/CMakeLists.txt @@ -18,15 +18,9 @@ armarx_add_library( SOURCES SkillManagerComponentPlugin.cpp SkillManagerComponentPluginUser.cpp - FluxioExecutor.cpp - FluxioNativeExecutor.cpp - FluxioCompositeExecutor.cpp HEADERS SkillManagerComponentPlugin.h SkillManagerComponentPluginUser.h - FluxioExecutor.h - FluxioNativeExecutor.h - FluxioCompositeExecutor.h ) add_library(RobotAPI::skills::manager ALIAS RobotAPISkillsManager) diff --git a/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.cpp b/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.cpp deleted file mode 100644 index da6c89ae5b5180df10d3ffbb44b7ac6a779fe178..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.cpp +++ /dev/null @@ -1,470 +0,0 @@ -#include "FluxioCompositeExecutor.h" - -#include <algorithm> -#include <thread> -#include <vector> - -#include <ArmarXCore/core/logging/Logging.h> - -#include "RobotAPI/libraries/skills/core/FluxioNode.h" -#include "RobotAPI/libraries/skills/core/FluxioParameter.h" -#include "RobotAPI/libraries/skills/core/FluxioParameterNode.h" -#include "RobotAPI/libraries/skills/core/FluxioSkill.h" -#include "RobotAPI/libraries/skills/core/FluxioSkillStatusUpdate.h" -#include "RobotAPI/libraries/skills/core/FluxioSubSkillNode.h" -#include "RobotAPI/libraries/skills/core/SkillStatusUpdate.h" -#include "RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.h" - -namespace armarx::plugins -{ - FluxioCompositeExecutor::FluxioCompositeExecutor(std::string& id, - SkillManagerComponentPlugin* plugin, - skills::FluxioSkill* skill) : - FluxioExecutor(id, plugin, false), skill(skill) - { - } - - void - FluxioCompositeExecutor::run(std::string executorName, armarx::aron::data::DictPtr parameters) - { - // if (!validateSkill()) - // { - - // ARMARX_WARNING << "Skill execution cancelled."; - // return; - // } - - // std::string NewExecutorName = skill->name; - subExecutionsMap.clear(); - setStatus(skills::SkillStatus::Constructing); - - // poll the status updates in a separate thread - std::atomic_bool threadRunning = true; - auto statusUpdateThread = std::thread( - [this, &threadRunning] - { - while (threadRunning) - { - this->pollSubStatuses(); - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - } - }); - statusUpdateThread.detach(); - - if (skill == nullptr) - { - ARMARX_WARNING << "Fluxioskill is not set"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - - const auto& nodes = skill->nodes; - if (nodes.empty()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no nodes"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& edges = skill->edges; - if (edges.empty()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no edges"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& skillParams = skill->parameters; - if (skillParams.empty()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no parameters"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& startParam = - std::find_if(skillParams.begin(), - skillParams.end(), - [](const std::pair<std::string, skills::FluxioParameter>& param) - { return (param.second.type == "Event" && param.second.isInput); }); - if (startParam == skillParams.end()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no start parameter"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& startNode = - std::find_if(nodes.begin(), - nodes.end(), - [&startParam](const std::pair<std::string, skills::FluxioNode*>& node) - { - // check for parameter node - if (node.second->nodeType != "PARAMETER") - { - return false; - } - // cast to parameter node - const auto* paramNode = - dynamic_cast<const skills::FluxioParameterNode*>(node.second); - return (paramNode->parameterPtr->id == startParam->second.id); - }); - if (startNode == nodes.end()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no start node"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& edgeWithStartNode = - std::find_if(edges.begin(), - edges.end(), - [&startNode, &startParam](const skills::FluxioEdge& edge) - { - return (edge.fromNodePtr->nodeId == startNode->second->nodeId && - edge.fromParameterPtr->id == startParam->second.id); - }); - if (edgeWithStartNode == edges.end()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no edge with start node"; - setStatus(skills::SkillStatus::Failed); - return; - } - - skills::FluxioNode* nextNode = edgeWithStartNode->toNodePtr; - while (nextNode->nodeType == "SUBSKILL") - { - if (status->status == skills::SkillStatus::Aborted || - status->status == skills::SkillStatus::Failed || - status->status == skills::SkillStatus::Succeeded) - { - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - - // cast to subskill node - const auto* subSkillNode = dynamic_cast<const skills::FluxioSubSkillNode*>(nextNode); - if (subSkillNode == nullptr) - { - ARMARX_WARNING << "Skill " << skill->name << " has no subskill node. Pointer null"; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - auto* executorPtr = - plugin->executeFluxioSkill(subSkillNode->skillPtr->id, executorName); - if (executorPtr == nullptr) - { - ARMARX_WARNING << "Failed to execute subskill " << subSkillNode->skillPtr->id; - setStatus(skills::SkillStatus::Failed); - - // kill thread t - threadRunning = false; - statusUpdateThread.join(); - return; - } - - subExecutionsMap.insert({nextNode->nodeId, executorPtr}); - std::string eventParamName; - setStatus(skills::SkillStatus::Running); - bool running = true; - while (running) - { - // sleep for a while - std::this_thread::sleep_for(std::chrono::milliseconds(250)); - const auto& executionStatus = executorPtr->getStatus(); - - // check if this composite skill has terminated or aborted - if (status->status == skills::SkillStatus::Aborted || - status->status == skills::SkillStatus::Failed || - status->status == skills::SkillStatus::Succeeded) - { - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - - if (!executionStatus.has_value()) - { - ARMARX_INFO << "No status update from skill " << subSkillNode->skillPtr->name - << " yet. Waiting..."; - continue; - } - - if (executionStatus->status == skills::SkillStatus::Succeeded) - { - eventParamName = "Succeeded"; - running = false; - } - else if (executionStatus->status == skills::SkillStatus::Failed) - { - eventParamName = "Failed"; - running = false; - } - else if (executionStatus->status == skills::SkillStatus::Aborted) - { - eventParamName = "Aborted"; - running = false; - } - else - { - ARMARX_INFO << "Skill " << subSkillNode->skillPtr->name - << " is still running. Waiting..."; - } - } - - const auto& eventParam = std::find_if( - subSkillNode->skillPtr->parameters.begin(), - subSkillNode->skillPtr->parameters.end(), - [&eventParamName](const std::pair<std::string, skills::FluxioParameter>& param) - { return (param.second.type == "Event" && param.second.name == eventParamName); }); - if (eventParam == skillParams.end()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no " << eventParamName - << " parameter"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - const auto& eventNode = subSkillNode; - const auto& edgeWithEventNode = - std::find_if(edges.begin(), - edges.end(), - [&eventNode, &eventParam](const skills::FluxioEdge& edge) - { - return (edge.fromNodePtr->nodeId == eventNode->nodeId && - edge.fromParameterPtr->id == eventParam->second.id); - }); - if (edgeWithEventNode == edges.end()) - { - ARMARX_WARNING << "Skill " << skill->name << " has no edge with " << eventParamName - << " node"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - nextNode = edgeWithEventNode->toNodePtr; - } - - if (nextNode->nodeType != "PARAMETER") - { - ARMARX_WARNING << "Skill " << skill->name << " has no parameter node"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - - // cast to parameter node - const auto* paramNode = dynamic_cast<const skills::FluxioParameterNode*>(nextNode); - if (paramNode == nullptr) - { - ARMARX_WARNING << "Skill " << skill->name << " has no parameter node. Pointer null"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - - if (paramNode->parameterPtr->type != "Event") - { - ARMARX_WARNING << "Skill " << skill->name << " has no valid event parameter"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - - - if (paramNode->parameterPtr->name == "Succeeded") - { - setStatus(skills::SkillStatus::Succeeded); - } - else if (paramNode->parameterPtr->name == "Failed") - { - setStatus(skills::SkillStatus::Failed); - } - else if (paramNode->parameterPtr->name == "Aborted") - { - setStatus(skills::SkillStatus::Aborted); - } - else - { - ARMARX_WARNING << "Skill " << skill->name << " has no valid event parameter"; - setStatus(skills::SkillStatus::Failed); - - // kill thread - threadRunning = false; - statusUpdateThread.join(); - return; - } - } - - void - FluxioCompositeExecutor::abort() - { - ARMARX_INFO << "Aborting skill " << skill->name; - setStatus(skills::SkillStatus::Aborted); - - if (plugin == nullptr) - { - ARMARX_WARNING << "Plugin is not set, cannot abort subskills."; - return; - } - - // abort all subskills - for (const auto& [nodeId, executorPtr] : subExecutionsMap) - { - auto s = executorPtr->getStatus(); - if (!s.has_value() || s->status == skills::SkillStatus::Succeeded || - s->status == skills::SkillStatus::Failed || - s->status == skills::SkillStatus::Aborted) - { - continue; - } - - plugin->abortFluxioSkill(executorPtr->id); - statusUpdates.push_front( - {armarx::DateTime::Now(), executorPtr->id, nodeId, skills::SkillStatus::Aborted}); - } - } - - std::optional<std::vector<skills::FluxioSkillStatusUpdate>> - FluxioCompositeExecutor::getStatusUpdate() - { - ARMARX_INFO << "Getting status updates for skill " << skill->name; - // convert statusupdates list to vector - auto ret = std::vector<skills::FluxioSkillStatusUpdate>(statusUpdates.begin(), - statusUpdates.end()); - - return ret; - } - - void - FluxioCompositeExecutor::pollSubStatuses() - { - for (const auto& [nodeId, executorPtr] : subExecutionsMap) - { - executorPtr->getStatusUpdate(); - auto s = executorPtr->getStatus(); - if (!s.has_value()) - { - continue; - } - - const auto& lastStatus = - find_if(statusUpdates.begin(), - statusUpdates.end(), - [&](const skills::FluxioSkillStatusUpdate& statusUpdate) - { return statusUpdate.subSkillNodeId == nodeId; }) - ->status; - if (lastStatus != s->status) - { - statusUpdates.push_front( - {armarx::DateTime::Now(), executorPtr->id, nodeId, s->status}); - } - } - } - - bool - FluxioCompositeExecutor::validateSkill() - { - // check if skill is not null - if (skill == nullptr) - { - ARMARX_WARNING << "Skill is not set"; - return false; - } - - // get start parameter - const auto& startParam = - std::find_if(skill->parameters.begin(), - skill->parameters.end(), - [](const std::pair<std::string, skills::FluxioParameter>& param) { - return (param.second.type == "Event" && param.second.isInput && - param.second.name == "Start"); - }); - - if (startParam == skill->parameters.end()) - { - ARMARX_WARNING << "Skill has no start parameter"; - return false; - } - - // get all parameter nodes for the start parameter - const auto& startNodes = - std::find_if(skill->nodes.begin(), - skill->nodes.end(), - [startParam](const std::pair<std::string, skills::FluxioNode*>& nodeEntry) - { - if (nodeEntry.second->nodeType != "PARAMETER") - { - return false; - } - - const auto* paramNode = - dynamic_cast<const skills::FluxioParameterNode*>(nodeEntry.second); - return (paramNode->parameterPtr->id == startParam->second.id); - }); - - // there can only be one - if (startNodes == skill->nodes.end() || std::distance(skill->nodes.begin(), startNodes) > 1) - { - ARMARX_WARNING << "Skill has no or multiple start nodes"; - return false; - } - - // TODO: the rest - - ARMARX_WARNING << "Skill validation is not implemented yet. Returning true."; - return true; - } - - void - FluxioCompositeExecutor::setStatus(skills::SkillStatus status) - { - const skills::FluxioSkillStatusUpdate update = { - armarx::DateTime::Now(), id, skill->id, status}; - this->status = update; - this->statusUpdates.push_front(update); - } -} // namespace armarx::plugins diff --git a/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.h b/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.h deleted file mode 100644 index 77db7bd23d956c3249108fcef23e8f6da184cb35..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include <optional> -#include <string> - -#include "RobotAPI/libraries/skills/core/FluxioSkill.h" -#include "RobotAPI/libraries/skills/core/FluxioSkillStatusUpdate.h" -#include "RobotAPI/libraries/skills/core/SkillExecutionID.h" -#include "RobotAPI/libraries/skills/core/SkillStatusUpdate.h" - -#include "FluxioExecutor.h" -#include "SkillManagerComponentPlugin.h" - -namespace armarx::plugins -{ - class SkillManagerComponentPlugin; // forward declaration - - class FluxioCompositeExecutor : public FluxioExecutor - { - public: - FluxioCompositeExecutor(std::string& id, - SkillManagerComponentPlugin* plugin, - skills::FluxioSkill* skill); - void run(std::string executorName, armarx::aron::data::DictPtr parameters) override; - void abort() override; - std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() override; - bool validateSkill(); - - private: - std::optional<skills::SkillExecutionID> executionId = std::nullopt; - skills::FluxioSkill* skill; - void setStatus(skills::SkillStatus status) override; - void pollSubStatuses(); - std::map<std::string, FluxioExecutor*> subExecutionsMap = {}; // key is subSkillNodeId - }; -} // namespace armarx::plugins diff --git a/source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.h b/source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.h deleted file mode 100644 index 10668222cc4181d495d61ed24651857b2cbe0d9f..0000000000000000000000000000000000000000 --- a/source/RobotAPI/libraries/skills/manager/FluxioNativeExecutor.h +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <optional> -#include <string> -#include <functional> - -#include "RobotAPI/libraries/skills/core/FluxioSkillStatusUpdate.h" -#include "RobotAPI/libraries/skills/core/SkillExecutionID.h" - -#include "FluxioExecutor.h" -#include "SkillManagerComponentPlugin.h" - -namespace armarx::plugins -{ - class SkillManagerComponentPlugin; // forward declaration - - class FluxioNativeExecutor : public FluxioExecutor - { - public: - FluxioNativeExecutor(std::string& id, - SkillManagerComponentPlugin* plugin, - skills::SkillID& skillId); - - void run(std::string executorName, armarx::aron::data::DictPtr parameters) override; - void abort() override; - std::optional<std::vector<skills::FluxioSkillStatusUpdate>> getStatusUpdate() override; - - private: - std::optional<skills::SkillExecutionID> executionId = std::nullopt; - skills::SkillID skillId; - void setStatus(skills::SkillStatus status) override; - - //std::function<void(skills::SkillID&)> foo; - }; -} // namespace armarx::plugins diff --git a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp index 3dc9058f75874dd9465534782be866e0af493e8f..ec41f5d10f059fad87f73ea46e1fb7b522cba0a5 100644 --- a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp +++ b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.cpp @@ -1,12 +1,20 @@ #include "SkillManagerComponentPlugin.h" -#include <cstddef> #include <list> #include <optional> +#include <stdexcept> #include <string> #include <thread> #include <tuple> +#include <boost/locale.hpp> +#include <boost/uuid/name_generator.hpp> +#include <boost/uuid/nil_generator.hpp> +#include <boost/uuid/string_generator.hpp> +#include <boost/uuid/uuid.hpp> +#include <boost/uuid/uuid_generators.hpp> +#include <boost/uuid/uuid_io.hpp> + #include <IceUtil/UUID.h> #include <ArmarXCore/core/Component.h> @@ -22,15 +30,14 @@ #include "RobotAPI/libraries/skills/core/FluxioValue.h" #include "RobotAPI/libraries/skills/core/error/FluxioErrorMessages.h" #include "RobotAPI/libraries/skills/core/error/FluxioException.h" -#include "RobotAPI/libraries/skills/manager/FluxioCompositeExecutor.h" -#include "RobotAPI/libraries/skills/manager/FluxioExecutor.h" +#include "RobotAPI/libraries/skills/core/executor/FluxioCompositeExecutor.h" +#include "RobotAPI/libraries/skills/core/executor/FluxioExecutor.h" +#include "RobotAPI/libraries/skills/core/executor/FluxioNativeExecutor.h" #include <RobotAPI/libraries/aron/converter/json/NLohmannJSONConverter.h> #include <RobotAPI/libraries/skills/core/SkillID.h> #include <RobotAPI/libraries/skills/core/SkillStatusUpdate.h> #include <RobotAPI/libraries/skills/core/error/Exception.h> -#include "FluxioNativeExecutor.h" - namespace armarx::plugins { void @@ -544,7 +551,7 @@ namespace armarx::plugins auto statusUpdate = skills::SkillStatusUpdate::FromIce( provider_statusUpdate_ice.value(), providerId); - ARMARX_WARNING << "Status update: " << statusUpdate.result; + // ARMARX_WARNING << "Status update: " << statusUpdate.result; return statusUpdate; } catch (...) @@ -613,8 +620,9 @@ namespace armarx::plugins //** Fluxio related methods **// //****************************// - FluxioExecutor* - SkillManagerComponentPlugin::executeFluxioSkill(std::string skillId, std::string executorName) + skills::FluxioExecutor* + SkillManagerComponentPlugin::executeFluxioSkill(const std::string& skillId, + const std::string& executorName) { const auto& result = getSkill(skillId); if (!result.isSuccess()) @@ -631,8 +639,18 @@ namespace armarx::plugins // currently empty parameters armarx::aron::data::DictPtr emptyParameters = {}; + const auto& executeFluxioSkillFunc = + [this](const std::string& skillId, const std::string& executorName) + { return this->executeFluxioSkill(skillId, executorName); }; + + const auto& abortFluxioSkill = [this](const std::string& executionId) + { this->abortFluxioSkill(executionId); }; + fluxioDC.fluxioExecutors[executionId] = - new FluxioCompositeExecutor(executionId, this, &fluxioDC.skills[skillId]); + new skills::FluxioCompositeExecutor(executionId, + &fluxioDC.skills[skillId], + std::move(abortFluxioSkill), + std::move(executeFluxioSkillFunc)); std::thread( [this, executionId, executorName, emptyParameters]() @@ -642,8 +660,8 @@ namespace armarx::plugins else { skills::SkillID sID = skills::SkillID{ - .providerId = skills::ProviderID{.providerName = skill.skillProviderPtr->id}, - .skillName = skill.id}; + .providerId = skills::ProviderID{.providerName = skill.skillProviderPtr->name}, + .skillName = skill.skillProviderPtr->name}; auto skillDescr = getSkillDescription(sID); if (!skillDescr.has_value()) @@ -653,9 +671,26 @@ namespace armarx::plugins return nullptr; } + const auto& abortSkillFunc = [this](const skills::SkillExecutionID& executionId) + { return this->abortSkill(executionId); }; + + const auto& executeSkillAsyncFunc = [this](const skills::SkillExecutionRequest& req) + { return this->executeSkillAsync(req); }; + + const auto& getSkillExecutionStatusFunc = + [this](const skills::SkillExecutionID& executionId) + { return this->getSkillExecutionStatus(executionId); }; + + // FIXME: do not use new, instead use smart pointer + std::string fluxioSkillUUID = skill.skillProviderPtr->id; fluxioDC.fluxioExecutors[executionId] = - new FluxioNativeExecutor(executionId, this, sID); + new skills::FluxioNativeExecutor(executionId, + sID, + fluxioSkillUUID, + std::move(abortSkillFunc), + std::move(executeSkillAsyncFunc), + std::move(getSkillExecutionStatusFunc)); fluxioDC.fluxioExecutors[executionId]->run(executorName, skillDescr->rootProfileDefaults); } @@ -673,13 +708,13 @@ namespace armarx::plugins return; } - FluxioExecutor* executorPtr = executorIt->second; + skills::FluxioExecutor* executorPtr = executorIt->second; if (executorPtr->native) { - FluxioNativeExecutor* castedExecutor = nullptr; + skills::FluxioNativeExecutor* castedExecutor = nullptr; try { - castedExecutor = dynamic_cast<FluxioNativeExecutor*>(executorPtr); + castedExecutor = dynamic_cast<skills::FluxioNativeExecutor*>(executorPtr); if (castedExecutor == nullptr) { ARMARX_WARNING << "Executor with id '" << executionId @@ -698,10 +733,10 @@ namespace armarx::plugins } else { - FluxioCompositeExecutor* castedExecutor = nullptr; + skills::FluxioCompositeExecutor* castedExecutor = nullptr; try { - castedExecutor = dynamic_cast<FluxioCompositeExecutor*>(executorPtr); + castedExecutor = dynamic_cast<skills::FluxioCompositeExecutor*>(executorPtr); if (castedExecutor == nullptr) { ARMARX_WARNING << "Executor with id '" << executionId @@ -727,18 +762,20 @@ namespace armarx::plugins if (executorIt == fluxioDC.fluxioExecutors.end()) { ARMARX_WARNING << "Execution with id '" << executionId << "' not found."; + auto error = + skills::error::FluxioException("Execution with id" + executionId + " not found."); auto error = skills::error::FluxioException(skills::error::createErrorMessage(skills::error::ErrorCode::ExecutionNotFound, {executionId})); error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); - return error; + return error; } - FluxioExecutor* executorPtr = executorIt->second; + skills::FluxioExecutor* executorPtr = executorIt->second; if (executorPtr->native) { - FluxioNativeExecutor* castedExecutor = nullptr; + skills::FluxioNativeExecutor* castedExecutor = nullptr; try { - castedExecutor = dynamic_cast<FluxioNativeExecutor*>(executorPtr); + castedExecutor = dynamic_cast<skills::FluxioNativeExecutor*>(executorPtr); if (castedExecutor == nullptr) { ARMARX_WARNING << "Executor with id '" << executionId @@ -756,22 +793,22 @@ namespace armarx::plugins error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); return error; } - + return {(castedExecutor->getStatusUpdate()).value()}; } - FluxioCompositeExecutor* castedExecutor = nullptr; + skills::FluxioCompositeExecutor* castedExecutor = nullptr; try { - castedExecutor = dynamic_cast<FluxioCompositeExecutor*>(executorPtr); + castedExecutor = dynamic_cast<skills::FluxioCompositeExecutor*>(executorPtr); if (castedExecutor == nullptr) { ARMARX_WARNING << "Executor with id '" << executionId << "' is not a FluxioCompositeExecutor."; + auto error = skills::error::FluxioException(skills::error::createErrorMessage(skills::error::ErrorCode::NotFluxioEompExecutor, {executionId})); error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); return error; - } } catch (const std::bad_cast& e) @@ -779,10 +816,10 @@ namespace armarx::plugins ARMARX_WARNING << "Error while getting execution runner for fluxio skill with id " << executionId << ": " << e.what(); + auto error = skills::error::FluxioException(skills::error::createErrorMessage(skills::error::ErrorCode::RunnerNotFound, {executionId}) + ": " + e.what()); error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); return error; - } @@ -793,7 +830,7 @@ namespace armarx::plugins * @brief converts parametersType or resultType from skilldescription to fluxio parameters * @param ret the map to insert the fluxio parameters into * @param ptr the aron object ptr to convert (this is either skillDescription.parametersType or skillDescription.resultType) - * @param isInput whether the parameters are input parameters or output parameters + * @param isInput value for the isInput field of the fluxio parameter(s) */ void SkillManagerComponentPlugin::convertAronObjectPtrToFluxioParameters( @@ -817,6 +854,29 @@ namespace armarx::plugins } } + boost::uuids::uuid + SkillManagerComponentPlugin::createUuidWithString( + const std::string& str, + std::optional<const boost::uuids::uuid> namespaceUUID) + { + boost::uuids::uuid nameS; + if (namespaceUUID.has_value()) + { + nameS = namespaceUUID.value(); + } + else + { + // use default null uuid + nameS = boost::uuids::nil_uuid(); + } + + // initialise the name generator with the starting UUID + boost::uuids::name_generator generator(nameS); + + // generate a new UUID, seeded with the given string + return generator(str); + } + skills::Result<std::vector<skills::FluxioSkill>, skills::error::FluxioException> SkillManagerComponentPlugin::getSkillList() { @@ -825,8 +885,11 @@ namespace armarx::plugins for (const auto& [skillId, skillDescription] : skillDescriptions) { - auto skillsEntry = fluxioDC.skills.find(skillId.skillName); + // generate skill id (idempotent) + const auto& providerId = createUuidWithString(skillId.providerId->providerName); + const auto& id = createUuidWithString(skillId.skillName, providerId); + auto skillsEntry = fluxioDC.skills.find(boost::uuids::to_string(id)); if (skillsEntry != fluxioDC.skills.end()) { continue; @@ -834,67 +897,73 @@ namespace armarx::plugins skills::FluxioSkill s; - s.id = skillId.skillName; - s.name = s.id; + s.id = boost::uuids::to_string(id); + s.name = skillId.skillName; s.description = skillDescription.description; s.lastChanged = ""; s.executable = false; s.native = true; - auto providersEntry = fluxioDC.providers.find(skillId.providerId->providerName); - + const auto& providersEntry = + fluxioDC.providers.find(boost::uuids::to_string(providerId)); if (providersEntry != fluxioDC.providers.end()) { s.skillProviderPtr = &(providersEntry->second); } else { - skills::FluxioProvider p; - p.id = skillId.providerId->providerName; - p.name = p.id; - - fluxioDC.providers[p.id] = p; - s.skillProviderPtr = &fluxioDC.providers[p.id]; + const auto& p = addFluxioProvider(skillId.providerId->providerName); + if (p == nullptr) + { + ARMARX_WARNING << "Could not add provider '" << skillId.providerId->providerName + << "'."; + continue; + } + s.skillProviderPtr = &fluxioDC.providers[p->id]; } s.parameters = {}; // add default control flow parameters skills::FluxioParameter start; - start.id = IceUtil::generateUUID(); start.name = "Start"; - start.description = "Start the skill"; start.type = "Event"; + const auto& startIdStr = start.name + start.type + "isInput"; + start.id = boost::uuids::to_string(createUuidWithString(startIdStr, id)); + start.description = "Start the skill"; start.required = true; start.isInput = true; - skills::FluxioParameter success; - success.id = IceUtil::generateUUID(); - success.name = "Succeeded"; - success.description = "Skill has been executed successfully"; - success.type = "Event"; - success.required = false; - success.isInput = false; - - skills::FluxioParameter failure; - failure.id = IceUtil::generateUUID(); - failure.name = "Failed"; - failure.description = "Skill has failed"; - failure.type = "Event"; - failure.required = false; - failure.isInput = false; + skills::FluxioParameter succeeded; + succeeded.name = "Succeeded"; + succeeded.type = "Event"; + const auto& successIdStr = succeeded.name + succeeded.type + "isOutput"; + succeeded.id = boost::uuids::to_string(createUuidWithString(successIdStr, id)); + succeeded.description = "Skill has been executed successfully"; + succeeded.required = false; + succeeded.isInput = false; + + skills::FluxioParameter failed; + failed.name = "Failed"; + failed.type = "Event"; + const auto& failedIdStr = failed.name + failed.type + "isOutput"; + failed.id = boost::uuids::to_string(createUuidWithString(failedIdStr, id)); + failed.description = "Skill has failed"; + failed.required = false; + failed.isInput = false; skills::FluxioParameter aborted; - aborted.id = IceUtil::generateUUID(); aborted.name = "Aborted"; - aborted.description = "Skill has been aborted"; aborted.type = "Event"; + const auto& abortedIdStr = aborted.name + aborted.type + "isOutput"; + aborted.id = boost::uuids::to_string(createUuidWithString(abortedIdStr, id)); + aborted.description = "Skill has been aborted"; aborted.required = false; aborted.isInput = false; s.parameters[start.id] = start; - s.parameters[success.id] = success; - s.parameters[failure.id] = failure; + s.parameters[succeeded.id] = succeeded; + s.parameters[failed.id] = failed; s.parameters[aborted.id] = aborted; if (skillDescription.parametersType != nullptr) @@ -939,7 +1008,6 @@ namespace armarx::plugins return {convertMapValuesToVector(fluxioDC.skills)}; } - skills::Result<skills::FluxioSkill, skills::error::FluxioException> SkillManagerComponentPlugin::getSkill(const std::string& id) { @@ -948,10 +1016,11 @@ namespace armarx::plugins { return {skillsEntry->second}; } + auto error = + skills::error::FluxioException("Skill with ID: " + id + " could not be found."); auto error = skills::error::FluxioException(skills::error::createErrorMessage(skills::error::ErrorCode::SkillNotFound, {id})); error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); return error; - } void @@ -1023,26 +1092,46 @@ namespace armarx::plugins fluxioDC.profiles[profile.id].name = profile.name; fluxioDC.profiles[profile.id].description = profile.description; fluxioDC.profiles[profile.id].parentPtr = profile.parentPtr; - } else { - //TODO: Do we throw the error here? + } + else + { + //TODO: Do we throw the error here? } } + skills::FluxioProvider* + SkillManagerComponentPlugin::addFluxioProvider(const std::string& name) + { + const std::string& providerId = boost::uuids::to_string(createUuidWithString(name)); + + if (fluxioDC.providers.find(providerId) != fluxioDC.providers.end()) + { + ARMARX_WARNING << "Provider with name '" << name << "' already exists."; + return nullptr; + } + + skills::FluxioProvider p; + p.id = providerId; + p.name = name; + + fluxioDC.providers[p.id] = p; + return &fluxioDC.providers[p.id]; + } + skills::Result<std::vector<skills::FluxioProvider>, skills::error::FluxioException> SkillManagerComponentPlugin::getProviderList() { - for (const auto& [providerName, providerPrx] : skillProviderMap) + for (const auto& [providerID, providerPrx] : skillProviderMap) { - const auto& providersEntry = fluxioDC.providers.find(providerName.providerName); + const auto& id = boost::uuids::to_string(createUuidWithString(providerID.providerName)); + const auto& providersEntry = fluxioDC.providers.find(id); if (providersEntry != fluxioDC.providers.end()) { continue; // provider already exists } - //TODO: use real uuids for the provider id - fluxioDC.providers[providerName.providerName] = {providerName.providerName, - providerName.providerName}; + addFluxioProvider(providerID.providerName); } return {convertMapValuesToVector(fluxioDC.providers)}; @@ -1091,71 +1180,91 @@ namespace armarx::plugins return {ret}; } - skills::Result<skills::FluxioSkill, skills::error::FluxioException> SkillManagerComponentPlugin::addSkillToProvider(const std::string& userId, const std::string& providerId, skills::FluxioSkill& skill) { + // check if the provider exists + const auto& providerIt = fluxioDC.providers.find(providerId); + if (providerIt == fluxioDC.providers.end()) + { + auto error = skills::error::FluxioException("FluxioException"); + error.addToContext("Provider with ID: " + providerId + " could not be found.", + "SkillManagerComponentPlugin", + __FUNCTION__, + __LINE__); + return error; + } + + if (skill.id == "") + { + // its a new skill, its a new world, ... + skill.id = IceUtil::generateUUID(); + } + + boost::uuids::uuid uuid; + try + { + boost::uuids::string_generator gen; + uuid = gen(skill.id); + } + catch (std::runtime_error& e) + { + ARMARX_WARNING << "Could not convert skill id to uuid: " << e.what(); + skill.id = IceUtil::generateUUID(); // fallback + } + // add default control flow parameters skills::FluxioParameter start; - start.id = IceUtil::generateUUID(); start.name = "Start"; + start.id = boost::uuids::to_string(createUuidWithString(start.name, uuid)); start.description = "Start the skill"; start.type = "Event"; start.required = true; start.isInput = true; - skills::FluxioParameter success; - success.id = IceUtil::generateUUID(); - success.name = "Succeeded"; - success.description = "Skill has been executed successfully"; - success.type = "Event"; - success.required = false; - success.isInput = false; - - skills::FluxioParameter failure; - failure.id = IceUtil::generateUUID(); - failure.name = "Failed"; - failure.description = "Skill has failed"; - failure.type = "Event"; - failure.required = false; - failure.isInput = false; + skills::FluxioParameter succeeded; + succeeded.name = "Succeeded"; + succeeded.id = boost::uuids::to_string(createUuidWithString(succeeded.name, uuid)); + succeeded.description = "Skill has been executed successfully"; + succeeded.type = "Event"; + succeeded.required = false; + succeeded.isInput = false; + + skills::FluxioParameter failed; + failed.name = "Failed"; + failed.id = boost::uuids::to_string(createUuidWithString(failed.name, uuid)); + failed.description = "Skill has failed"; + failed.type = "Event"; + failed.required = false; + failed.isInput = false; skills::FluxioParameter aborted; - aborted.id = IceUtil::generateUUID(); aborted.name = "Aborted"; + aborted.id = boost::uuids::to_string(createUuidWithString(aborted.name, uuid)); aborted.description = "Skill has been aborted"; aborted.type = "Event"; aborted.required = false; aborted.isInput = false; skill.parameters[start.id] = start; - skill.parameters[success.id] = success; - skill.parameters[failure.id] = failure; + skill.parameters[succeeded.id] = succeeded; + skill.parameters[failed.id] = failed; skill.parameters[aborted.id] = aborted; - // check if the provider exists - const auto& providerIt = fluxioDC.providers.find(providerId); - if (providerIt == fluxioDC.providers.end()) - { - auto error = skills::error::FluxioException(skills::error::createErrorMessage(skills::error::ErrorCode::ProviderNotFound, {providerId})); - error.addToContext(std::nullopt, "SkillManagerComponentPlugin", __FUNCTION__, __LINE__); - return error; - } - // set id, lastChanged and store in data collector - std::string id = IceUtil::generateUUID(); - fluxioDC.skills[id] = skill; - fluxioDC.skills[id].id = id; - fluxioDC.skills[id].lastChanged = armarx::DateTime::Now().toDateTimeString(); + skill.lastChanged = armarx::DateTime::Now().toDateTimeString(); + + // store in data collector + fluxioDC.skills[skill.id] = skill; // set mutex - setEditFluxioSkillMutex(true, userId, id); + setEditFluxioSkillMutex(true, userId, skill.id); - return {fluxioDC.skills[id]}; + return {fluxioDC.skills[skill.id]}; } - skills::Result<bool,skills::error::FluxioException> + skills::Result<bool, skills::error::FluxioException> SkillManagerComponentPlugin::setEditFluxioSkillMutex(bool aquireMutex, const std::string& userId, const std::string& skillId) @@ -1208,7 +1317,7 @@ namespace armarx::plugins if (std::get<0>(fluxioDC.skillMutexMap[skillId]) == userId) { fluxioDC.skillMutexMap.erase(skillId); - return {true}; + return {true}; } // mutex could not be removed diff --git a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.h b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.h index d27b6099ae44c214852c3030ff365e35dbda9a4d..c4151d91153e819ecde36206e9946d06f324d9a3 100644 --- a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.h +++ b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPlugin.h @@ -1,8 +1,11 @@ #pragma once #include <mutex> +#include <optional> #include <string> +#include <boost/uuid/uuid.hpp> + #include <ArmarXCore/core/ComponentPlugin.h> #include <ArmarXCore/core/ManagedIceObject.h> #include <ArmarXCore/core/time/DateTime.h> @@ -16,15 +19,15 @@ #include <RobotAPI/libraries/skills/core/FluxioParameter.h> #include <RobotAPI/libraries/skills/core/FluxioProfile.h> #include <RobotAPI/libraries/skills/core/FluxioProvider.h> +#include <RobotAPI/libraries/skills/core/FluxioResult.h> #include <RobotAPI/libraries/skills/core/FluxioSkill.h> #include <RobotAPI/libraries/skills/core/FluxioValue.h> #include <RobotAPI/libraries/skills/core/ProviderID.h> #include <RobotAPI/libraries/skills/core/ProviderInfo.h> -#include <RobotAPI/libraries/skills/core/FluxioResult.h> #include <RobotAPI/libraries/skills/core/SkillExecutionRequest.h> #include <RobotAPI/libraries/skills/core/SkillStatusUpdate.h> +#include <RobotAPI/libraries/skills/core/executor/FluxioExecutor.h> -#include "FluxioExecutor.h" #include "SkillManagerComponentPluginUser.h" namespace armarx @@ -34,8 +37,6 @@ namespace armarx namespace armarx::plugins { - class FluxioExecutor; // forward declaration - class SkillManagerComponentPlugin : public ComponentPlugin { public: @@ -77,21 +78,24 @@ namespace armarx::plugins //** Fluxio related methods **// //****************************// - FluxioExecutor* executeFluxioSkill(std::string skillId, - std::string executorName = "Fluxio"); + skills::FluxioExecutor* executeFluxioSkill(const std::string& skillId, + const std::string& executorName); void abortFluxioSkill(const std::string& executionId); skills::Result<std::vector<skills::FluxioSkillStatusUpdate>, skills::error::FluxioException> getFluxioSkillExecutionStatus(const std::string& executionId); - skills::Result<std::vector<skills::FluxioSkill>, skills::error::FluxioException> getSkillList(); + skills::Result<std::vector<skills::FluxioSkill>, skills::error::FluxioException> + getSkillList(); - skills::Result<skills::FluxioSkill, skills::error::FluxioException>getSkill(const std::string& id); + skills::Result<skills::FluxioSkill, skills::error::FluxioException> + getSkill(const std::string& id); void removeSkill(const std::string& id); - skills::Result<bool, skills::error::FluxioException> getSkillMutex(const std::string& skillId, const std::string& userId); + skills::Result<bool, skills::error::FluxioException> + getSkillMutex(const std::string& skillId, const std::string& userId); void deleteSkillMutex(const std::string& skillId, const std::string& userId); @@ -99,33 +103,42 @@ namespace armarx::plugins const std::string& skillId, const std::string& parameterId); - skills::Result<std::vector<skills::FluxioProfile>, skills::error::FluxioException> getProfileList(); + skills::Result<std::vector<skills::FluxioProfile>, skills::error::FluxioException> + getProfileList(); - skills::Result<skills::FluxioProfile, skills::error::FluxioException> getProfile(const std::string& id); + skills::Result<skills::FluxioProfile, skills::error::FluxioException> + getProfile(const std::string& id); - skills::Result<skills::FluxioProfile, skills::error::FluxioException> createProfile(const skills::FluxioProfile& profile); + skills::Result<skills::FluxioProfile, skills::error::FluxioException> + createProfile(const skills::FluxioProfile& profile); void updateProfile(const skills::FluxioProfile& profile); - skills::Result<std::vector<skills::FluxioProvider>, skills::error::FluxioException> getProviderList(); + skills::Result<std::vector<skills::FluxioProvider>, skills::error::FluxioException> + getProviderList(); + + skills::Result<std::optional<skills::FluxioProvider>, skills::error::FluxioException> + getProvider(const std::string& id); - skills::Result<std::optional<skills::FluxioProvider>, skills::error::FluxioException> getProvider(const std::string& id); + skills::FluxioProvider* addFluxioProvider(const std::string& name); - skills::Result<std::vector<skills::FluxioSkill>, skills::error::FluxioException> getSkillsOfProvider(const std::string& id); + skills::Result<std::vector<skills::FluxioSkill>, skills::error::FluxioException> + getSkillsOfProvider(const std::string& id); - skills::Result<skills::FluxioSkill, skills::error::FluxioException> addSkillToProvider(const std::string& userId, - const std::string& providerId, - skills::FluxioSkill& skill); + skills::Result<skills::FluxioSkill, skills::error::FluxioException> + addSkillToProvider(const std::string& userId, + const std::string& providerId, + skills::FluxioSkill& skill); private: struct FluxioDataCollector { - std::map<std::string, skills::FluxioSkill> skills = {}; - std::map<std::string, skills::FluxioProfile> profiles = {}; - std::map<std::string, skills::FluxioProvider> providers = {}; - std::map<std::string, std::tuple<std::string, armarx::DateTime>> skillMutexMap = {}; + std::map<std::string, skills::FluxioSkill> skills; + std::map<std::string, skills::FluxioProfile> profiles; + std::map<std::string, skills::FluxioProvider> providers; + std::map<std::string, std::tuple<std::string, armarx::DateTime>> skillMutexMap; const std::int64_t mutexTimeout = 30; // minutes - std::map<std::string, FluxioExecutor*> fluxioExecutors = {}; + std::map<std::string, skills::FluxioExecutor*> fluxioExecutors; }; FluxioDataCollector fluxioDC = {}; @@ -139,6 +152,10 @@ namespace armarx::plugins template <typename S, typename T> std::vector<T> convertMapValuesToVector(std::map<S, T>& map); + static boost::uuids::uuid + createUuidWithString(const std::string& str, + std::optional<const boost::uuids::uuid> namespaceUUID = std::nullopt); + static void convertAronObjectPtrToFluxioParameters(std::map<std::string, skills::FluxioParameter>& ret, const aron::type::ObjectPtr& ptr, @@ -148,9 +165,10 @@ namespace armarx::plugins void replaceVectorContent(std::vector<T>& originalVector, const std::vector<T>& replacementVector); - skills::Result<bool, skills::error::FluxioException> setEditFluxioSkillMutex(bool aquireMutex, - const std::string& userId, - const std::string& skillId); + skills::Result<bool, skills::error::FluxioException> + setEditFluxioSkillMutex(bool aquireMutex, + const std::string& userId, + const std::string& skillId); friend class armarx::SkillManagerComponentPluginUser; }; diff --git a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPluginUser.cpp b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPluginUser.cpp index dcde3a22c4b5082425cf02d53de735ea95e677e0..5759d5ecd61eb869947c83cd20efa51ea6aff156 100644 --- a/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPluginUser.cpp +++ b/source/RobotAPI/libraries/skills/manager/SkillManagerComponentPluginUser.cpp @@ -170,7 +170,7 @@ namespace armarx SkillManagerComponentPluginUser::executeFluxioSkill(const std::string& skillId, const Ice::Current& current) { - const auto& res = this->plugin->executeFluxioSkill(skillId); + const auto& res = this->plugin->executeFluxioSkill(skillId, "Fluxio"); if (res != nullptr) { return res->id; diff --git a/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.cpp b/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.cpp index 8eaa3633f7248dcb7c78b91c16874ccc7811a407..45925621cbb9871c9a49786e3b9b8ce124742251 100644 --- a/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.cpp +++ b/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.cpp @@ -101,13 +101,16 @@ namespace armarx::skills::gui this->skillDetailsTreeWidget = new SkillDetailsTreeWidget(this->memory, this); this->skillDescriptionWidget = new SkillDescriptionWidget(this); this->layout = new QVBoxLayout(); + this->splitter = new QSplitter(); + splitter->setOrientation(Qt::Orientation::Vertical); // Layouting this->setLayout(layout); layout->addWidget(this->profileMenuWidget); - layout->addWidget(this->skillDescriptionWidget); - layout->addWidget(this->skillDetailsTreeWidget); + layout->addWidget(splitter); + splitter->addWidget(this->skillDescriptionWidget); + splitter->addWidget(this->skillDetailsTreeWidget); layout->addWidget(this->executeSkillButton); // Text diff --git a/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.h b/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.h index 1723870bc6fb5a0c32c24efad41d17c9baf0fb58..3d2a56b14c7c6cc224e7daa18d5df5d3375e2b62 100644 --- a/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.h +++ b/source/RobotAPI/libraries/skills_gui/skill_details/SkillDetailsGroupBox.h @@ -3,6 +3,7 @@ #include <QGroupBox> #include <QPushButton> +#include <QSplitter> #include <QVBoxLayout> #include "RobotAPI/libraries/skills_gui/aron_tree_widget/AronTreeWidgetController.h" @@ -50,6 +51,7 @@ namespace armarx::skills::gui void connectSignals(); QVBoxLayout* layout = nullptr; + QSplitter* splitter = nullptr; QPushButton* executeSkillButton = nullptr; SkillDetailsTreeWidget* skillDetailsTreeWidget = nullptr; SkillDescriptionWidget* skillDescriptionWidget = nullptr; diff --git a/source/RobotAPI/libraries/skills_gui/skills/SkillGroupBox.cpp b/source/RobotAPI/libraries/skills_gui/skills/SkillGroupBox.cpp index 30b26364853dd4d96fb159847edb575b372ae833..d57753f5ce0ca37536296237ca5d29e1d733f8de 100644 --- a/source/RobotAPI/libraries/skills_gui/skills/SkillGroupBox.cpp +++ b/source/RobotAPI/libraries/skills_gui/skills/SkillGroupBox.cpp @@ -78,6 +78,7 @@ namespace armarx::skills::gui connect( this->acceptSearchButton, &QPushButton::clicked, this, &SkillGroupBox::filterAndFetch); connect(this->searchBar, &QLineEdit::editingFinished, this, &SkillGroupBox::handleSearch); + connect(this->searchBar, &QLineEdit::textChanged, this, &SkillGroupBox::handleSearch); connect(this, &SkillGroupBox::searchRequest, this->memory.get(),