Skip to content

Commit 6f9de32

Browse files
committed
first rough working poc of distributed chained controllers
1 parent 92e2965 commit 6f9de32

File tree

6 files changed

+255
-8
lines changed

6 files changed

+255
-8
lines changed

controller_manager/include/controller_manager/controller_manager.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,9 @@ class ControllerManager : public rclcpp::Node
259259
CONTROLLER_MANAGER_PUBLIC
260260
void register_sub_controller_manager();
261261

262+
CONTROLLER_MANAGER_PUBLIC
263+
void register_reference_interfaces(const std::vector<std::string> & reference_interfaces_names);
264+
262265
CONTROLLER_MANAGER_PUBLIC
263266
controller_interface::ControllerInterfaceBaseSharedPtr add_controller_impl(
264267
const ControllerSpec & controller);

controller_manager/src/controller_manager.cpp

Lines changed: 167 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#include "controller_interface/controller_interface_base.hpp"
2525
#include "controller_manager_msgs/msg/hardware_component_state.hpp"
26+
#include "controller_manager_msgs/msg/publisher_description.hpp"
2627

2728
#include "hardware_interface/distributed_control_interface/command_forwarder.hpp"
2829
#include "hardware_interface/distributed_control_interface/state_publisher.hpp"
@@ -645,6 +646,55 @@ void ControllerManager::register_sub_controller_manager_references_srv_cb(
645646
std::shared_ptr<controller_manager_msgs::srv::RegisterSubControllerManagerReferences::Response>
646647
response)
647648
{
649+
std::lock_guard<std::mutex> guard(central_controller_manager_srv_lock_);
650+
651+
// only command interfaces can be state publishers. We initialize state interfaces to empty list.
652+
std::vector<controller_manager_msgs::msg::PublisherDescription> empty_state_publishers{};
653+
auto sub_ctrl_mng_wrapper = std::make_shared<distributed_control::SubControllerManagerWrapper>(
654+
request->sub_controller_manager_namespace, request->sub_controller_manager_name,
655+
empty_state_publishers, request->command_state_publishers);
656+
657+
std::vector<std::shared_ptr<hardware_interface::DistributedReadWriteHandle>>
658+
distributed_command_interfaces;
659+
distributed_command_interfaces.reserve(sub_ctrl_mng_wrapper->get_command_forwarder_count());
660+
// create distributed command interface and import into resource storage.
661+
distributed_command_interfaces =
662+
resource_manager_->import_reference_interfaces_of_sub_controller_manager(
663+
sub_ctrl_mng_wrapper, get_namespace(), distributed_pub_sub_node_);
664+
665+
for (const auto & command_interface : distributed_command_interfaces)
666+
{
667+
// register every node of command_interface at executor only if multiple nodes
668+
// are used. Otherwise the single nodes has already been added
669+
if (use_multiple_nodes())
670+
{
671+
try
672+
{
673+
executor_->add_node(command_interface->get_node()->get_node_base_interface());
674+
}
675+
catch (const std::runtime_error & e)
676+
{
677+
response->ok = false;
678+
RCLCPP_WARN_STREAM(
679+
get_logger(),
680+
"ControllerManager: Caught exception while trying to register node of reference "
681+
"interface of sub_controller_manager. Exception:"
682+
<< e.what());
683+
}
684+
}
685+
auto msg = controller_manager_msgs::msg::PublisherDescription();
686+
msg.ns = get_namespace();
687+
msg.name.prefix_name = command_interface->get_prefix_name();
688+
msg.name.interface_name = command_interface->get_interface_name();
689+
// TODO(Manuel): want topic name relative to namespace, but have to treat "root" namespace separate
690+
msg.publisher_topic = std::string("/") + command_interface->forward_command_topic_name();
691+
response->command_state_publishers.push_back(msg);
692+
}
693+
694+
response->ok = true;
695+
RCLCPP_INFO_STREAM(
696+
get_logger(), "ControllerManager: Registered reference interfaces of sub_controller_manager <"
697+
<< sub_ctrl_mng_wrapper->get_name() << ">.");
648698
}
649699

650700
void ControllerManager::create_hardware_state_publishers(
@@ -829,6 +879,112 @@ void ControllerManager::register_sub_controller_manager()
829879
}
830880
}
831881

882+
void ControllerManager::register_reference_interfaces(
883+
const std::vector<std::string> & reference_interfaces_names)
884+
{
885+
RCLCPP_INFO_STREAM(
886+
get_logger(), "SubControllerManager:<" << get_namespace() << "/" << get_name()
887+
<< "> trying to register reference interfaces.");
888+
rclcpp::Client<controller_manager_msgs::srv::RegisterSubControllerManagerReferences>::SharedPtr
889+
client = create_client<controller_manager_msgs::srv::RegisterSubControllerManagerReferences>(
890+
"/register_sub_controller_manager_references");
891+
892+
auto request = std::make_shared<
893+
controller_manager_msgs::srv::RegisterSubControllerManagerReferences::Request>();
894+
request->sub_controller_manager_namespace = get_namespace();
895+
request->sub_controller_manager_name = get_name();
896+
897+
// export the provided CommandForwarders
898+
for (auto const & reference_interface_name : reference_interfaces_names)
899+
{
900+
auto [found, command_forwarder] =
901+
resource_manager_->find_command_forwarder(reference_interface_name);
902+
if (found)
903+
{
904+
// create description of StatePublisher including: prefix_name, interface_name and topic.
905+
// So that receiver is able to create a DistributedStateInterface which subscribes to the
906+
// topics provided by this sub controller manager
907+
request->command_state_publishers.push_back(
908+
command_forwarder->create_publisher_description_msg());
909+
}
910+
else
911+
{
912+
RCLCPP_WARN_STREAM(
913+
get_logger(), "SubControllerManager: <"
914+
<< get_namespace() << "/" << get_name()
915+
<< "> could not find command_forwarder for reference interfaces:"
916+
<< reference_interface_name);
917+
}
918+
}
919+
920+
using namespace std::chrono_literals;
921+
while (!client->wait_for_service(1s))
922+
{
923+
if (!rclcpp::ok())
924+
{
925+
RCLCPP_ERROR_STREAM(
926+
get_logger(), "SubControllerManager:<"
927+
<< get_namespace() << "/" << get_name()
928+
<< ">. Interrupted while waiting for central controller managers "
929+
"register_sub_controller_manager_references service. Exiting.");
930+
return;
931+
}
932+
RCLCPP_INFO_STREAM(
933+
get_logger(), "SubControllerManager:<"
934+
<< get_namespace() << "/" << get_name()
935+
<< ">. Central controller managers "
936+
"register_sub_controller_manager_references service not available, "
937+
"waiting again...");
938+
}
939+
940+
auto result = client->async_send_request(request);
941+
// TODO(Manuel): first try to wait synchronous. If this doesn't work we might have to create a
942+
// queue or something similar, add the future and check in update periodically if finished.
943+
944+
// This blocks... which might be bad...
945+
result.wait();
946+
// can call get only once
947+
auto res = result.get();
948+
if (res->ok)
949+
{
950+
auto command_state_publishers = res->command_state_publishers;
951+
// TODO(Manuel) we should probably make the keys explicit (add key_generation function to handles)
952+
// send keys with request
953+
for (const auto & command_state_publisher : command_state_publishers)
954+
{
955+
std::string key = command_state_publisher.name.prefix_name + "/" +
956+
command_state_publisher.name.interface_name;
957+
auto [found, command_forwarder] = resource_manager_->find_command_forwarder(key);
958+
if (found)
959+
{
960+
RCLCPP_WARN_STREAM(
961+
get_logger(), "SubControllerManager: <" << get_namespace() << "/" << get_name()
962+
<< "> found commad forwarder for" << key);
963+
command_forwarder->subscribe_to_command_publisher(command_state_publisher.publisher_topic);
964+
}
965+
else
966+
{
967+
RCLCPP_WARN_STREAM(
968+
get_logger(), "SubControllerManager:<"
969+
<< get_namespace() << "/" << get_name()
970+
<< ">. Could not find a CommandForwarder for key[" << key
971+
<< "]. No subscription to command state possible.");
972+
}
973+
}
974+
RCLCPP_INFO_STREAM(
975+
get_logger(), "SubControllerManager:<" << get_namespace() << "/" << get_name()
976+
<< ">. Successfully registered.");
977+
}
978+
else
979+
{
980+
RCLCPP_WARN_STREAM(
981+
get_logger(), "SubControllerManager: <"
982+
<< get_namespace() << "/" << get_name()
983+
<< ">. Registration of StatePublishers failed. Central ControllerManager "
984+
"returned error code.");
985+
}
986+
}
987+
832988
controller_interface::ControllerInterfaceBaseSharedPtr ControllerManager::load_controller(
833989
const std::string & controller_name, const std::string & controller_type)
834990
{
@@ -1069,10 +1225,19 @@ controller_interface::return_type ControllerManager::configure_controller(
10691225

10701226
if (is_sub_controller_manager())
10711227
{
1228+
// TODO(Manuel); This is only for fast poc, chaining of multiples in sub controller
1229+
// is most likely going to lead to issues if handled this way.
1230+
// We should only allow the first controller in the chain to be distributed in each
1231+
// sub controller manager and chain the successor locally in sub controller manager
1232+
// instead of exporting for every.
1233+
1234+
// Set chained mode as default true and make references available so that
1235+
// hardware_command_forwarders can be created.
1236+
controller->set_chained_mode(true);
1237+
resource_manager_->make_controller_reference_interfaces_available(controller_name);
10721238
// export all of the just created reference interfaces by default
10731239
create_hardware_command_forwarders(reference_interfaces_names);
1074-
1075-
// TODO(Manuel) : register
1240+
register_reference_interfaces(reference_interfaces_names);
10761241
}
10771242

10781243
// TODO(destogl): check and resort controllers in the vector

hardware_interface/include/hardware_interface/distributed_control_interface/sub_controller_manager_wrapper.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,12 @@ class SubControllerManagerWrapper final
5252

5353
size_t get_command_forwarder_count() const { return command_forwarder_descriptions_.size(); }
5454

55+
std::string type() const { return TYPE_; }
56+
5557
private:
5658
const std::string NAMESPACE_;
5759
const std::string NAME_;
60+
const std::string TYPE_ = "sub_controller_manager";
5861
std::vector<PublisherDescription> state_publisher_descriptions_;
5962
std::vector<PublisherDescription> command_forwarder_descriptions_;
6063
};

hardware_interface/include/hardware_interface/hardware_component_info.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ struct HardwareComponentInfo
3636
/// Component name.
3737
std::string name;
3838

39-
/// Component "classification": "actuator", "sensor" or "system"
39+
/// Component "classification": "actuator", "sensor", "system" or "sub_controller_manager"
4040
std::string type;
4141

4242
/// Component pluginlib plugin name.

hardware_interface/include/hardware_interface/resource_manager.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,11 @@ class HARDWARE_INTERFACE_PUBLIC ResourceManager
142142
std::shared_ptr<distributed_control::SubControllerManagerWrapper> sub_controller_manager,
143143
const std::string & ns, std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node);
144144

145+
std::vector<std::shared_ptr<DistributedReadWriteHandle>>
146+
import_reference_interfaces_of_sub_controller_manager(
147+
std::shared_ptr<distributed_control::SubControllerManagerWrapper> sub_controller_manager,
148+
const std::string & ns, std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node);
149+
145150
void add_hardware_state_publishers(
146151
std::shared_ptr<distributed_control::StatePublisher> state_publisher);
147152

hardware_interface/src/resource_manager.cpp

Lines changed: 76 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -665,8 +665,13 @@ class ResourceStorage
665665
void add_sub_controller_manager(
666666
std::shared_ptr<distributed_control::SubControllerManagerWrapper> sub_controller_manager)
667667
{
668-
const auto [it, success] = sub_controller_manager_map_.insert(
669-
std::pair{sub_controller_manager->get_name(), sub_controller_manager});
668+
HardwareComponentInfo component_info;
669+
component_info.name = sub_controller_manager->get_name();
670+
component_info.type = sub_controller_manager->type();
671+
component_info.state_interfaces = {};
672+
component_info.command_interfaces = {};
673+
const auto [it, success] =
674+
hardware_info_map_.insert(std::pair{component_info.name, component_info});
670675
if (!success)
671676
{
672677
std::string msg(
@@ -786,6 +791,64 @@ class ResourceStorage
786791
return distributed_command_interfaces;
787792
}
788793

794+
std::vector<std::shared_ptr<DistributedReadWriteHandle>> import_distributed_reference_interfaces(
795+
std::shared_ptr<distributed_control::SubControllerManagerWrapper> sub_controller_manager,
796+
const std::string & ns, std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node)
797+
{
798+
std::vector<std::shared_ptr<DistributedReadWriteHandle>> distributed_command_interfaces;
799+
distributed_command_interfaces.reserve(sub_controller_manager->get_command_forwarder_count());
800+
std::vector<std::string> interface_names;
801+
interface_names.reserve(sub_controller_manager->get_command_forwarder_count());
802+
803+
for (const auto & command_forwarder_description :
804+
sub_controller_manager->get_command_forwarder_descriptions())
805+
{
806+
// create StateInterface from the Description and store in ResourceStorage.
807+
auto command_interface =
808+
std::make_shared<DistributedReadWriteHandle>(command_forwarder_description, ns, node);
809+
add_command_interface(command_interface);
810+
// add to return vector, node needs to added to executor.
811+
distributed_command_interfaces.push_back(command_interface);
812+
// TODO(Manuel) this should be handled at one point DRY (adding, claimed ....), key should be made explicit
813+
claimed_command_interface_map_.emplace(std::make_pair(command_interface->get_name(), false));
814+
interface_names.push_back(command_interface->get_name());
815+
}
816+
// TODO(Manuel) this should be handled at one point DRY(adding, claimed,make available....), key should be made explicit
817+
available_command_interfaces_.reserve(
818+
available_command_interfaces_.capacity() + interface_names.size());
819+
auto command_interfaces_end =
820+
hardware_info_map_[sub_controller_manager->get_name()].command_interfaces.end();
821+
hardware_info_map_[sub_controller_manager->get_name()].command_interfaces.insert(
822+
command_interfaces_end, interface_names.begin(), interface_names.end());
823+
824+
for (const auto & interface : interface_names)
825+
{
826+
// TODO(destogl): check if interface should be available on configure
827+
auto found_it = std::find(
828+
available_command_interfaces_.begin(), available_command_interfaces_.end(), interface);
829+
830+
if (found_it == available_command_interfaces_.end())
831+
{
832+
available_command_interfaces_.emplace_back(interface);
833+
RCUTILS_LOG_DEBUG_NAMED(
834+
"resource_manager", "(hardware '%s'): '%s' command interface added into available list",
835+
sub_controller_manager->get_name().c_str(), interface.c_str());
836+
}
837+
else
838+
{
839+
// TODO(destogl): do here error management if interfaces are only partially added into
840+
// "available" list - this should never be the case!
841+
RCUTILS_LOG_WARN_NAMED(
842+
"resource_manager",
843+
"(hardware '%s'): '%s' command interface already in available list."
844+
" This can happen due to multiple calls to 'configure'",
845+
sub_controller_manager->get_name().c_str(), interface.c_str());
846+
}
847+
}
848+
849+
return distributed_command_interfaces;
850+
}
851+
789852
// hardware plugins
790853
pluginlib::ClassLoader<ActuatorInterface> actuator_loader_;
791854
pluginlib::ClassLoader<SensorInterface> sensor_loader_;
@@ -821,9 +884,6 @@ class ResourceStorage
821884

822885
std::map<std::string, std::shared_ptr<distributed_control::CommandForwarder>>
823886
command_interface_command_forwarder_map_;
824-
825-
std::map<std::string, std::shared_ptr<distributed_control::SubControllerManagerWrapper>>
826-
sub_controller_manager_map_;
827887
};
828888

829889
ResourceManager::ResourceManager() : resource_storage_(std::make_unique<ResourceStorage>()) {}
@@ -957,6 +1017,16 @@ ResourceManager::import_command_interfaces_of_sub_controller_manager(
9571017
return resource_storage_->import_distributed_command_interfaces(sub_controller_manager, ns, node);
9581018
}
9591019

1020+
std::vector<std::shared_ptr<DistributedReadWriteHandle>>
1021+
ResourceManager::import_reference_interfaces_of_sub_controller_manager(
1022+
std::shared_ptr<distributed_control::SubControllerManagerWrapper> sub_controller_manager,
1023+
const std::string & ns, std::shared_ptr<rclcpp_lifecycle::LifecycleNode> node)
1024+
{
1025+
std::lock_guard<std::recursive_mutex> guard(resource_interfaces_lock_);
1026+
return resource_storage_->import_distributed_reference_interfaces(
1027+
sub_controller_manager, ns, node);
1028+
}
1029+
9601030
void ResourceManager::add_hardware_state_publishers(
9611031
std::shared_ptr<distributed_control::StatePublisher> state_publisher)
9621032
{
@@ -988,6 +1058,7 @@ ResourceManager::get_command_forwarders() const
9881058
std::pair<bool, std::shared_ptr<distributed_control::CommandForwarder>>
9891059
ResourceManager::find_command_forwarder(const std::string & key)
9901060
{
1061+
std::lock_guard<std::recursive_mutex> guard(resource_interfaces_lock_);
9911062
return resource_storage_->find_command_forwarder(key);
9921063
}
9931064

0 commit comments

Comments
 (0)