51 using RosServiceNode::RosServiceNode;
53 static BT::PortsList providedPorts()
58 BT::InputPort<std::string>(INPUT_KEY_CONTAINER,
"Name of the component container to load the node into."),
59 BT::InputPort<std::string>(INPUT_KEY_PACKAGE,
"Name of the ROS 2 package containing the composable node plugin."),
60 BT::InputPort<std::string>(INPUT_KEY_PLUGIN,
"Name of the composable node plugin to load."),
61 BT::InputPort<std::string>(
62 INPUT_KEY_NODE_NAME,
"",
"Assigned name of the loaded node. Leave empty to use the plugin's default name."),
63 BT::InputPort<std::string>(
64 INPUT_KEY_NODE_NAMESPACE,
"",
65 "Assigned namespace of the loaded node. Leave empty to use the plugin's default namespace."),
66 BT::InputPort<std::string>(
67 INPUT_KEY_LOG_LEVEL,
"UNSET",
68 "Log level for the loaded node. Must be one of [UNSET, DEBUG, INFO, WARN, ERROR, FATAL] "
69 "(case-insensitive). UNSET (default) means the container's log level is inherited."),
70 BT::InputPort<std::string>(
71 INPUT_KEY_REMAP_RULES,
"",
72 "Semicolon-separated list of static remapping rules applied to the loaded node "
73 "(e.g. 'old_topic:=new_topic;foo:=bar')."),
74 BT::OutputPort<uint64_t>(
75 PORT_KEY_UNIQUE_ID,
"Container-local unique ID assigned to the loaded node (0 if loading failed)."),
76 BT::OutputPort<std::string>(
77 OUTPUT_KEY_FULL_NODE_NAME,
"Full node name (namespace/name) of the loaded node (empty if loading failed)."),
81 bool setRequest(Request::SharedPtr & request)
override final
83 const BT::Expected<std::string> expected_package = getInput<std::string>(INPUT_KEY_PACKAGE);
84 if (!expected_package || expected_package.value().empty()) {
86 logger_,
"%s - Package name must not be empty.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
87 RCLCPP_DEBUG_EXPRESSION(
88 logger_, !expected_package,
"%s - Error message: %s", context_.getFullyQualifiedTreeNodeName(
this).c_str(),
89 expected_package.error().c_str());
92 request->package_name = expected_package.value();
94 const BT::Expected<std::string> expected_plugin = getInput<std::string>(INPUT_KEY_PLUGIN);
95 if (!expected_plugin || expected_plugin.value().empty()) {
97 logger_,
"%s - Plugin name must not be empty.", context_.getFullyQualifiedTreeNodeName(
this).c_str());
98 RCLCPP_DEBUG_EXPRESSION(
99 logger_, !expected_plugin,
"%s - Error message: %s", context_.getFullyQualifiedTreeNodeName(
this).c_str(),
100 expected_plugin.error().c_str());
103 request->plugin_name = expected_plugin.value();
104 requested_plugin_name_ = expected_plugin.value();
106 request->node_name = getInput<std::string>(INPUT_KEY_NODE_NAME).value_or(
"");
107 request->node_namespace = getInput<std::string>(INPUT_KEY_NODE_NAMESPACE).value_or(
"");
110 setOutput(PORT_KEY_UNIQUE_ID, uint64_t{0});
111 setOutput(OUTPUT_KEY_FULL_NODE_NAME, std::string{});
114 const std::string level_str = getInput<std::string>(INPUT_KEY_LOG_LEVEL).value_or(
"UNSET");
117 rcutils_logging_severity_level_from_string(level_str.c_str(), rcutils_get_default_allocator(), &log_level) !=
119 const std::string error = rcutils_get_error_string().str;
120 rcutils_reset_error();
122 logger_,
"%s - Cannot convert log level '%s' to a valid severity: %s",
123 context_.getFullyQualifiedTreeNodeName(
this).c_str(), level_str.c_str(), error.c_str());
126 request->log_level =
static_cast<uint8_t
>(log_level);
129 const std::string remap_str = getInput<std::string>(INPUT_KEY_REMAP_RULES).value_or(
"");
130 if (!remap_str.empty()) {
131 std::istringstream ss(remap_str);
133 while (std::getline(ss, rule,
';')) {
134 const auto first = rule.find_first_not_of(
" \t");
135 const auto last = rule.find_last_not_of(
" \t");
136 if (first != std::string::npos) {
137 request->remap_rules.push_back(rule.substr(first, last - first + 1));
145 BT::NodeStatus onResponseReceived(
const Response::SharedPtr & response)
override final
147 if (!response->success) {
149 logger_,
"%s - Failed to load composable node '%s' via service '%s': %s",
150 context_.getFullyQualifiedTreeNodeName(
this).c_str(), requested_plugin_name_.c_str(),
getServiceName().c_str(),
151 response->error_message.c_str());
152 return BT::NodeStatus::FAILURE;
154 setOutput(PORT_KEY_UNIQUE_ID, response->unique_id);
155 setOutput(OUTPUT_KEY_FULL_NODE_NAME, response->full_node_name);
156 return BT::NodeStatus::SUCCESS;
160 std::string requested_plugin_name_;
173 using RosServiceNode::RosServiceNode;
175 static BT::PortsList providedPorts()
180 BT::InputPort<std::string>(INPUT_KEY_CONTAINER,
"Name of the component container to unload the node from."),
181 BT::InputPort<uint64_t>(PORT_KEY_UNIQUE_ID,
"Container-local unique ID of the composable node to unload."),
185 bool setRequest(Request::SharedPtr & request)
override final
187 const BT::Expected<uint64_t> expected_id = getInput<uint64_t>(PORT_KEY_UNIQUE_ID);
190 logger_,
"%s - %s", context_.getFullyQualifiedTreeNodeName(
this).c_str(), expected_id.error().c_str());
193 request->unique_id = expected_id.value();
194 requested_unique_id_ = expected_id.value();
198 BT::NodeStatus onResponseReceived(
const Response::SharedPtr & response)
override final
200 if (!response->success) {
202 logger_,
"%s - Failed to unload composable node (id: %s) via service '%s': %s",
203 context_.getFullyQualifiedTreeNodeName(
this).c_str(), std::to_string(requested_unique_id_).c_str(),
205 return BT::NodeStatus::FAILURE;
207 return BT::NodeStatus::SUCCESS;
211 uint64_t requested_unique_id_{0};