3 #include <nlohmann/json.hpp> 10 constexpr
char PARAM_SEP =
'.';
12 static std::pair<std::string, std::string> getNodeAndParamName(
13 const std::string& nodeNameAndParamName) {
14 return {nodeNameAndParamName.substr(0UL, nodeNameAndParamName.find(PARAM_SEP)),
15 nodeNameAndParamName.substr(nodeNameAndParamName.find(PARAM_SEP) + 1UL)};
18 static std::string prependNodeNameToParamName(
const std::string& paramName,
19 const std::string& nodeName) {
20 return nodeName + PARAM_SEP + paramName;
27 const auto paramType = p.
getType();
30 if (paramType == ParameterType::PARAMETER_BOOL) {
31 return rclcpp::Parameter(p.
getName(), value.getValue<
bool>());
32 }
else if (paramType == ParameterType::PARAMETER_INTEGER) {
33 return rclcpp::Parameter(p.
getName(), value.getValue<int64_t>());
34 }
else if (paramType == ParameterType::PARAMETER_DOUBLE) {
35 return rclcpp::Parameter(p.
getName(), value.getValue<
double>());
36 }
else if (paramType == ParameterType::PARAMETER_STRING) {
37 return rclcpp::Parameter(p.
getName(), value.getValue<std::string>());
38 }
else if (paramType == ParameterType::PARAMETER_BYTE_ARRAY) {
39 return rclcpp::Parameter(p.
getName(), value.getValue<std::vector<unsigned char>>());
40 }
else if (paramType == ParameterType::PARAMETER_ARRAY) {
41 const auto paramVec = value.getValue<std::vector<foxglove::ParameterValue>>();
43 const auto elementType = paramVec.front().getType();
44 if (elementType == ParameterType::PARAMETER_BOOL) {
45 std::vector<bool> vec;
46 for (
const auto& paramValue : paramVec) {
47 vec.push_back(paramValue.getValue<
bool>());
49 return rclcpp::Parameter(p.
getName(), vec);
50 }
else if (elementType == ParameterType::PARAMETER_INTEGER) {
51 std::vector<int64_t> vec;
52 for (
const auto& paramValue : paramVec) {
53 vec.push_back(paramValue.getValue<int64_t>());
55 return rclcpp::Parameter(p.
getName(), vec);
56 }
else if (elementType == ParameterType::PARAMETER_DOUBLE) {
57 std::vector<double> vec;
58 for (
const auto& paramValue : paramVec) {
59 vec.push_back(paramValue.getValue<
double>());
61 return rclcpp::Parameter(p.
getName(), vec);
62 }
else if (elementType == ParameterType::PARAMETER_STRING) {
63 std::vector<std::string> vec;
64 for (
const auto& paramValue : paramVec) {
65 vec.push_back(paramValue.getValue<std::string>());
67 return rclcpp::Parameter(p.
getName(), vec);
69 throw std::runtime_error(
"Unsupported parameter type");
70 }
else if (paramType == ParameterType::PARAMETER_NOT_SET) {
71 return rclcpp::Parameter(p.
getName());
73 throw std::runtime_error(
"Unsupported parameter type");
76 return rclcpp::Parameter();
80 const auto type = p.get_type();
82 if (type == rclcpp::ParameterType::PARAMETER_NOT_SET) {
84 }
else if (type == rclcpp::ParameterType::PARAMETER_BOOL) {
86 }
else if (type == rclcpp::ParameterType::PARAMETER_INTEGER) {
88 }
else if (type == rclcpp::ParameterType::PARAMETER_DOUBLE) {
90 }
else if (type == rclcpp::ParameterType::PARAMETER_STRING) {
92 }
else if (type == rclcpp::ParameterType::PARAMETER_BYTE_ARRAY) {
94 }
else if (type == rclcpp::ParameterType::PARAMETER_BOOL_ARRAY) {
95 std::vector<foxglove::ParameterValue> paramVec;
96 for (
const auto value : p.as_bool_array()) {
97 paramVec.push_back(value);
100 }
else if (type == rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY) {
101 std::vector<foxglove::ParameterValue> paramVec;
102 for (
const auto value : p.as_integer_array()) {
103 paramVec.push_back(value);
106 }
else if (type == rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY) {
107 std::vector<foxglove::ParameterValue> paramVec;
108 for (
const auto value : p.as_double_array()) {
109 paramVec.push_back(value);
112 }
else if (type == rclcpp::ParameterType::PARAMETER_STRING_ARRAY) {
113 std::vector<foxglove::ParameterValue> paramVec;
114 for (
const auto& value : p.as_string_array()) {
115 paramVec.push_back(value);
119 throw std::runtime_error(
"Unsupported parameter type");
130 std::vector<std::regex> paramWhitelistPatterns)
132 , _paramWhitelistPatterns(paramWhitelistPatterns)
133 , _callbackGroup(node->create_callback_group(rclcpp::CallbackGroupType::Reentrant)) {}
136 const std::chrono::duration<double>& timeout) {
137 std::lock_guard<std::mutex> lock(
_mutex);
139 std::unordered_map<std::string, std::vector<std::string>> paramNamesByNodeName;
140 const auto thisNode =
_node->get_fully_qualified_name();
142 if (!paramNames.empty()) {
145 for (
const auto& fullParamName : paramNames) {
146 const auto& [nodeName, paramName] = getNodeAndParamName(fullParamName);
147 paramNamesByNodeName[nodeName].push_back(paramName);
150 RCLCPP_INFO(
_node->get_logger(),
"Getting %zu parameters from %zu nodes...", paramNames.size(),
151 paramNamesByNodeName.size());
155 for (
const auto& fqnNodeName :
_node->get_node_names()) {
156 if (fqnNodeName == thisNode) {
160 const auto serviceNamesAndTypes =
161 _node->get_service_names_and_types_by_node(nodeName, nodeNs);
163 bool listParamsSrvFound =
false, getParamsSrvFound =
false;
164 for (
const auto& [serviceName, serviceTypes] : serviceNamesAndTypes) {
165 constexpr
char GET_PARAMS_SERVICE_TYPE[] =
"rcl_interfaces/srv/GetParameters";
166 constexpr
char LIST_PARAMS_SERVICE_TYPE[] =
"rcl_interfaces/srv/ListParameters";
168 if (!getParamsSrvFound) {
169 getParamsSrvFound = std::find(serviceTypes.begin(), serviceTypes.end(),
170 GET_PARAMS_SERVICE_TYPE) != serviceTypes.end();
172 if (!listParamsSrvFound) {
173 listParamsSrvFound = std::find(serviceTypes.begin(), serviceTypes.end(),
174 LIST_PARAMS_SERVICE_TYPE) != serviceTypes.end();
178 if (listParamsSrvFound && getParamsSrvFound) {
179 paramNamesByNodeName.insert({fqnNodeName, {}});
183 if (!paramNamesByNodeName.empty()) {
184 RCLCPP_INFO(
_node->get_logger(),
"Getting all parameters from %zu nodes...",
185 paramNamesByNodeName.size());
189 std::vector<std::future<ParameterList>> getParametersFuture;
190 for (
const auto& [nodeName, nodeParamNames] : paramNamesByNodeName) {
191 if (nodeName == thisNode) {
198 nodeName, rclcpp::AsyncParametersClient::make_shared(
200 paramClientIt = insertedPair.first;
203 getParametersFuture.emplace_back(
205 paramClientIt->second, nodeName, nodeParamNames, timeout));
209 for (
auto& future : getParametersFuture) {
211 const auto params = future.get();
212 result.insert(result.begin(), params.begin(), params.end());
213 }
catch (
const std::exception& e) {
214 RCLCPP_ERROR(
_node->get_logger(),
"Exception when getting parameters: %s", e.what());
222 const std::chrono::duration<double>& timeout) {
223 std::lock_guard<std::mutex> lock(
_mutex);
225 rclcpp::ParameterMap paramsByNode;
226 for (
const auto&
param : parameters) {
232 const auto& [nodeName, paramName] = getNodeAndParamName(rosParam.get_name());
233 paramsByNode[nodeName].emplace_back(paramName, rosParam.get_parameter_value());
236 std::vector<std::future<void>> setParametersFuture;
237 for (
const auto& [nodeName, params] : paramsByNode) {
241 nodeName, rclcpp::AsyncParametersClient::make_shared(
243 paramClientIt = insertedPair.first;
246 setParametersFuture.emplace_back(std::async(std::launch::async,
248 paramClientIt->second, nodeName, params, timeout));
251 for (
auto& future : setParametersFuture) {
254 }
catch (
const std::exception& e) {
255 RCLCPP_ERROR(
_node->get_logger(),
"Exception when setting parameters: %s", e.what());
261 std::lock_guard<std::mutex> lock(
_mutex);
263 std::unordered_set<std::string> nodesToSubscribe;
264 for (
const auto& paramName : paramNames) {
269 const auto& [nodeName, paramN] = getNodeAndParamName(paramName);
272 auto& subscribedNodeParams = subscribedParamsit->second;
273 subscribedNodeParams.insert(paramN);
275 if (wasNewlyCreated) {
276 nodesToSubscribe.insert(nodeName);
280 for (
const auto& nodeName : nodesToSubscribe) {
284 nodeName, rclcpp::AsyncParametersClient::make_shared(
286 paramClientIt = insertedPair.first;
289 auto& paramClient = paramClientIt->second;
292 [
this, nodeName](rcl_interfaces::msg::ParameterEvent::ConstSharedPtr msg) {
293 RCLCPP_DEBUG(
_node->get_logger(),
"Retrieved param update for node %s: %zu params changed",
294 nodeName.c_str(), msg->changed_parameters.size());
298 for (
const auto&
param : msg->changed_parameters) {
299 if (subscribedNodeParams.find(
param.name) != subscribedNodeParams.end()) {
301 rclcpp::Parameter(prependNodeNameToParamName(
param.name, nodeName),
param.value)));
313 std::lock_guard<std::mutex> lock(
_mutex);
315 for (
const auto& paramName : paramNames) {
316 const auto& [nodeName, paramN] = getNodeAndParamName(paramName);
320 subscribedNodeParamsIt->second.erase(subscribedNodeParamsIt->second.find(paramN));
322 if (subscribedNodeParamsIt->second.empty()) {
331 std::lock_guard<std::mutex> lock(
_mutex);
336 const rclcpp::AsyncParametersClient::SharedPtr paramClient,
const std::string& nodeName,
337 const std::vector<std::string>& paramNames,
const std::chrono::duration<double>& timeout) {
338 if (!paramClient->service_is_ready()) {
339 throw std::runtime_error(
"Parameter service for node '" + nodeName +
"' is not ready");
342 auto paramsToRequest = paramNames;
343 if (paramsToRequest.empty()) {
345 auto future = paramClient->list_parameters({}, 0UL);
346 if (std::future_status::ready != future.wait_for(timeout)) {
347 throw std::runtime_error(
"Failed to retrieve parameter names for node '" + nodeName +
"'");
349 paramsToRequest = future.get().names;
353 auto getParamsFuture = paramClient->get_parameters(paramsToRequest);
354 if (std::future_status::ready != getParamsFuture.wait_for(timeout)) {
355 throw std::runtime_error(
"Timed out waiting for " + std::to_string(paramsToRequest.size()) +
356 " parameter(s) from node '" + nodeName +
"'");
358 const auto params = getParamsFuture.get();
361 for (
const auto&
param : params) {
362 const auto fullParamName = prependNodeNameToParamName(
param.get_name(), nodeName);
364 result.push_back(
fromRosParam(rclcpp::Parameter(fullParamName,
param.get_parameter_value())));
371 const std::string& nodeName,
372 const std::vector<rclcpp::Parameter>& params,
373 const std::chrono::duration<double>& timeout) {
374 if (!paramClient->service_is_ready()) {
375 throw std::runtime_error(
"Parameter service for node '" + nodeName +
"' is not ready");
378 auto future = paramClient->set_parameters(params);
380 std::vector<std::string> paramsToDelete;
381 for (
const auto& p : params) {
382 if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
383 paramsToDelete.push_back(p.get_name());
387 if (!paramsToDelete.empty()) {
388 auto deleteFuture = paramClient->delete_parameters(paramsToDelete);
389 if (std::future_status::ready != deleteFuture.wait_for(timeout)) {
392 "Param client failed to delete %zu parameter(s) for node '%s' within the given timeout",
393 paramsToDelete.size(), nodeName.c_str());
397 if (std::future_status::ready != future.wait_for(timeout)) {
398 throw std::runtime_error(
"Param client failed to set " + std::to_string(params.size()) +
399 " parameter(s) for node '" + nodeName +
"' within the given timeout");
402 const auto setParamResults = future.get();
403 for (
auto& result : setParamResults) {
404 if (!result.successful) {
405 RCLCPP_WARN(
_node->get_logger(),
"Failed to set one or more parameters for node '%s': %s",
406 nodeName.c_str(), result.reason.c_str());
const ParameterValue & getValue() const
void setParamUpdateCallback(ParamUpdateFunc paramUpdateFunc)
bool param(const std::string ¶m_name, T ¶m_val, const T &default_val)
void subscribeParams(const std::vector< std::string > ¶mNames)
ParameterInterface(rclcpp::Node *node, std::vector< std::regex > paramWhitelistPatterns)
std::pair< std::string, std::string > getNodeAndNodeNamespace(const std::string &fqnNodeName)
XmlRpc::XmlRpcValue toRosParam(const foxglove::ParameterValue ¶m)
bool isWhitelisted(const std::string &name, const std::vector< std::regex > ®exPatterns)
ParamUpdateFunc _paramUpdateFunc
void setParams(const ParameterList ¶ms, const std::chrono::duration< double > &timeout)
std::vector< foxglove::Parameter > ParameterList
std::function< void(const ParameterList &)> ParamUpdateFunc
rclcpp::CallbackGroup::SharedPtr _callbackGroup
std::vector< std::regex > _paramWhitelistPatterns
void unsubscribeParams(const std::vector< std::string > ¶mNames)
const std::string & getName() const
std::unordered_map< std::string, rclcpp::SubscriptionBase::SharedPtr > _paramSubscriptionsByNode
std::unordered_map< std::string, rclcpp::AsyncParametersClient::SharedPtr > _paramClientsByNode
std::unordered_map< std::string, std::unordered_set< std::string > > _subscribedParamsByNode
foxglove::Parameter fromRosParam(const std::string &name, const XmlRpc::XmlRpcValue &value)
ParameterList getNodeParameters(rclcpp::AsyncParametersClient::SharedPtr paramClient, const std::string &nodeName, const std::vector< std::string > ¶mNames, const std::chrono::duration< double > &timeout)
ParameterType getType() const
void setNodeParameters(rclcpp::AsyncParametersClient::SharedPtr paramClient, const std::string &nodeName, const std::vector< rclcpp::Parameter > ¶ms, const std::chrono::duration< double > &timeout)
ParameterList getParams(const std::vector< std::string > ¶mNames, const std::chrono::duration< double > &timeout)