76 const std::string & prefix_name,
const std::string & interface_name,
77 const std::string & data_type =
"double",
const std::string & initial_value =
"")
79 interface_name_(interface_name),
84 if (data_type_ == hardware_interface::HandleDataType::DOUBLE)
88 value_ = initial_value.empty() ? std::numeric_limits<double>::quiet_NaN()
91 catch (
const std::invalid_argument & err)
93 throw std::invalid_argument(
95 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
96 initial_value, handle_name_, data_type_.to_string()));
99 else if (data_type_ == hardware_interface::HandleDataType::FLOAT32)
103 value_ = initial_value.empty()
104 ? std::numeric_limits<float>::quiet_NaN()
107 catch (
const std::invalid_argument & err)
109 throw std::invalid_argument(
111 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
112 initial_value, handle_name_, data_type_.to_string()));
115 else if (data_type_ == hardware_interface::HandleDataType::BOOL)
121 catch (
const std::invalid_argument & err)
123 throw std::invalid_argument(
125 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
126 initial_value, handle_name_, data_type_.to_string()));
129 else if (data_type_ == hardware_interface::HandleDataType::UINT8)
133 value_ = initial_value.empty()
134 ?
static_cast<uint8_t
>(std::numeric_limits<uint8_t>::max())
135 :
static_cast<uint8_t
>(hardware_interface::stoui8(initial_value));
137 catch (
const std::invalid_argument & err)
139 throw std::invalid_argument(
141 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
142 initial_value, handle_name_, data_type_.to_string()));
145 else if (data_type_ == hardware_interface::HandleDataType::INT8)
149 value_ = initial_value.empty()
150 ?
static_cast<int8_t
>(std::numeric_limits<int8_t>::max())
151 :
static_cast<int8_t
>(hardware_interface::stoi8(initial_value));
153 catch (
const std::invalid_argument & err)
155 throw std::invalid_argument(
157 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
158 initial_value, handle_name_, data_type_.to_string()));
161 else if (data_type_ == hardware_interface::HandleDataType::UINT16)
165 value_ = initial_value.empty()
166 ?
static_cast<uint16_t
>(std::numeric_limits<uint16_t>::max())
167 :
static_cast<uint16_t
>(hardware_interface::stoui16(initial_value));
169 catch (
const std::invalid_argument & err)
171 throw std::invalid_argument(
173 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
174 initial_value, handle_name_, data_type_.to_string()));
177 else if (data_type_ == hardware_interface::HandleDataType::INT16)
181 value_ = initial_value.empty()
182 ?
static_cast<int16_t
>(std::numeric_limits<int16_t>::max())
183 :
static_cast<int16_t
>(hardware_interface::stoi16(initial_value));
185 catch (
const std::invalid_argument & err)
187 throw std::invalid_argument(
189 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
190 initial_value, handle_name_, data_type_.to_string()));
193 else if (data_type_ == hardware_interface::HandleDataType::UINT32)
197 value_ = initial_value.empty()
198 ?
static_cast<uint32_t
>(std::numeric_limits<uint32_t>::max())
199 :
static_cast<uint32_t
>(hardware_interface::stoui32(initial_value));
201 catch (
const std::invalid_argument & err)
203 throw std::invalid_argument(
205 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
206 initial_value, handle_name_, data_type_.to_string()));
209 else if (data_type_ == hardware_interface::HandleDataType::INT32)
213 value_ = initial_value.empty()
214 ?
static_cast<int32_t
>(std::numeric_limits<int32_t>::max())
215 :
static_cast<int32_t
>(hardware_interface::stoi32(initial_value));
217 catch (
const std::invalid_argument & err)
219 throw std::invalid_argument(
221 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
222 initial_value, handle_name_, data_type_.to_string()));
227 throw std::runtime_error(
229 FMT_COMPILE(
"Invalid data type: '{}' for interface: {}. Check supported types."),
230 data_type, handle_name_));
236 interface_description.get_prefix_name(), interface_description.get_interface_name(),
237 interface_description.get_data_type_string(),
242 [[deprecated(
"Use InterfaceDescription for initializing the Interface")]]
244 explicit Handle(
const std::string & interface_name)
245 : interface_name_(interface_name), handle_name_(
"/" + interface_name_)
249 [[deprecated(
"Use InterfaceDescription for initializing the Interface")]]
251 explicit Handle(
const char * interface_name)
252 : interface_name_(interface_name), handle_name_(
"/" + interface_name_)
267 Handle(
Handle && other)
noexcept { swap(*
this, other); }
277 const std::string & get_name()
const {
return handle_name_; }
279 const std::string & get_interface_name()
const {
return interface_name_; }
281 const std::string & get_prefix_name()
const {
return prefix_name_; }
293 template <
typename T =
double>
296 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
297 return get_optional<T>(lock);
310 template <
typename T =
double>
311 [[nodiscard]] std::optional<T>
get_optional(std::shared_lock<std::shared_mutex> & lock)
const
313 if (!lock.owns_lock())
319 if constexpr (std::is_same_v<T, double>)
323 case HandleDataType::DOUBLE:
324 return std::get<double>(value_);
325 case HandleDataType::BOOL:
332 rclcpp::get_logger(get_name()),
"%s",
335 "Casting bool to double for interface '{}'. Better use get_optional<bool>()."),
340 return static_cast<double>(std::get<bool>(value_));
341 case HandleDataType::FLOAT32:
342 case HandleDataType::UINT8:
343 case HandleDataType::INT8:
344 case HandleDataType::UINT16:
345 case HandleDataType::INT16:
346 case HandleDataType::UINT32:
347 case HandleDataType::INT32:
348 throw std::runtime_error(
351 "Data type: '{}' will not be casted to double for interface: {}. Use "
352 "get_optional<{}>() instead."),
353 data_type_.to_string(), get_name(), data_type_.to_string()));
355 throw std::runtime_error(
357 FMT_COMPILE(
"Data type: '{}' cannot be casted to double for interface: {}"),
358 data_type_.to_string(), get_name()));
363 return std::get<T>(value_);
365 catch (
const std::bad_variant_access & err)
367 throw std::runtime_error(
369 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
370 get_type_name<T>(), get_name(), data_type_.to_string()));
388 typename T,
typename = std::enable_if_t<
389 !std::is_same_v<std::decay_t<T>, std::shared_lock<std::shared_mutex>>>>
390 [[nodiscard]]
bool get_value(T & value,
bool wait_for_lock)
const
394 std::shared_lock<std::shared_mutex> lock(handle_mutex_);
399 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
415 template <
typename T>
416 [[nodiscard]]
bool set_value(
const T & value,
bool wait_for_lock)
420 std::unique_lock<std::shared_mutex> lock(handle_mutex_);
425 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
441 template <
typename T>
444 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
460 template <
typename T>
461 [[nodiscard]]
bool set_value(std::unique_lock<std::shared_mutex> & lock,
const T & value)
463 if (!lock.owns_lock())
467 if (!std::holds_alternative<T>(value_))
469 throw std::runtime_error(
471 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
472 get_type_name<T>(), get_name(), data_type_.to_string()));
478 std::shared_mutex & get_mutex()
const {
return handle_mutex_; }
480 HandleDataType get_data_type()
const {
return data_type_; }
485 bool is_valid()
const {
return !std::holds_alternative<std::monostate>(value_); }
497 template <
typename T>
498 [[nodiscard]]
bool get_value(std::shared_lock<std::shared_mutex> & lock, T & value)
const
500 if (!lock.owns_lock())
506 if constexpr (std::is_same_v<T, double>)
510 case HandleDataType::DOUBLE:
511 value = std::get<double>(value_);
513 case HandleDataType::BOOL:
520 rclcpp::get_logger(get_name()),
521 "Casting bool to double for interface: %s. Better use get_optional<bool>().",
525 value =
static_cast<double>(std::get<bool>(value_));
527 case HandleDataType::FLOAT32:
528 case HandleDataType::UINT8:
529 case HandleDataType::INT8:
530 case HandleDataType::UINT16:
531 case HandleDataType::INT16:
532 case HandleDataType::UINT32:
533 case HandleDataType::INT32:
534 throw std::runtime_error(
537 "Data type: '{}' will not be casted to double for interface: {}. Use "
538 "get_optional<{}>() instead."),
539 data_type_.to_string(), get_name(), data_type_.to_string()));
541 throw std::runtime_error(
543 FMT_COMPILE(
"Data type: '{}' cannot be casted to double for interface: {}"),
544 data_type_.to_string(), get_name()));
549 value = std::get<T>(value_);
552 catch (
const std::bad_variant_access & err)
554 throw std::runtime_error(
556 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
557 get_type_name<T>(), get_name(), data_type_.to_string()));
562 void copy(
const Handle & other)
noexcept
564 std::scoped_lock lock(other.handle_mutex_, handle_mutex_);
566 interface_name_ = other.interface_name_;
567 handle_name_ = other.handle_name_;
568 value_ = other.value_;
569 data_type_ = other.data_type_;
572 void swap(Handle & first, Handle & second)
noexcept
574 std::scoped_lock lock(first.handle_mutex_, second.handle_mutex_);
575 std::swap(first.prefix_name_, second.prefix_name_);
576 std::swap(first.interface_name_, second.interface_name_);
577 std::swap(first.handle_name_, second.handle_name_);
578 std::swap(first.value_, second.value_);
579 std::swap(first.data_type_, second.data_type_);
585 std::string interface_name_;
586 std::string handle_name_;
590 mutable std::shared_mutex handle_mutex_;
596 mutable bool notified_ =
false;