3 #include <nlohmann/json.hpp>
4 #include <rclcpp/qos.hpp>
5 #include <rclcpp/version.h>
12 constexpr
char PARAM_SEP =
'.';
14 #if RCLCPP_VERSION_MAJOR > 16
15 const rclcpp::ParametersQoS parameterQoS;
17 const rmw_qos_profile_t& parameterQoS = rmw_qos_profile_parameters;
20 static std::pair<std::string, std::string> getNodeAndParamName(
21 const std::string& nodeNameAndParamName) {
22 return {nodeNameAndParamName.substr(0UL, nodeNameAndParamName.find(PARAM_SEP)),
23 nodeNameAndParamName.substr(nodeNameAndParamName.find(PARAM_SEP) + 1UL)};
26 static std::string prependNodeNameToParamName(
const std::string& paramName,
27 const std::string& nodeName) {
28 return nodeName + PARAM_SEP + paramName;
35 const auto paramType = p.
getType();
38 if (paramType == ParameterType::PARAMETER_BOOL) {
39 return rclcpp::Parameter(p.
getName(), value.getValue<
bool>());
40 }
else if (paramType == ParameterType::PARAMETER_INTEGER) {
41 return rclcpp::Parameter(p.
getName(), value.getValue<int64_t>());
42 }
else if (paramType == ParameterType::PARAMETER_DOUBLE) {
43 return rclcpp::Parameter(p.
getName(), value.getValue<
double>());
44 }
else if (paramType == ParameterType::PARAMETER_STRING) {
45 return rclcpp::Parameter(p.
getName(), value.getValue<std::string>());
46 }
else if (paramType == ParameterType::PARAMETER_BYTE_ARRAY) {
47 return rclcpp::Parameter(p.
getName(), value.getValue<std::vector<unsigned char>>());
48 }
else if (paramType == ParameterType::PARAMETER_ARRAY) {
49 const auto paramVec = value.getValue<std::vector<foxglove::ParameterValue>>();
51 const auto elementType = paramVec.front().getType();
52 if (elementType == ParameterType::PARAMETER_BOOL) {
53 std::vector<bool> vec;
54 for (
const auto& paramValue : paramVec) {
55 vec.push_back(paramValue.getValue<
bool>());
57 return rclcpp::Parameter(p.
getName(), vec);
58 }
else if (elementType == ParameterType::PARAMETER_INTEGER) {
59 std::vector<int64_t> vec;
60 for (
const auto& paramValue : paramVec) {
61 vec.push_back(paramValue.getValue<int64_t>());
63 return rclcpp::Parameter(p.
getName(), vec);
64 }
else if (elementType == ParameterType::PARAMETER_DOUBLE) {
65 std::vector<double> vec;
66 for (
const auto& paramValue : paramVec) {
67 vec.push_back(paramValue.getValue<
double>());
69 return rclcpp::Parameter(p.
getName(), vec);
70 }
else if (elementType == ParameterType::PARAMETER_STRING) {
71 std::vector<std::string> vec;
72 for (
const auto& paramValue : paramVec) {
73 vec.push_back(paramValue.getValue<std::string>());
75 return rclcpp::Parameter(p.
getName(), vec);
77 throw std::runtime_error(
"Unsupported parameter type");
78 }
else if (paramType == ParameterType::PARAMETER_NOT_SET) {
79 return rclcpp::Parameter(p.
getName());
81 throw std::runtime_error(
"Unsupported parameter type");
84 return rclcpp::Parameter();
88 const auto type = p.get_type();
90 if (type == rclcpp::ParameterType::PARAMETER_NOT_SET) {
92 }
else if (type == rclcpp::ParameterType::PARAMETER_BOOL) {
94 }
else if (type == rclcpp::ParameterType::PARAMETER_INTEGER) {
96 }
else if (type == rclcpp::ParameterType::PARAMETER_DOUBLE) {
98 }
else if (type == rclcpp::ParameterType::PARAMETER_STRING) {
100 }
else if (type == rclcpp::ParameterType::PARAMETER_BYTE_ARRAY) {
102 }
else if (type == rclcpp::ParameterType::PARAMETER_BOOL_ARRAY) {
103 std::vector<foxglove::ParameterValue> paramVec;
104 for (
const auto value : p.as_bool_array()) {
108 }
else if (type == rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY) {
109 std::vector<foxglove::ParameterValue> paramVec;
110 for (
const auto value : p.as_integer_array()) {
111 paramVec.push_back(value);
114 }
else if (type == rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY) {
115 std::vector<foxglove::ParameterValue> paramVec;
116 for (
const auto value : p.as_double_array()) {
117 paramVec.push_back(value);
120 }
else if (type == rclcpp::ParameterType::PARAMETER_STRING_ARRAY) {
121 std::vector<foxglove::ParameterValue> paramVec;
122 for (
const auto& value : p.as_string_array()) {
123 paramVec.push_back(value);
127 throw std::runtime_error(
"Unsupported parameter type");
138 std::vector<std::regex> paramWhitelistPatterns,
141 , _paramWhitelistPatterns(paramWhitelistPatterns)
142 , _callbackGroup(node->create_callback_group(rclcpp::CallbackGroupType::Reentrant))
143 , _ignoredNodeNames({node->get_fully_qualified_name()})
144 , _unresponsiveNodePolicy(unresponsiveNodePolicy) {}
147 const std::chrono::duration<double>& timeout) {
148 std::lock_guard<std::mutex> lock(
_mutex);
150 std::unordered_map<std::string, std::vector<std::string>> paramNamesByNodeName;
151 const auto thisNode =
_node->get_fully_qualified_name();
153 if (!paramNames.empty()) {
156 for (
const auto& fullParamName : paramNames) {
157 const auto& [nodeName, paramName] = getNodeAndParamName(fullParamName);
158 paramNamesByNodeName[nodeName].push_back(paramName);
161 RCLCPP_DEBUG(
_node->get_logger(),
"Getting %zu parameters from %zu nodes...", paramNames.size(),
162 paramNamesByNodeName.size());
166 for (
const auto& fqnNodeName :
_node->get_node_names()) {
171 const auto serviceNamesAndTypes =
172 _node->get_service_names_and_types_by_node(nodeName, nodeNs);
174 bool listParamsSrvFound =
false, getParamsSrvFound =
false;
175 for (
const auto& [serviceName, serviceTypes] : serviceNamesAndTypes) {
176 constexpr
char GET_PARAMS_SERVICE_TYPE[] =
"rcl_interfaces/srv/GetParameters";
177 constexpr
char LIST_PARAMS_SERVICE_TYPE[] =
"rcl_interfaces/srv/ListParameters";
179 if (!getParamsSrvFound) {
180 getParamsSrvFound = std::find(serviceTypes.begin(), serviceTypes.end(),
181 GET_PARAMS_SERVICE_TYPE) != serviceTypes.end();
183 if (!listParamsSrvFound) {
184 listParamsSrvFound = std::find(serviceTypes.begin(), serviceTypes.end(),
185 LIST_PARAMS_SERVICE_TYPE) != serviceTypes.end();
189 if (listParamsSrvFound && getParamsSrvFound) {
190 paramNamesByNodeName.insert({fqnNodeName, {}});
194 if (!paramNamesByNodeName.empty()) {
195 RCLCPP_DEBUG(
_node->get_logger(),
"Getting all parameters from %zu nodes...",
196 paramNamesByNodeName.size());
200 std::unordered_map<std::string, std::future<ParameterList>> getParametersFuture;
201 for (
const auto& [nodeName, nodeParamNames] : paramNamesByNodeName) {
202 if (nodeName == thisNode) {
210 rclcpp::AsyncParametersClient::make_shared(
_node, nodeName, parameterQoS,
_callbackGroup));
211 paramClientIt = insertedPair.first;
214 getParametersFuture.emplace(
216 paramClientIt->second, nodeName, nodeParamNames, timeout));
220 for (
auto& [nodeName, future] : getParametersFuture) {
222 const auto params = future.get();
223 result.insert(result.begin(), params.begin(), params.end());
224 }
catch (
const std::exception& e) {
225 RCLCPP_ERROR(
_node->get_logger(),
"Failed to retrieve parameters from node '%s': %s",
226 nodeName.c_str(), e.what());
235 RCLCPP_WARN(
_node->get_logger(),
236 "Adding node %s to the ignore list to prevent repeated timeouts or failures in "
237 "future parameter requests.",
247 const std::chrono::duration<double>& timeout) {
248 std::lock_guard<std::mutex> lock(
_mutex);
250 rclcpp::ParameterMap paramsByNode;
251 for (
const auto&
param : parameters) {
257 const auto& [nodeName, paramName] = getNodeAndParamName(rosParam.get_name());
258 paramsByNode[nodeName].emplace_back(paramName, rosParam.get_parameter_value());
261 std::vector<std::future<void>> setParametersFuture;
262 for (
const auto& [nodeName, params] : paramsByNode) {
267 rclcpp::AsyncParametersClient::make_shared(
_node, nodeName, parameterQoS,
_callbackGroup));
268 paramClientIt = insertedPair.first;
271 setParametersFuture.emplace_back(std::async(std::launch::async,
273 paramClientIt->second, nodeName, params, timeout));
276 for (
auto& future : setParametersFuture) {
279 }
catch (
const std::exception& e) {
280 RCLCPP_ERROR(
_node->get_logger(),
"Exception when setting parameters: %s", e.what());
286 std::lock_guard<std::mutex> lock(
_mutex);
288 std::unordered_set<std::string> nodesToSubscribe;
289 for (
const auto& paramName : paramNames) {
294 const auto& [nodeName, paramN] = getNodeAndParamName(paramName);
297 auto& subscribedNodeParams = subscribedParamsit->second;
298 subscribedNodeParams.insert(paramN);
300 if (wasNewlyCreated) {
301 nodesToSubscribe.insert(nodeName);
305 for (
const auto& nodeName : nodesToSubscribe) {
310 rclcpp::AsyncParametersClient::make_shared(
_node, nodeName, parameterQoS,
_callbackGroup));
311 paramClientIt = insertedPair.first;
314 auto& paramClient = paramClientIt->second;
317 [
this, nodeName](rcl_interfaces::msg::ParameterEvent::ConstSharedPtr msg) {
318 RCLCPP_DEBUG(
_node->get_logger(),
"Retrieved param update for node %s: %zu params changed",
319 nodeName.c_str(), msg->changed_parameters.size());
323 for (
const auto&
param : msg->changed_parameters) {
324 if (subscribedNodeParams.find(
param.name) != subscribedNodeParams.end()) {
326 rclcpp::Parameter(prependNodeNameToParamName(
param.name, nodeName),
param.value)));
338 std::lock_guard<std::mutex> lock(
_mutex);
340 for (
const auto& paramName : paramNames) {
341 const auto& [nodeName, paramN] = getNodeAndParamName(paramName);
345 subscribedNodeParamsIt->second.erase(subscribedNodeParamsIt->second.find(paramN));
347 if (subscribedNodeParamsIt->second.empty()) {
356 std::lock_guard<std::mutex> lock(
_mutex);
361 const rclcpp::AsyncParametersClient::SharedPtr paramClient,
const std::string& nodeName,
362 const std::vector<std::string>& paramNames,
const std::chrono::duration<double>& timeout) {
363 if (!paramClient->service_is_ready()) {
364 throw std::runtime_error(
"Parameter service for node '" + nodeName +
"' is not ready");
367 auto paramsToRequest = paramNames;
368 if (paramsToRequest.empty()) {
370 auto future = paramClient->list_parameters({}, 0UL);
371 if (std::future_status::ready != future.wait_for(timeout)) {
372 throw std::runtime_error(
"Failed to retrieve parameter names for node '" + nodeName +
"'");
374 paramsToRequest = future.get().names;
378 auto getParamsFuture = paramClient->get_parameters(paramsToRequest);
379 if (std::future_status::ready != getParamsFuture.wait_for(timeout)) {
380 throw std::runtime_error(
"Timed out waiting for " + std::to_string(paramsToRequest.size()) +
381 " parameter(s) from node '" + nodeName +
"'");
383 const auto params = getParamsFuture.get();
386 for (
const auto&
param : params) {
387 const auto fullParamName = prependNodeNameToParamName(
param.get_name(), nodeName);
389 result.push_back(
fromRosParam(rclcpp::Parameter(fullParamName,
param.get_parameter_value())));
396 const std::string& nodeName,
397 const std::vector<rclcpp::Parameter>& params,
398 const std::chrono::duration<double>& timeout) {
399 if (!paramClient->service_is_ready()) {
400 throw std::runtime_error(
"Parameter service for node '" + nodeName +
"' is not ready");
403 auto future = paramClient->set_parameters(params);
405 std::vector<std::string> paramsToDelete;
406 for (
const auto& p : params) {
407 if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
408 paramsToDelete.push_back(p.get_name());
412 if (!paramsToDelete.empty()) {
413 auto deleteFuture = paramClient->delete_parameters(paramsToDelete);
414 if (std::future_status::ready != deleteFuture.wait_for(timeout)) {
417 "Param client failed to delete %zu parameter(s) for node '%s' within the given timeout",
418 paramsToDelete.size(), nodeName.c_str());
422 if (std::future_status::ready != future.wait_for(timeout)) {
423 throw std::runtime_error(
"Param client failed to set " + std::to_string(params.size()) +
424 " parameter(s) for node '" + nodeName +
"' within the given timeout");
427 const auto setParamResults = future.get();
428 for (
auto& result : setParamResults) {
429 if (!result.successful) {
430 RCLCPP_WARN(
_node->get_logger(),
"Failed to set one or more parameters for node '%s': %s",
431 nodeName.c_str(), result.reason.c_str());