ros2_control - kilted
Loading...
Searching...
No Matches
handle.hpp
1// Copyright 2020 PAL Robotics S.L.
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#ifndef HARDWARE_INTERFACE__HANDLE_HPP_
16#define HARDWARE_INTERFACE__HANDLE_HPP_
17
18#include <fmt/compile.h>
19
20#include <algorithm>
21#include <cstdint>
22#include <cstdlib>
23#include <functional>
24#include <limits>
25#include <memory>
26#include <mutex>
27#include <optional>
28#include <shared_mutex>
29#include <stdexcept>
30#include <string>
31#include <type_traits>
32#include <typeinfo>
33#include <utility>
34#include <variant>
35
36#ifndef _WIN32
37#include <cxxabi.h>
38#endif
39
40#include "hardware_interface/hardware_info.hpp"
41#include "hardware_interface/introspection.hpp"
42#include "hardware_interface/lexical_casts.hpp"
43#include "hardware_interface/macros.hpp"
44
45#include "rclcpp/logging.hpp"
46
47namespace
48{
49#ifndef _WIN32
50template <typename T>
51std::string get_type_name()
52{
53 int status = 0;
54 std::unique_ptr<char[], void (*)(void *)> res{
55 abi::__cxa_demangle(typeid(T).name(), nullptr, nullptr, &status), std::free};
56 return (status == 0) ? res.get() : typeid(T).name();
57}
58#else
59// not supported on Windows, use typeid directly
60template <typename T>
61std::string get_type_name()
62{
63 return typeid(T).name();
64}
65#endif
66} // namespace
67
68namespace hardware_interface
69{
70
72class Handle
73{
74public:
75 [[deprecated("Use InterfaceDescription for initializing the Interface")]]
76 Handle(const std::string & prefix_name, const std::string & interface_name, double * value_ptr)
77 : prefix_name_(prefix_name),
78 interface_name_(interface_name),
79 handle_name_(prefix_name_ + "/" + interface_name_),
80 value_ptr_(value_ptr)
81 {
82 }
83
84 explicit Handle(
85 const std::string & prefix_name, const std::string & interface_name,
86 const std::string & data_type = "double", const std::string & initial_value = "")
87 : prefix_name_(prefix_name),
88 interface_name_(interface_name),
89 handle_name_(prefix_name_ + "/" + interface_name_),
90 data_type_(data_type)
91 {
92 // we need to initialize according the type passed in interface description
93 if (data_type_ == hardware_interface::HandleDataType::DOUBLE)
94 {
95 try
96 {
97 value_ = initial_value.empty() ? std::numeric_limits<double>::quiet_NaN()
98 : hardware_interface::stod(initial_value);
99 value_ptr_ = std::get_if<double>(&value_);
100 }
101 catch (const std::invalid_argument & err)
102 {
103 throw std::invalid_argument(
104 fmt::format(
105 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
106 initial_value, handle_name_, data_type_.to_string()));
107 }
108 }
109 else if (data_type_ == hardware_interface::HandleDataType::FLOAT32)
110 {
111 try
112 {
113 value_ptr_ = nullptr;
114 value_ = initial_value.empty()
115 ? std::numeric_limits<float>::quiet_NaN()
116 : static_cast<float>(hardware_interface::stof(initial_value));
117 }
118 catch (const std::invalid_argument & err)
119 {
120 throw std::invalid_argument(
121 fmt::format(
122 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
123 initial_value, handle_name_, data_type_.to_string()));
124 }
125 }
126 else if (data_type_ == hardware_interface::HandleDataType::BOOL)
127 {
128 try
129 {
130 value_ptr_ = nullptr;
131 value_ = initial_value.empty() ? false : hardware_interface::parse_bool(initial_value);
132 }
133 catch (const std::invalid_argument & err)
134 {
135 throw std::invalid_argument(
136 fmt::format(
137 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
138 initial_value, handle_name_, data_type_.to_string()));
139 }
140 }
141 else if (data_type_ == hardware_interface::HandleDataType::UINT8)
142 {
143 try
144 {
145 value_ptr_ = nullptr;
146 value_ = initial_value.empty()
147 ? static_cast<uint8_t>(std::numeric_limits<uint8_t>::max())
148 : static_cast<uint8_t>(hardware_interface::stoui8(initial_value));
149 }
150 catch (const std::invalid_argument & err)
151 {
152 throw std::invalid_argument(
153 fmt::format(
154 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
155 initial_value, handle_name_, data_type_.to_string()));
156 }
157 }
158 else if (data_type_ == hardware_interface::HandleDataType::INT8)
159 {
160 try
161 {
162 value_ptr_ = nullptr;
163 value_ = initial_value.empty()
164 ? static_cast<int8_t>(std::numeric_limits<int8_t>::max())
165 : static_cast<int8_t>(hardware_interface::stoi8(initial_value));
166 }
167 catch (const std::invalid_argument & err)
168 {
169 throw std::invalid_argument(
170 fmt::format(
171 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
172 initial_value, handle_name_, data_type_.to_string()));
173 }
174 }
175 else if (data_type_ == hardware_interface::HandleDataType::UINT16)
176 {
177 try
178 {
179 value_ptr_ = nullptr;
180 value_ = initial_value.empty()
181 ? static_cast<uint16_t>(std::numeric_limits<uint16_t>::max())
182 : static_cast<uint16_t>(hardware_interface::stoui16(initial_value));
183 }
184 catch (const std::invalid_argument & err)
185 {
186 throw std::invalid_argument(
187 fmt::format(
188 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
189 initial_value, handle_name_, data_type_.to_string()));
190 }
191 }
192 else if (data_type_ == hardware_interface::HandleDataType::INT16)
193 {
194 try
195 {
196 value_ptr_ = nullptr;
197 value_ = initial_value.empty()
198 ? static_cast<int16_t>(std::numeric_limits<int16_t>::max())
199 : static_cast<int16_t>(hardware_interface::stoi16(initial_value));
200 }
201 catch (const std::invalid_argument & err)
202 {
203 throw std::invalid_argument(
204 fmt::format(
205 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
206 initial_value, handle_name_, data_type_.to_string()));
207 }
208 }
209 else if (data_type_ == hardware_interface::HandleDataType::UINT32)
210 {
211 try
212 {
213 value_ptr_ = nullptr;
214 value_ = initial_value.empty()
215 ? static_cast<uint32_t>(std::numeric_limits<uint32_t>::max())
216 : static_cast<uint32_t>(hardware_interface::stoui32(initial_value));
217 }
218 catch (const std::invalid_argument & err)
219 {
220 throw std::invalid_argument(
221 fmt::format(
222 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
223 initial_value, handle_name_, data_type_.to_string()));
224 }
225 }
226 else if (data_type_ == hardware_interface::HandleDataType::INT32)
227 {
228 try
229 {
230 value_ptr_ = nullptr;
231 value_ = initial_value.empty()
232 ? static_cast<int32_t>(std::numeric_limits<int32_t>::max())
233 : static_cast<int32_t>(hardware_interface::stoi32(initial_value));
234 }
235 catch (const std::invalid_argument & err)
236 {
237 throw std::invalid_argument(
238 fmt::format(
239 FMT_COMPILE("Invalid initial value: '{}' parsed for interface: '{}' with type: '{}'"),
240 initial_value, handle_name_, data_type_.to_string()));
241 }
242 }
243 else
244 {
245 throw std::runtime_error(
246 fmt::format(
247 FMT_COMPILE("Invalid data type: '{}' for interface: {}. Check supported types."),
248 data_type, handle_name_));
249 }
250 }
251
252 explicit Handle(const InterfaceDescription & interface_description)
253 : Handle(
254 interface_description.get_prefix_name(), interface_description.get_interface_name(),
255 interface_description.get_data_type_string(),
256 interface_description.interface_info.initial_value)
257 {
258 }
259
260 [[deprecated("Use InterfaceDescription for initializing the Interface")]]
261
262 explicit Handle(const std::string & interface_name)
263 : interface_name_(interface_name), handle_name_("/" + interface_name_), value_ptr_(nullptr)
264 {
265 }
266
267 [[deprecated("Use InterfaceDescription for initializing the Interface")]]
268
269 explicit Handle(const char * interface_name)
270 : interface_name_(interface_name), handle_name_("/" + interface_name_), value_ptr_(nullptr)
271 {
272 }
273
274 Handle(const Handle & other) noexcept { copy(other); }
275
276 Handle & operator=(const Handle & other)
277 {
278 if (this != &other)
279 {
280 copy(other);
281 }
282 return *this;
283 }
284
285 Handle(Handle && other) noexcept { swap(*this, other); }
286
287 Handle & operator=(Handle && other)
288 {
289 swap(*this, other);
290 return *this;
291 }
292
293 virtual ~Handle() = default;
294
296 inline operator bool() const { return value_ptr_ != nullptr; }
297
298 const std::string & get_name() const { return handle_name_; }
299
300 const std::string & get_interface_name() const { return interface_name_; }
301
302 const std::string & get_prefix_name() const { return prefix_name_; }
303
314 template <typename T = double>
315 [[nodiscard]] std::optional<T> get_optional() const
316 {
317 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
318 return get_optional<T>(lock);
319 }
331 template <typename T = double>
332 [[nodiscard]] std::optional<T> get_optional(std::shared_lock<std::shared_mutex> & lock) const
333 {
334 if (!lock.owns_lock())
335 {
336 return std::nullopt;
337 }
338 // BEGIN (Handle export change): for backward compatibility
339 // TODO(saikishor) return value_ if old functionality is removed
340 if constexpr (std::is_same_v<T, double>)
341 {
342 switch (data_type_)
343 {
344 case HandleDataType::DOUBLE:
345 THROW_ON_NULLPTR(value_ptr_);
346 return *value_ptr_;
347 case HandleDataType::BOOL:
348 // TODO(christophfroehlich): replace with RCLCPP_WARN_ONCE once
349 // https://github.com/ros2/rclcpp/issues/2587
350 // is fixed
351 if (!notified_)
352 {
353 RCLCPP_WARN(
354 rclcpp::get_logger(get_name()), "%s",
355 fmt::format(
356 FMT_COMPILE(
357 "Casting bool to double for interface '{}'. Better use get_optional<bool>()."),
358 get_name())
359 .c_str());
360 notified_ = true;
361 }
362 return static_cast<double>(std::get<bool>(value_));
363 case HandleDataType::FLOAT32: // fallthrough
364 case HandleDataType::UINT8: // fallthrough
365 case HandleDataType::INT8: // fallthrough
366 case HandleDataType::UINT16: // fallthrough
367 case HandleDataType::INT16: // fallthrough
368 case HandleDataType::UINT32: // fallthrough
369 case HandleDataType::INT32: // fallthrough
370 throw std::runtime_error(
371 fmt::format(
372 FMT_COMPILE(
373 "Data type: '{}' will not be casted to double for interface: {}. Use "
374 "get_optional<{}>() instead."),
375 data_type_.to_string(), get_name(), data_type_.to_string()));
376 default:
377 throw std::runtime_error(
378 fmt::format(
379 FMT_COMPILE("Data type: '{}' cannot be casted to double for interface: {}"),
380 data_type_.to_string(), get_name()));
381 }
382 }
383 try
384 {
385 return std::get<T>(value_);
386 }
387 catch (const std::bad_variant_access & err)
388 {
389 throw std::runtime_error(
390 fmt::format(
391 FMT_COMPILE("Invalid data type: '{}' access for interface: {} expected: '{}'"),
392 get_type_name<T>(), get_name(), data_type_.to_string()));
393 }
394 // END
395 }
396
409 template <
410 typename T, typename = std::enable_if_t<
411 !std::is_same_v<std::decay_t<T>, std::shared_lock<std::shared_mutex>>>>
412 [[nodiscard]] bool get_value(T & value, bool wait_for_lock) const
413 {
414 if (wait_for_lock)
415 {
416 std::shared_lock<std::shared_mutex> lock(handle_mutex_);
417 return get_value(lock, value);
418 }
419 else
420 {
421 std::shared_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
422 return get_value(lock, value);
423 }
424 }
425
437 template <typename T>
438 [[nodiscard]] bool set_value(const T & value, bool wait_for_lock)
439 {
440 if (wait_for_lock)
441 {
442 std::unique_lock<std::shared_mutex> lock(handle_mutex_);
443 return set_value(lock, value);
444 }
445 else
446 {
447 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
448 return set_value(lock, value);
449 }
450 }
451
463 template <typename T>
464 [[nodiscard]] bool set_value(const T & value)
465 {
466 std::unique_lock<std::shared_mutex> lock(handle_mutex_, std::try_to_lock);
467 return set_value(lock, value);
468 }
469
482 template <typename T>
483 [[nodiscard]] bool set_value(std::unique_lock<std::shared_mutex> & lock, const T & value)
484 {
485 if (!lock.owns_lock())
486 {
487 return false;
488 }
489 // BEGIN (Handle export change): for backward compatibility
490 // TODO(Manuel) set value_ directly if old functionality is removed
491 if constexpr (std::is_same_v<T, double>)
492 {
493 // If the template is of type double, check if the value_ptr_ is not nullptr
494 THROW_ON_NULLPTR(value_ptr_);
495 *value_ptr_ = value;
496 }
497 else
498 {
499 if (!std::holds_alternative<T>(value_))
500 {
501 throw std::runtime_error(
502 fmt::format(
503 FMT_COMPILE("Invalid data type: '{}' access for interface: {} expected: '{}'"),
504 get_type_name<T>(), get_name(), data_type_.to_string()));
505 }
506 value_ = value;
507 }
508 return true;
509 // END
510 }
511
512 std::shared_mutex & get_mutex() const { return handle_mutex_; }
513
514 HandleDataType get_data_type() const { return data_type_; }
515
517 bool is_castable_to_double() const { return data_type_.is_castable_to_double(); }
518
519 bool is_valid() const
520 {
521 return (value_ptr_ != nullptr) || !std::holds_alternative<std::monostate>(value_);
522 }
523
524protected:
534 template <typename T>
535 [[nodiscard]] bool get_value(std::shared_lock<std::shared_mutex> & lock, T & value) const
536 {
537 if (!lock.owns_lock())
538 {
539 return false;
540 }
541 // BEGIN (Handle export change): for backward compatibility
542 // TODO(saikishor) get value_ if old functionality is removed
543 if constexpr (std::is_same_v<T, double>)
544 {
545 switch (data_type_)
546 {
547 case HandleDataType::DOUBLE:
548 THROW_ON_NULLPTR(value_ptr_);
549 value = *value_ptr_;
550 return true;
551 case HandleDataType::BOOL:
552 // TODO(christophfroehlich): replace with RCLCPP_WARN_ONCE once
553 // https://github.com/ros2/rclcpp/issues/2587
554 // is fixed
555 if (!notified_)
556 {
557 RCLCPP_WARN(
558 rclcpp::get_logger(get_name()),
559 "Casting bool to double for interface: %s. Better use get_optional<bool>().",
560 get_name().c_str());
561 notified_ = true;
562 }
563 value = static_cast<double>(std::get<bool>(value_));
564 return true;
565 case HandleDataType::FLOAT32: // fallthrough
566 case HandleDataType::UINT8: // fallthrough
567 case HandleDataType::INT8: // fallthrough
568 case HandleDataType::UINT16: // fallthrough
569 case HandleDataType::INT16: // fallthrough
570 case HandleDataType::UINT32: // fallthrough
571 case HandleDataType::INT32: // fallthrough
572 throw std::runtime_error(
573 fmt::format(
574 FMT_COMPILE(
575 "Data type: '{}' will not be casted to double for interface: {}. Use "
576 "get_optional<{}>() instead."),
577 data_type_.to_string(), get_name(), data_type_.to_string()));
578 default:
579 throw std::runtime_error(
580 fmt::format(
581 FMT_COMPILE("Data type: '{}' cannot be casted to double for interface: {}"),
582 data_type_.to_string(), get_name()));
583 }
584 }
585 try
586 {
587 value = std::get<T>(value_);
588 return true;
589 }
590 catch (const std::bad_variant_access & err)
591 {
592 throw std::runtime_error(
593 fmt::format(
594 FMT_COMPILE("Invalid data type: '{}' access for interface: {} expected: '{}'"),
595 get_type_name<T>(), get_name(), data_type_.to_string()));
596 }
597 }
598
599private:
600 void copy(const Handle & other) noexcept
601 {
602 std::scoped_lock lock(other.handle_mutex_, handle_mutex_);
604 interface_name_ = other.interface_name_;
605 handle_name_ = other.handle_name_;
606 value_ = other.value_;
607 data_type_ = other.data_type_;
608 if (std::holds_alternative<std::monostate>(value_))
609 {
610 value_ptr_ = other.value_ptr_;
611 }
612 else
613 {
614 value_ptr_ = std::get_if<double>(&value_);
615 }
616 }
617
618 void swap(Handle & first, Handle & second) noexcept
619 {
620 std::scoped_lock lock(first.handle_mutex_, second.handle_mutex_);
621 std::swap(first.prefix_name_, second.prefix_name_);
622 std::swap(first.interface_name_, second.interface_name_);
623 std::swap(first.handle_name_, second.handle_name_);
624 std::swap(first.value_, second.value_);
625 std::swap(first.data_type_, second.data_type_);
626 std::swap(first.value_ptr_, second.value_ptr_);
627 }
628
629protected:
631 std::string prefix_name_;
632 std::string interface_name_;
633 std::string handle_name_;
634 HANDLE_DATATYPE value_ = std::monostate{};
635 HandleDataType data_type_ = HandleDataType::DOUBLE;
636 // BEGIN (Handle export change): for backward compatibility
637 // TODO(Manuel) redeclare as HANDLE_DATATYPE * value_ptr_ if old functionality is removed
638 double * value_ptr_ = nullptr;
639 // END
640 mutable std::shared_mutex handle_mutex_;
641
642private:
643 // TODO(christophfroehlich): remove once
644 // https://github.com/ros2/rclcpp/issues/2587
645 // is fixed
646 mutable bool notified_ = false;
647};
648
649class StateInterface : public Handle
650{
651public:
652 explicit StateInterface(const InterfaceDescription & interface_description)
653 : Handle(interface_description)
654 {
655 }
656
657 void registerIntrospection() const
658 {
659 if (!is_valid())
660 {
661 RCLCPP_WARN(
662 rclcpp::get_logger(get_name()),
663 "Cannot register state introspection for state interface: %s without a valid value "
664 "pointer or initialized value.",
665 get_name().c_str());
666 return;
667 }
668 if (value_ptr_ || data_type_.is_castable_to_double())
669 {
670 std::function<double()> f = [this]()
671 {
672 if (value_ptr_)
673 {
674 return *value_ptr_;
675 }
676 else
677 {
678 return data_type_.cast_to_double(value_);
679 }
680 };
681 DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name(), f);
682 }
683 }
684
685 void unregisterIntrospection() const
686 {
687 if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
688 {
689 DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("state_interface." + get_name());
690 }
691 }
692
693 StateInterface(const StateInterface & other) = default;
694
695 StateInterface(StateInterface && other) = default;
696
697 using Handle::Handle;
698
699 using SharedPtr = std::shared_ptr<StateInterface>;
700 using ConstSharedPtr = std::shared_ptr<const StateInterface>;
701};
702
704{
705public:
706 explicit CommandInterface(const InterfaceDescription & interface_description)
707 : Handle(interface_description)
708 {
709 }
711
716 CommandInterface(const CommandInterface & other) = delete;
717
718 CommandInterface(CommandInterface && other) = default;
719
720 void set_on_set_command_limiter(std::function<double(double, bool &)> on_set_command_limiter)
721 {
722 on_set_command_limiter_ = on_set_command_limiter;
723 }
724
726
730 template <typename T>
731 [[nodiscard]] bool set_limited_value(const T & value)
732 {
733 if constexpr (std::is_same_v<T, double>)
734 {
735 return set_value(on_set_command_limiter_(value, is_command_limited_));
736 }
737 else
738 {
739 return set_value(value);
740 }
741 }
742
743 const bool & is_limited() const { return is_command_limited_; }
744
745 void registerIntrospection() const
746 {
747 if (!is_valid())
748 {
749 RCLCPP_WARN(
750 rclcpp::get_logger(get_name()),
751 "Cannot register command introspection for command interface: %s without a valid value "
752 "pointer or initialized value.",
753 get_name().c_str());
754 return;
755 }
756 if (value_ptr_ || data_type_.is_castable_to_double())
757 {
758 std::function<double()> f = [this]()
759 {
760 if (value_ptr_)
761 {
762 return *value_ptr_;
763 }
764 else
765 {
766 return data_type_.cast_to_double(value_);
767 }
768 };
769 DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name(), f);
770 DEFAULT_REGISTER_ROS2_CONTROL_INTROSPECTION(
771 "command_interface." + get_name() + ".is_limited", &is_command_limited_);
772 }
773 }
774
775 void unregisterIntrospection() const
776 {
777 if (is_valid() && (value_ptr_ || data_type_.is_castable_to_double()))
778 {
779 DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION("command_interface." + get_name());
780 DEFAULT_UNREGISTER_ROS2_CONTROL_INTROSPECTION(
781 "command_interface." + get_name() + ".is_limited");
782 }
783 }
784
785 using Handle::Handle;
786
787 using SharedPtr = std::shared_ptr<CommandInterface>;
788
789private:
790 bool is_command_limited_ = false;
791 std::function<double(double, bool &)> on_set_command_limiter_ =
792 [](double value, bool & is_limited)
793 {
794 is_limited = false;
795 return value;
796 };
797};
798
799} // namespace hardware_interface
800
801#endif // HARDWARE_INTERFACE__HANDLE_HPP_
Definition handle.hpp:704
bool set_limited_value(const T &value)
A setter for the value of the command interface that triggers the limiter.
Definition handle.hpp:731
CommandInterface(const CommandInterface &other)=delete
CommandInterface copy constructor is actively deleted.
Definition hardware_info.hpp:158
bool is_castable_to_double() const
Check if the HandleDataType can be casted to double.
Definition hardware_info.hpp:262
double cast_to_double(const HANDLE_DATATYPE &value) const
Cast the given value to double.
Definition hardware_info.hpp:288
A handle used to get and set a value on a given interface.
Definition handle.hpp:73
bool set_value(const T &value)
Set the value of the handle.
Definition handle.hpp:464
bool get_value(std::shared_lock< std::shared_mutex > &lock, T &value) const
Get the value of the handle.
Definition handle.hpp:535
std::optional< T > get_optional(std::shared_lock< std::shared_mutex > &lock) const
Get the value of the handle.
Definition handle.hpp:332
bool get_value(T &value, bool wait_for_lock) const
Get the value of the handle.
Definition handle.hpp:412
bool is_castable_to_double() const
Returns true if the handle data type can be casted to double.
Definition handle.hpp:517
std::string prefix_name_
Definition handle.hpp:631
bool set_value(std::unique_lock< std::shared_mutex > &lock, const T &value)
Set the value of the handle.
Definition handle.hpp:483
bool set_value(const T &value, bool wait_for_lock)
Set the value of the handle.
Definition handle.hpp:438
std::optional< T > get_optional() const
Get the value of the handle.
Definition handle.hpp:315
Definition handle.hpp:650
Definition mujoco_system_interface.hpp:57
std::variant< std::monostate, double, float, bool, uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t > HANDLE_DATATYPE
Definition hardware_info.hpp:156
float stof(const std::string &s)
Helper function to convert a std::string to float in a locale-independent way.
Definition lexical_casts.cpp:116
double stod(const std::string &s)
Helper function to convert a std::string to double in a locale-independent way.
Definition lexical_casts.cpp:107
bool parse_bool(const std::string &bool_string)
Parse a boolean value from a string.
Definition lexical_casts.cpp:134
Definition hardware_info.hpp:327
InterfaceInfo interface_info
Definition hardware_info.hpp:343
std::string initial_value
(Optional) Initial value of the interface.
Definition hardware_info.hpp:55