Skip to content

Commit 113bdd2

Browse files
committed
refactor(gateway): extract declare_plugin_params_from_yaml to shared header
- Move function to param_utils.hpp so test uses the real implementation instead of a diverging copy - Use unique_ptr RAII guard for rcl_params_t instead of manual cleanup - Log param name on declaration failure instead of silent catch(...) - Warn when encountering unsupported array param types
1 parent cab12f5 commit 113bdd2

3 files changed

Lines changed: 81 additions & 96 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2026 bburda
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
#pragma once
16+
17+
#include <memory>
18+
#include <string>
19+
20+
#include <rcl/arguments.h>
21+
#include <rcl_yaml_param_parser/parser.h>
22+
#include <rclcpp/rclcpp.hpp>
23+
24+
namespace ros2_medkit_gateway {
25+
26+
/// Declare plugin config parameters from the global --params-file YAML.
27+
///
28+
/// Parameters from --params-file go into the ROS 2 global rcl context,
29+
/// NOT into NodeOptions::parameter_overrides(). We must discover and
30+
/// declare them explicitly so list_parameters()/get_parameter() can find them.
31+
inline void declare_plugin_params_from_yaml(rclcpp::Node * node, const std::string & prefix,
32+
const std::string & path_key = "") {
33+
auto rcl_ctx = node->get_node_base_interface()->get_context()->get_rcl_context();
34+
rcl_params_t * global_params = nullptr;
35+
auto ret = rcl_arguments_get_param_overrides(&rcl_ctx->global_arguments, &global_params);
36+
if (ret != RCL_RET_OK || global_params == nullptr) {
37+
return;
38+
}
39+
auto cleanup = [](rcl_params_t * p) {
40+
if (p) {
41+
rcl_yaml_node_struct_fini(p);
42+
}
43+
};
44+
std::unique_ptr<rcl_params_t, decltype(cleanup)> guard(global_params, cleanup);
45+
46+
std::string node_name = node->get_name();
47+
std::string node_fqn = node->get_fully_qualified_name();
48+
for (size_t n = 0; n < global_params->num_nodes; ++n) {
49+
std::string yaml_node = global_params->node_names[n];
50+
if (yaml_node != node_name && yaml_node != node_fqn && yaml_node != "/**") {
51+
continue;
52+
}
53+
auto * node_p = &global_params->params[n];
54+
for (size_t p = 0; p < node_p->num_params; ++p) {
55+
std::string pname = node_p->parameter_names[p];
56+
if (pname.rfind(prefix, 0) == 0 && pname != path_key && !node->has_parameter(pname)) {
57+
auto & val = node_p->parameter_values[p];
58+
try {
59+
if (val.string_value != nullptr) {
60+
node->declare_parameter(pname, std::string(val.string_value));
61+
} else if (val.bool_value != nullptr) {
62+
node->declare_parameter(pname, *val.bool_value);
63+
} else if (val.integer_value != nullptr) {
64+
node->declare_parameter(pname, static_cast<int64_t>(*val.integer_value));
65+
} else if (val.double_value != nullptr) {
66+
node->declare_parameter(pname, *val.double_value);
67+
} else {
68+
RCLCPP_WARN(node->get_logger(), "Skipping param '%s': unsupported type (array?)", pname.c_str());
69+
}
70+
} catch (const std::exception & e) {
71+
RCLCPP_DEBUG(node->get_logger(), "Could not declare param '%s': %s", pname.c_str(), e.what());
72+
}
73+
}
74+
}
75+
}
76+
}
77+
78+
} // namespace ros2_medkit_gateway

src/ros2_medkit_gateway/src/gateway_node.cpp

Lines changed: 1 addition & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,8 @@
2222
#include <set>
2323
#include <unordered_set>
2424

25-
#include <rcl/arguments.h>
26-
#include <rcl_yaml_param_parser/parser.h>
27-
2825
#include "ros2_medkit_gateway/aggregation/network_utils.hpp"
26+
#include "ros2_medkit_gateway/param_utils.hpp"
2927

3028
#include "ros2_medkit_gateway/http/handlers/sse_transport_provider.hpp"
3129
#include "ros2_medkit_gateway/sqlite_trigger_store.hpp"
@@ -62,52 +60,6 @@ nlohmann::json parameter_to_json(const rclcpp::Parameter & param) {
6260
}
6361
}
6462

65-
/// Declare plugin config parameters from the global --params-file YAML.
66-
///
67-
/// Parameters from --params-file go into the ROS 2 global rcl context,
68-
/// NOT into NodeOptions::parameter_overrides(). We must discover and
69-
/// declare them explicitly so list_parameters()/get_parameter() can find them.
70-
void declare_plugin_params_from_yaml(rclcpp::Node * node, const std::string & prefix, const std::string & path_key) {
71-
auto rcl_ctx = node->get_node_base_interface()->get_context()->get_rcl_context();
72-
rcl_params_t * global_params = nullptr;
73-
auto ret = rcl_arguments_get_param_overrides(&rcl_ctx->global_arguments, &global_params);
74-
if (ret != RCL_RET_OK || global_params == nullptr) {
75-
return;
76-
}
77-
78-
std::string node_name = node->get_name();
79-
std::string node_fqn = node->get_fully_qualified_name();
80-
for (size_t n = 0; n < global_params->num_nodes; ++n) {
81-
std::string yaml_node = global_params->node_names[n];
82-
if (yaml_node != node_name && yaml_node != node_fqn && yaml_node != "/**") {
83-
continue;
84-
}
85-
auto * node_p = &global_params->params[n];
86-
for (size_t p = 0; p < node_p->num_params; ++p) {
87-
std::string pname = node_p->parameter_names[p];
88-
if (pname.rfind(prefix, 0) == 0 && pname != path_key && !node->has_parameter(pname)) {
89-
// Determine the type from the rcl_variant_t so we can declare with the correct type
90-
auto & val = node_p->parameter_values[p];
91-
try {
92-
if (val.string_value != nullptr) {
93-
node->declare_parameter(pname, std::string(val.string_value));
94-
} else if (val.bool_value != nullptr) {
95-
node->declare_parameter(pname, *val.bool_value);
96-
} else if (val.integer_value != nullptr) {
97-
node->declare_parameter(pname, static_cast<int64_t>(*val.integer_value));
98-
} else if (val.double_value != nullptr) {
99-
node->declare_parameter(pname, *val.double_value);
100-
}
101-
} catch (...) {
102-
// Skip params that can't be declared
103-
}
104-
}
105-
}
106-
}
107-
108-
rcl_yaml_node_struct_fini(global_params);
109-
}
110-
11163
/// Extract per-plugin config from ROS 2 parameters.
11264
/// Scans for keys matching "plugins.<name>.<key>" (excluding ".path")
11365
/// and builds a flat JSON object: {"<key>": value, ...}

src/ros2_medkit_gateway/test/test_plugin_config.cpp

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -28,54 +28,9 @@
2828
#include <fstream>
2929
#include <string>
3030

31-
#include <rcl/arguments.h>
32-
#include <rcl_yaml_param_parser/parser.h>
3331
#include <rclcpp/rclcpp.hpp>
3432

35-
namespace {
36-
37-
/// Replicate the fix logic from gateway_node.cpp for testability.
38-
/// Declares plugin params from the global rcl context on a given node.
39-
void declare_plugin_params_from_yaml(rclcpp::Node * node, const std::string & prefix) {
40-
auto rcl_ctx = node->get_node_base_interface()->get_context()->get_rcl_context();
41-
rcl_params_t * global_params = nullptr;
42-
if (rcl_arguments_get_param_overrides(&rcl_ctx->global_arguments, &global_params) != RCL_RET_OK ||
43-
global_params == nullptr) {
44-
return;
45-
}
46-
47-
std::string node_name = node->get_name();
48-
std::string node_fqn = node->get_fully_qualified_name();
49-
for (size_t n = 0; n < global_params->num_nodes; ++n) {
50-
std::string yaml_node = global_params->node_names[n];
51-
if (yaml_node != node_name && yaml_node != node_fqn && yaml_node != "/**") {
52-
continue;
53-
}
54-
auto * node_p = &global_params->params[n];
55-
for (size_t p = 0; p < node_p->num_params; ++p) {
56-
std::string pname = node_p->parameter_names[p];
57-
if (pname.rfind(prefix, 0) == 0 && !node->has_parameter(pname)) {
58-
auto & val = node_p->parameter_values[p];
59-
try {
60-
if (val.string_value != nullptr) {
61-
node->declare_parameter(pname, std::string(val.string_value));
62-
} else if (val.bool_value != nullptr) {
63-
node->declare_parameter(pname, *val.bool_value);
64-
} else if (val.integer_value != nullptr) {
65-
node->declare_parameter(pname, static_cast<int64_t>(*val.integer_value));
66-
} else if (val.double_value != nullptr) {
67-
node->declare_parameter(pname, *val.double_value);
68-
}
69-
} catch (...) {
70-
}
71-
}
72-
}
73-
}
74-
75-
rcl_yaml_node_struct_fini(global_params);
76-
}
77-
78-
} // namespace
33+
#include "ros2_medkit_gateway/param_utils.hpp"
7934

8035
/// Proves the bug and validates the fix using a lightweight rclcpp::Node.
8136
///
@@ -111,7 +66,7 @@ TEST(PluginConfig, YamlPluginParamsReachGateway) {
11166
<< "(this confirms the root cause of the bug)";
11267

11368
// FIX: declare_plugin_params_from_yaml discovers and declares them
114-
declare_plugin_params_from_yaml(node.get(), "plugins.my_plugin.");
69+
ros2_medkit_gateway::declare_plugin_params_from_yaml(node.get(), "plugins.my_plugin.");
11570

11671
// Verify all param types are correctly declared
11772
ASSERT_TRUE(node->has_parameter("plugins.my_plugin.custom_key"));

0 commit comments

Comments
 (0)