|
23 | 23 |
|
24 | 24 | #include "controller_interface/controller_interface_base.hpp" |
25 | 25 | #include "controller_manager_msgs/msg/hardware_component_state.hpp" |
| 26 | +#include "controller_manager_msgs/msg/publisher_description.hpp" |
26 | 27 |
|
27 | 28 | #include "hardware_interface/distributed_control_interface/command_forwarder.hpp" |
28 | 29 | #include "hardware_interface/distributed_control_interface/state_publisher.hpp" |
@@ -645,6 +646,55 @@ void ControllerManager::register_sub_controller_manager_references_srv_cb( |
645 | 646 | std::shared_ptr<controller_manager_msgs::srv::RegisterSubControllerManagerReferences::Response> |
646 | 647 | response) |
647 | 648 | { |
| 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() << ">."); |
648 | 698 | } |
649 | 699 |
|
650 | 700 | void ControllerManager::create_hardware_state_publishers( |
@@ -829,6 +879,112 @@ void ControllerManager::register_sub_controller_manager() |
829 | 879 | } |
830 | 880 | } |
831 | 881 |
|
| 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 | + |
832 | 988 | controller_interface::ControllerInterfaceBaseSharedPtr ControllerManager::load_controller( |
833 | 989 | const std::string & controller_name, const std::string & controller_type) |
834 | 990 | { |
@@ -1069,10 +1225,19 @@ controller_interface::return_type ControllerManager::configure_controller( |
1069 | 1225 |
|
1070 | 1226 | if (is_sub_controller_manager()) |
1071 | 1227 | { |
| 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); |
1072 | 1238 | // export all of the just created reference interfaces by default |
1073 | 1239 | create_hardware_command_forwarders(reference_interfaces_names); |
1074 | | - |
1075 | | - // TODO(Manuel) : register |
| 1240 | + register_reference_interfaces(reference_interfaces_names); |
1076 | 1241 | } |
1077 | 1242 |
|
1078 | 1243 | // TODO(destogl): check and resort controllers in the vector |
|
0 commit comments