68 const std::string & prefix_name,
const std::string & interface_name,
69 const std::string & data_type =
"double",
const std::string & initial_value =
"")
71 interface_name_(interface_name),
76 if (data_type_ == hardware_interface::HandleDataType::DOUBLE)
80 value_ = initial_value.empty() ? std::numeric_limits<double>::quiet_NaN()
83 catch (
const std::invalid_argument & err)
85 throw std::invalid_argument(
87 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
88 initial_value, handle_name_, data_type_.to_string()));
91 else if (data_type_ == hardware_interface::HandleDataType::FLOAT32)
95 value_ = initial_value.empty()
96 ? std::numeric_limits<float>::quiet_NaN()
99 catch (
const std::invalid_argument & err)
101 throw std::invalid_argument(
103 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
104 initial_value, handle_name_, data_type_.to_string()));
107 else if (data_type_ == hardware_interface::HandleDataType::BOOL)
113 catch (
const std::invalid_argument & err)
115 throw std::invalid_argument(
117 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
118 initial_value, handle_name_, data_type_.to_string()));
121 else if (data_type_ == hardware_interface::HandleDataType::UINT8)
125 value_ = initial_value.empty()
126 ?
static_cast<uint8_t
>(std::numeric_limits<uint8_t>::max())
127 :
static_cast<uint8_t
>(hardware_interface::stoui8(initial_value));
129 catch (
const std::invalid_argument & err)
131 throw std::invalid_argument(
133 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
134 initial_value, handle_name_, data_type_.to_string()));
137 else if (data_type_ == hardware_interface::HandleDataType::INT8)
141 value_ = initial_value.empty()
142 ?
static_cast<int8_t
>(std::numeric_limits<int8_t>::max())
143 :
static_cast<int8_t
>(hardware_interface::stoi8(initial_value));
145 catch (
const std::invalid_argument & err)
147 throw std::invalid_argument(
149 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
150 initial_value, handle_name_, data_type_.to_string()));
153 else if (data_type_ == hardware_interface::HandleDataType::UINT16)
157 value_ = initial_value.empty()
158 ?
static_cast<uint16_t
>(std::numeric_limits<uint16_t>::max())
159 :
static_cast<uint16_t
>(hardware_interface::stoui16(initial_value));
161 catch (
const std::invalid_argument & err)
163 throw std::invalid_argument(
165 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
166 initial_value, handle_name_, data_type_.to_string()));
169 else if (data_type_ == hardware_interface::HandleDataType::INT16)
173 value_ = initial_value.empty()
174 ?
static_cast<int16_t
>(std::numeric_limits<int16_t>::max())
175 :
static_cast<int16_t
>(hardware_interface::stoi16(initial_value));
177 catch (
const std::invalid_argument & err)
179 throw std::invalid_argument(
181 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
182 initial_value, handle_name_, data_type_.to_string()));
185 else if (data_type_ == hardware_interface::HandleDataType::UINT32)
189 value_ = initial_value.empty()
190 ?
static_cast<uint32_t
>(std::numeric_limits<uint32_t>::max())
191 :
static_cast<uint32_t
>(hardware_interface::stoui32(initial_value));
193 catch (
const std::invalid_argument & err)
195 throw std::invalid_argument(
197 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
198 initial_value, handle_name_, data_type_.to_string()));
201 else if (data_type_ == hardware_interface::HandleDataType::INT32)
205 value_ = initial_value.empty()
206 ?
static_cast<int32_t
>(std::numeric_limits<int32_t>::max())
207 :
static_cast<int32_t
>(hardware_interface::stoi32(initial_value));
209 catch (
const std::invalid_argument & err)
211 throw std::invalid_argument(
213 FMT_COMPILE(
"Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
214 initial_value, handle_name_, data_type_.to_string()));
219 throw std::runtime_error(
221 FMT_COMPILE(
"Invalid data type: '{}' for interface: {}. Check supported types."),
222 data_type, handle_name_));
228 interface_description.get_prefix_name(), interface_description.get_interface_name(),
229 interface_description.get_data_type_string(),
234 [[deprecated(
"Use InterfaceDescription for initializing the Interface")]]
236 explicit Handle(
const std::string & interface_name)
237 : interface_name_(interface_name), handle_name_(
"/" + interface_name_)
241 [[deprecated(
"Use InterfaceDescription for initializing the Interface")]]
243 explicit Handle(
const char * interface_name)
244 : interface_name_(interface_name), handle_name_(
"/" + interface_name_)
259 Handle(
Handle && other)
noexcept { swap(*
this, other); }
269 const std::string & get_name()
const {
return handle_name_; }
271 const std::string & get_interface_name()
const {
return interface_name_; }
273 const std::string & get_prefix_name()
const {
return prefix_name_; }
285 template <
typename T =
double>
288 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
289 return get_optional<T>(lock);
302 template <
typename T =
double>
303 [[nodiscard]] std::optional<T>
get_optional(std::shared_lock<std::shared_mutex> & lock)
const
305 if (!lock.owns_lock())
311 if constexpr (std::is_same_v<T, double>)
315 case HandleDataType::DOUBLE:
316 return std::get<double>(value_);
317 case HandleDataType::BOOL:
324 rclcpp::get_logger(get_name()),
"%s",
327 "Casting bool to double for interface '{}'. Better use get_optional<bool>()."),
332 return static_cast<double>(std::get<bool>(value_));
333 case HandleDataType::FLOAT32:
334 case HandleDataType::UINT8:
335 case HandleDataType::INT8:
336 case HandleDataType::UINT16:
337 case HandleDataType::INT16:
338 case HandleDataType::UINT32:
339 case HandleDataType::INT32:
340 throw std::runtime_error(
343 "Data type: '{}' will not be casted to double for interface: {}. Use "
344 "get_optional<{}>() instead."),
345 data_type_.to_string(), get_name(), data_type_.to_string()));
347 throw std::runtime_error(
349 FMT_COMPILE(
"Data type: '{}' cannot be casted to double for interface: {}"),
350 data_type_.to_string(), get_name()));
355 return std::get<T>(value_);
357 catch (
const std::bad_variant_access & err)
359 throw std::runtime_error(
361 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
362 get_type_name<T>(), get_name(), data_type_.to_string()));
380 typename T,
typename = std::enable_if_t<
381 !std::is_same_v<std::decay_t<T>, std::shared_lock<std::shared_mutex>>>>
382 [[nodiscard]]
bool get_value(T & value,
bool wait_for_lock)
const
386 std::shared_lock<std::shared_mutex> lock(handle_mutex_);
391 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
407 template <
typename T>
408 [[nodiscard]]
bool set_value(
const T & value,
bool wait_for_lock)
412 std::unique_lock<std::shared_mutex> lock(handle_mutex_);
417 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
433 template <
typename T>
436 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
452 template <
typename T>
453 [[nodiscard]]
bool set_value(std::unique_lock<std::shared_mutex> & lock,
const T & value)
455 if (!lock.owns_lock())
459 if (!std::holds_alternative<T>(value_))
461 throw std::runtime_error(
463 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
464 get_type_name<T>(), get_name(), data_type_.to_string()));
470 std::shared_mutex & get_mutex()
const {
return handle_mutex_; }
472 HandleDataType get_data_type()
const {
return data_type_; }
477 bool is_valid()
const {
return !std::holds_alternative<std::monostate>(value_); }
489 template <
typename T>
490 [[nodiscard]]
bool get_value(std::shared_lock<std::shared_mutex> & lock, T & value)
const
492 if (!lock.owns_lock())
498 if constexpr (std::is_same_v<T, double>)
502 case HandleDataType::DOUBLE:
503 value = std::get<double>(value_);
505 case HandleDataType::BOOL:
512 rclcpp::get_logger(get_name()),
513 "Casting bool to double for interface: %s. Better use get_optional<bool>().",
517 value =
static_cast<double>(std::get<bool>(value_));
519 case HandleDataType::FLOAT32:
520 case HandleDataType::UINT8:
521 case HandleDataType::INT8:
522 case HandleDataType::UINT16:
523 case HandleDataType::INT16:
524 case HandleDataType::UINT32:
525 case HandleDataType::INT32:
526 throw std::runtime_error(
529 "Data type: '{}' will not be casted to double for interface: {}. Use "
530 "get_optional<{}>() instead."),
531 data_type_.to_string(), get_name(), data_type_.to_string()));
533 throw std::runtime_error(
535 FMT_COMPILE(
"Data type: '{}' cannot be casted to double for interface: {}"),
536 data_type_.to_string(), get_name()));
541 value = std::get<T>(value_);
544 catch (
const std::bad_variant_access & err)
546 throw std::runtime_error(
548 FMT_COMPILE(
"Invalid data type: '{}' access for interface: {} expected: '{}'"),
549 get_type_name<T>(), get_name(), data_type_.to_string()));
554 void copy(
const Handle & other)
noexcept
556 std::scoped_lock lock(other.handle_mutex_, handle_mutex_);
558 interface_name_ = other.interface_name_;
559 handle_name_ = other.handle_name_;
560 value_ = other.value_;
561 data_type_ = other.data_type_;
564 void swap(Handle & first, Handle & second)
noexcept
566 std::scoped_lock lock(first.handle_mutex_, second.handle_mutex_);
567 std::swap(first.prefix_name_, second.prefix_name_);
568 std::swap(first.interface_name_, second.interface_name_);
569 std::swap(first.handle_name_, second.handle_name_);
570 std::swap(first.value_, second.value_);
571 std::swap(first.data_type_, second.data_type_);
577 std::string interface_name_;
578 std::string handle_name_;
582 mutable std::shared_mutex handle_mutex_;
588 mutable bool notified_ =
false;