1mod override_map;
2mod value;
3
4pub(crate) use override_map::*;
5pub use value::*;
6
7use crate::rcl_bindings::*;
8use crate::{call_string_getter_with_handle, RclrsError};
9use std::collections::{btree_map::Entry, BTreeMap};
10use std::fmt::Debug;
11use std::marker::PhantomData;
12use std::sync::{
13 atomic::{AtomicBool, Ordering},
14 Arc, Mutex, RwLock, Weak,
15};
16
17#[derive(Clone, Debug)]
38struct ParameterOptionsStorage {
39 _description: Arc<str>,
40 _constraints: Arc<str>,
41 ranges: ParameterRanges,
42}
43
44impl<T: ParameterVariant> From<ParameterOptions<T>> for ParameterOptionsStorage {
45 fn from(opts: ParameterOptions<T>) -> Self {
46 Self {
47 _description: opts.description,
48 _constraints: opts.constraints,
49 ranges: opts.ranges.into(),
50 }
51 }
52}
53
54#[derive(Clone, Debug)]
57pub struct ParameterOptions<T: ParameterVariant> {
58 description: Arc<str>,
59 constraints: Arc<str>,
60 ranges: T::Range,
61}
62
63impl<T: ParameterVariant> Default for ParameterOptions<T> {
64 fn default() -> Self {
65 Self {
66 description: Arc::from(""),
67 constraints: Arc::from(""),
68 ranges: Default::default(),
69 }
70 }
71}
72
73impl From<ParameterRange<f64>> for ParameterRanges {
74 fn from(params: ParameterRange<f64>) -> Self {
75 Self {
76 float: Some(params),
77 ..Default::default()
78 }
79 }
80}
81
82impl From<ParameterRange<i64>> for ParameterRanges {
83 fn from(params: ParameterRange<i64>) -> Self {
84 Self {
85 integer: Some(params),
86 ..Default::default()
87 }
88 }
89}
90
91impl From<()> for ParameterRanges {
92 fn from(_empty: ()) -> Self {
93 Self::default()
94 }
95}
96
97#[derive(Clone, Debug, Default)]
104pub struct ParameterRanges {
105 float: Option<ParameterRange<f64>>,
106 integer: Option<ParameterRange<i64>>,
107}
108
109impl ParameterRanges {
110 fn validate(&self) -> Result<(), DeclarationError> {
111 if let Some(integer) = &self.integer {
112 integer.validate()?;
113 }
114 if let Some(float) = &self.float {
115 float.validate()?;
116 }
117 Ok(())
118 }
119
120 fn in_range(&self, value: &ParameterValue) -> bool {
121 match value {
122 ParameterValue::Integer(v) => {
123 if let Some(range) = &self.integer {
124 if !range.in_range(*v) {
125 return false;
126 }
127 }
128 }
129 ParameterValue::Double(v) => {
130 if let Some(range) = &self.float {
131 if !range.in_range(*v) {
132 return false;
133 }
134 }
135 }
136 _ => {}
137 }
138 true
139 }
140}
141
142#[derive(Clone, Debug, Default)]
144pub struct ParameterRange<T: ParameterVariant + PartialOrd> {
145 pub lower: Option<T>,
147 pub upper: Option<T>,
149 pub step: Option<T>,
155}
156
157impl<T: ParameterVariant + PartialOrd + Default> ParameterRange<T> {
158 fn inside_boundary(&self, value: &T) -> bool {
159 if self.lower.as_ref().is_some_and(|l| value < l) {
160 return false;
161 }
162 if self.upper.as_ref().is_some_and(|u| value > u) {
163 return false;
164 }
165 true
166 }
167
168 fn validate(&self) -> Result<(), DeclarationError> {
169 if self
170 .lower
171 .as_ref()
172 .zip(self.upper.as_ref())
173 .is_some_and(|(l, u)| l > u)
174 {
175 return Err(DeclarationError::InvalidRange);
176 }
177 if self.step.as_ref().is_some_and(|s| s <= &T::default()) {
178 return Err(DeclarationError::InvalidRange);
179 }
180 Ok(())
181 }
182}
183
184impl ParameterRange<i64> {
185 fn in_range(&self, value: i64) -> bool {
186 if !self.inside_boundary(&value) {
187 return false;
188 }
189 if self.upper.is_some_and(|u| u == value) {
190 return true;
191 }
192 if let (Some(l), Some(s)) = (self.lower, self.step) {
193 if (value - l) % s != 0 {
194 return false;
195 }
196 }
197 true
198 }
199}
200
201impl ParameterRange<f64> {
202 fn are_close(v1: f64, v2: f64) -> bool {
204 const ULP_TOL: f64 = 100.0;
205 (v1 - v2).abs() <= (f64::EPSILON * (v1 + v2).abs() * ULP_TOL)
206 }
207
208 fn in_range(&self, value: f64) -> bool {
209 if self.upper.is_some_and(|u| Self::are_close(u, value))
210 || self.lower.is_some_and(|l| Self::are_close(l, value))
211 {
212 return true;
213 }
214 if !self.inside_boundary(&value) {
215 return false;
216 }
217 if let (Some(l), Some(s)) = (self.lower, self.step) {
218 if !Self::are_close(((value - l) / s).round() * s + l, value) {
219 return false;
220 }
221 }
222 true
223 }
224}
225
226#[derive(Clone, Debug)]
227enum DeclaredValue {
228 Mandatory(Arc<RwLock<ParameterValue>>),
229 Optional(Arc<RwLock<Option<ParameterValue>>>),
230 ReadOnly(ParameterValue),
231}
232
233#[must_use]
236pub struct ParameterBuilder<'a, T: ParameterVariant> {
237 name: Arc<str>,
238 default_value: Option<T>,
239 ignore_override: bool,
240 discard_mismatching_prior_value: bool,
241 discriminator: DiscriminatorFunction<'a, T>,
242 options: ParameterOptions<T>,
243 interface: &'a ParameterInterface,
244}
245
246impl<'a, T: ParameterVariant> ParameterBuilder<'a, T> {
247 pub fn default(mut self, value: T) -> Self {
258 self.default_value = Some(value);
259 self
260 }
261
262 pub fn ignore_override(mut self) -> Self {
268 self.ignore_override = true;
269 self
270 }
271
272 pub fn discard_mismatching_prior_value(mut self) -> Self {
279 self.discard_mismatching_prior_value = true;
280 self
281 }
282
283 pub fn discriminate<F>(mut self, f: F) -> Self
288 where
289 F: FnOnce(AvailableValues<T>) -> Option<T> + 'a,
290 {
291 self.discriminator = Box::new(f);
292 self
293 }
294
295 pub fn range(mut self, range: T::Range) -> Self {
297 self.options.ranges = range;
298 self
299 }
300
301 pub fn description(mut self, description: impl Into<Arc<str>>) -> Self {
303 self.options.description = description.into();
304 self
305 }
306
307 pub fn constraints(mut self, constraints: impl Into<Arc<str>>) -> Self {
311 self.options.constraints = constraints.into();
312 self
313 }
314
315 pub fn mandatory(self) -> Result<MandatoryParameter<T>, DeclarationError> {
321 self.try_into()
322 }
323
324 pub fn read_only(self) -> Result<ReadOnlyParameter<T>, DeclarationError> {
330 self.try_into()
331 }
332
333 pub fn optional(self) -> Result<OptionalParameter<T>, DeclarationError> {
341 self.try_into()
342 }
343}
344
345impl<'a, T> ParameterBuilder<'a, Arc<[T]>>
346where
347 Arc<[T]>: ParameterVariant,
348{
349 pub fn default_from_iter(mut self, default_value: impl IntoIterator<Item = T>) -> Self {
351 self.default_value = Some(default_value.into_iter().collect());
352 self
353 }
354}
355
356impl<'a> ParameterBuilder<'a, Arc<[Arc<str>]>> {
357 pub fn default_string_array<U>(mut self, default_value: U) -> Self
359 where
360 U: IntoIterator,
361 U::Item: Into<Arc<str>>,
362 {
363 self.default_value = Some(default_value.into_iter().map(|v| v.into()).collect());
364 self
365 }
366}
367
368pub struct AvailableValues<'a, T> {
371 pub default_value: Option<T>,
373 pub override_value: Option<T>,
375 pub prior_value: Option<T>,
377 pub ranges: &'a ParameterRanges,
379}
380
381pub fn default_initial_value_discriminator<T: ParameterVariant>(
394 available: AvailableValues<T>,
395) -> Option<T> {
396 if let Some(prior) = available.prior_value {
397 if available.ranges.in_range(&prior.clone().into()) {
398 return Some(prior);
399 }
400 }
401 if available.override_value.is_some() {
402 return available.override_value;
403 }
404 available.default_value
405}
406
407type DiscriminatorFunction<'a, T> = Box<dyn FnOnce(AvailableValues<T>) -> Option<T> + 'a>;
408
409impl<T: ParameterVariant> TryFrom<ParameterBuilder<'_, T>> for OptionalParameter<T> {
410 type Error = DeclarationError;
411
412 fn try_from(builder: ParameterBuilder<T>) -> Result<Self, Self::Error> {
413 let ranges = builder.options.ranges.clone().into();
414 let initial_value = builder.interface.get_declaration_initial_value::<T>(
415 &builder.name,
416 builder.default_value,
417 builder.ignore_override,
418 builder.discard_mismatching_prior_value,
419 builder.discriminator,
420 &ranges,
421 )?;
422 let value = Arc::new(RwLock::new(initial_value.map(|v| v.into())));
423 builder.interface.store_parameter(
424 builder.name.clone(),
425 T::kind(),
426 DeclaredValue::Optional(value.clone()),
427 builder.options.into(),
428 );
429 Ok(OptionalParameter {
430 name: builder.name,
431 value,
432 ranges,
433 map: Arc::downgrade(&builder.interface._parameter_map),
434 _marker: Default::default(),
435 })
436 }
437}
438
439pub struct MandatoryParameter<T: ParameterVariant> {
443 name: Arc<str>,
444 value: Arc<RwLock<ParameterValue>>,
445 ranges: ParameterRanges,
446 map: Weak<Mutex<ParameterMap>>,
447 _marker: PhantomData<T>,
448}
449
450impl<T: ParameterVariant + Debug> Debug for MandatoryParameter<T> {
451 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
452 f.debug_struct("MandatoryParameter")
453 .field("name", &self.name)
454 .field("value", &self.get())
455 .field("range", &self.ranges)
456 .finish()
457 }
458}
459
460impl<T: ParameterVariant> Drop for MandatoryParameter<T> {
461 fn drop(&mut self) {
462 if let Some(map) = self.map.upgrade() {
464 let storage = &mut map.lock().unwrap().storage;
465 storage.remove(&self.name);
466 }
467 }
468}
469
470impl<'a, T: ParameterVariant + 'a> TryFrom<ParameterBuilder<'a, T>> for MandatoryParameter<T> {
471 type Error = DeclarationError;
472
473 fn try_from(builder: ParameterBuilder<T>) -> Result<Self, Self::Error> {
474 let ranges = builder.options.ranges.clone().into();
475 let initial_value = builder.interface.get_declaration_initial_value::<T>(
476 &builder.name,
477 builder.default_value,
478 builder.ignore_override,
479 builder.discard_mismatching_prior_value,
480 builder.discriminator,
481 &ranges,
482 )?;
483 let Some(initial_value) = initial_value else {
484 return Err(DeclarationError::NoValueAvailable);
485 };
486 let value = Arc::new(RwLock::new(initial_value.into()));
487 builder.interface.store_parameter(
488 builder.name.clone(),
489 T::kind(),
490 DeclaredValue::Mandatory(value.clone()),
491 builder.options.into(),
492 );
493 Ok(MandatoryParameter {
494 name: builder.name,
495 value,
496 ranges,
497 map: Arc::downgrade(&builder.interface._parameter_map),
498 _marker: Default::default(),
499 })
500 }
501}
502
503pub struct OptionalParameter<T: ParameterVariant> {
507 name: Arc<str>,
508 value: Arc<RwLock<Option<ParameterValue>>>,
509 ranges: ParameterRanges,
510 map: Weak<Mutex<ParameterMap>>,
511 _marker: PhantomData<T>,
512}
513
514impl<T: ParameterVariant + Debug> Debug for OptionalParameter<T> {
515 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
516 f.debug_struct("OptionalParameter")
517 .field("name", &self.name)
518 .field("value", &self.get())
519 .field("range", &self.ranges)
520 .finish()
521 }
522}
523
524impl<T: ParameterVariant> Drop for OptionalParameter<T> {
525 fn drop(&mut self) {
526 if let Some(map) = self.map.upgrade() {
528 let storage = &mut map.lock().unwrap().storage;
529 storage.remove(&self.name);
530 }
531 }
532}
533
534pub struct ReadOnlyParameter<T: ParameterVariant> {
538 name: Arc<str>,
539 value: ParameterValue,
540 map: Weak<Mutex<ParameterMap>>,
541 _marker: PhantomData<T>,
542}
543
544impl<T: ParameterVariant + Debug> Debug for ReadOnlyParameter<T> {
545 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
546 f.debug_struct("ReadOnlyParameter")
547 .field("name", &self.name)
548 .field("value", &self.value)
549 .finish()
550 }
551}
552
553impl<T: ParameterVariant> Drop for ReadOnlyParameter<T> {
554 fn drop(&mut self) {
555 if let Some(map) = self.map.upgrade() {
557 let storage = &mut map.lock().unwrap().storage;
558 storage.remove(&self.name);
559 }
560 }
561}
562
563impl<'a, T: ParameterVariant + 'a> TryFrom<ParameterBuilder<'a, T>> for ReadOnlyParameter<T> {
564 type Error = DeclarationError;
565
566 fn try_from(builder: ParameterBuilder<T>) -> Result<Self, Self::Error> {
567 let ranges = builder.options.ranges.clone().into();
568 let initial_value = builder.interface.get_declaration_initial_value::<T>(
569 &builder.name,
570 builder.default_value,
571 builder.ignore_override,
572 builder.discard_mismatching_prior_value,
573 builder.discriminator,
574 &ranges,
575 )?;
576 let Some(initial_value) = initial_value else {
577 return Err(DeclarationError::NoValueAvailable);
578 };
579 let value = initial_value.into();
580 builder.interface.store_parameter(
581 builder.name.clone(),
582 T::kind(),
583 DeclaredValue::ReadOnly(value.clone()),
584 builder.options.into(),
585 );
586 Ok(ReadOnlyParameter {
587 name: builder.name,
588 value,
589 map: Arc::downgrade(&builder.interface._parameter_map),
590 _marker: Default::default(),
591 })
592 }
593}
594
595#[derive(Clone, Debug)]
596struct DeclaredStorage {
597 value: DeclaredValue,
598 kind: ParameterKind,
599 options: ParameterOptionsStorage,
600}
601
602#[derive(Debug)]
603enum ParameterStorage {
604 Declared(DeclaredStorage),
605 Undeclared(ParameterValue),
606}
607
608#[derive(Debug, Default)]
609struct ParameterMap {
610 storage: BTreeMap<Arc<str>, ParameterStorage>,
611}
612
613impl<T: ParameterVariant> MandatoryParameter<T> {
614 pub fn get(&self) -> T {
616 self.value.read().unwrap().clone().try_into().ok().unwrap()
617 }
618
619 pub fn set<U: Into<T>>(&self, value: U) -> Result<(), ParameterValueError> {
622 let value = value.into().into();
623 if !self.ranges.in_range(&value) {
624 return Err(ParameterValueError::OutOfRange);
625 }
626 *self.value.write().unwrap() = value;
627 Ok(())
628 }
629}
630
631impl<T: ParameterVariant> ReadOnlyParameter<T> {
632 pub fn get(&self) -> T {
634 self.value.clone().try_into().ok().unwrap()
635 }
636}
637
638impl<T: ParameterVariant> OptionalParameter<T> {
639 pub fn get(&self) -> Option<T> {
641 self.value
642 .read()
643 .unwrap()
644 .clone()
645 .map(|p| p.try_into().ok().unwrap())
646 }
647
648 pub fn set<U: Into<T>>(&self, value: U) -> Result<(), ParameterValueError> {
651 let value = value.into().into();
652 if !self.ranges.in_range(&value) {
653 return Err(ParameterValueError::OutOfRange);
654 }
655 *self.value.write().unwrap() = Some(value);
656 Ok(())
657 }
658
659 pub fn unset(&self) {
661 *self.value.write().unwrap() = None;
662 }
663}
664
665pub struct Parameters<'a> {
667 pub(crate) interface: &'a ParameterInterface,
668}
669
670#[derive(Debug)]
672pub enum ParameterValueError {
673 OutOfRange,
675 TypeMismatch,
677 ReadOnly,
679}
680
681#[derive(Debug)]
683pub enum DeclarationError {
684 AlreadyDeclared,
686 NoValueAvailable,
689 OverrideValueTypeMismatch,
692 PriorValueTypeMismatch,
695 InitialValueOutOfRange,
697 InvalidRange,
699}
700
701impl<'a> Parameters<'a> {
702 pub fn get<T: ParameterVariant>(&self, name: &str) -> Option<T> {
706 let storage = &self.interface._parameter_map.lock().unwrap().storage;
707 let storage = storage.get(name)?;
708 match storage {
709 ParameterStorage::Declared(storage) => match &storage.value {
710 DeclaredValue::Mandatory(p) => p.read().unwrap().clone().try_into().ok(),
711 DeclaredValue::Optional(p) => {
712 p.read().unwrap().clone().and_then(|p| p.try_into().ok())
713 }
714 DeclaredValue::ReadOnly(p) => p.clone().try_into().ok(),
715 },
716 ParameterStorage::Undeclared(value) => value.clone().try_into().ok(),
717 }
718 }
719
720 pub fn set<T: ParameterVariant>(
727 &self,
728 name: impl Into<Arc<str>>,
729 value: T,
730 ) -> Result<(), ParameterValueError> {
731 let mut map = self.interface._parameter_map.lock().unwrap();
732 let name: Arc<str> = name.into();
733 match map.storage.entry(name) {
734 Entry::Occupied(mut entry) => {
735 match entry.get_mut() {
738 ParameterStorage::Declared(param) => {
739 if T::kind() == param.kind {
740 let value = value.into();
741 if !param.options.ranges.in_range(&value) {
742 return Err(ParameterValueError::OutOfRange);
743 }
744 match ¶m.value {
745 DeclaredValue::Mandatory(p) => *p.write().unwrap() = value,
746 DeclaredValue::Optional(p) => *p.write().unwrap() = Some(value),
747 DeclaredValue::ReadOnly(_) => {
748 return Err(ParameterValueError::ReadOnly);
749 }
750 }
751 } else {
752 return Err(ParameterValueError::TypeMismatch);
753 }
754 }
755 ParameterStorage::Undeclared(param) => {
756 *param = value.into();
757 }
758 }
759 }
760 Entry::Vacant(entry) => {
761 entry.insert(ParameterStorage::Undeclared(value.into()));
762 }
763 }
764
765 Ok(())
766 }
767}
768
769pub(crate) struct ParameterInterface {
770 _parameter_map: Arc<Mutex<ParameterMap>>,
771 _override_map: ParameterOverrideMap,
772 allow_undeclared: AtomicBool,
773 }
776
777impl ParameterInterface {
778 pub(crate) fn new(
779 rcl_node_mtx: &Arc<Mutex<rcl_node_t>>,
780 node_arguments: &rcl_arguments_t,
781 global_arguments: &rcl_arguments_t,
782 ) -> Result<Self, RclrsError> {
783 let rcl_node = rcl_node_mtx.lock().unwrap();
784 let _override_map = unsafe {
785 let fqn = call_string_getter_with_handle(&rcl_node, rcl_node_get_fully_qualified_name);
786 resolve_parameter_overrides(&fqn, node_arguments, global_arguments)?
787 };
788
789 Ok(ParameterInterface {
790 _parameter_map: Default::default(),
791 _override_map,
792 allow_undeclared: Default::default(),
793 })
794 }
795
796 pub(crate) fn declare<'a, T: ParameterVariant + 'a>(
797 &'a self,
798 name: Arc<str>,
799 ) -> ParameterBuilder<'a, T> {
800 ParameterBuilder {
801 name,
802 default_value: None,
803 ignore_override: false,
804 discard_mismatching_prior_value: false,
805 discriminator: Box::new(default_initial_value_discriminator::<T>),
806 options: Default::default(),
807 interface: self,
808 }
809 }
810
811 fn get_declaration_initial_value<'a, T: ParameterVariant + 'a>(
812 &self,
813 name: &str,
814 default_value: Option<T>,
815 ignore_override: bool,
816 discard_mismatching_prior: bool,
817 discriminator: DiscriminatorFunction<T>,
818 ranges: &ParameterRanges,
819 ) -> Result<Option<T>, DeclarationError> {
820 ranges.validate()?;
821 let override_value: Option<T> = if ignore_override {
822 None
823 } else if let Some(override_value) = self._override_map.get(name).cloned() {
824 Some(
825 override_value
826 .try_into()
827 .map_err(|_| DeclarationError::OverrideValueTypeMismatch)?,
828 )
829 } else {
830 None
831 };
832
833 let prior_value =
834 if let Some(prior_value) = self._parameter_map.lock().unwrap().storage.get(name) {
835 match prior_value {
836 ParameterStorage::Declared(_) => return Err(DeclarationError::AlreadyDeclared),
837 ParameterStorage::Undeclared(param) => match param.clone().try_into() {
838 Ok(prior) => Some(prior),
839 Err(_) => {
840 if !discard_mismatching_prior {
841 return Err(DeclarationError::PriorValueTypeMismatch);
842 }
843 None
844 }
845 },
846 }
847 } else {
848 None
849 };
850
851 let selection = discriminator(AvailableValues {
852 default_value,
853 override_value,
854 prior_value,
855 ranges,
856 });
857 if let Some(initial_value) = &selection {
858 if !ranges.in_range(&initial_value.clone().into()) {
859 return Err(DeclarationError::InitialValueOutOfRange);
860 }
861 }
862 Ok(selection)
863 }
864
865 fn store_parameter(
866 &self,
867 name: Arc<str>,
868 kind: ParameterKind,
869 value: DeclaredValue,
870 options: ParameterOptionsStorage,
871 ) {
872 self._parameter_map.lock().unwrap().storage.insert(
873 name,
874 ParameterStorage::Declared(DeclaredStorage {
875 options,
876 value,
877 kind,
878 }),
879 );
880 }
881
882 pub(crate) fn allow_undeclared(&self) {
883 self.allow_undeclared.store(true, Ordering::Relaxed);
884 }
885}
886
887#[cfg(test)]
888mod tests {
889 use super::*;
890 use crate::{create_node, Context};
891
892 #[test]
893 fn test_parameter_override_errors() {
894 let ctx = Context::new([
896 String::from("--ros-args"),
897 String::from("-p"),
898 String::from("declared_int:=10"),
899 ])
900 .unwrap();
901 let node = create_node(&ctx, "param_test_node").unwrap();
902
903 assert!(matches!(
906 node.declare_parameter("declared_int")
907 .default(1.0)
908 .mandatory(),
909 Err(DeclarationError::OverrideValueTypeMismatch)
910 ));
911
912 assert!(node
914 .declare_parameter("declared_int")
915 .default(1.0)
916 .ignore_override()
917 .mandatory()
918 .is_ok());
919
920 let range = ParameterRange {
922 upper: Some(5),
923 ..Default::default()
924 };
925 assert!(matches!(
926 node.declare_parameter("declared_int")
927 .default(1)
928 .range(range.clone())
929 .mandatory(),
930 Err(DeclarationError::InitialValueOutOfRange)
931 ));
932
933 assert!(node
936 .declare_parameter("declared_int")
937 .default(1)
938 .range(range)
939 .ignore_override()
940 .mandatory()
941 .is_ok());
942 }
943
944 #[test]
945 fn test_parameter_setting_declaring() {
946 let ctx = Context::new([
948 String::from("--ros-args"),
949 String::from("-p"),
950 String::from("declared_int:=10"),
951 String::from("-p"),
952 String::from("double_array:=[1.0, 2.0]"),
953 String::from("-p"),
954 String::from("optional_bool:=true"),
955 String::from("-p"),
956 String::from("non_declared_string:='param'"),
957 ])
958 .unwrap();
959 let node = create_node(&ctx, "param_test_node").unwrap();
960
961 let overridden_int = node
962 .declare_parameter("declared_int")
963 .default(123)
964 .mandatory()
965 .unwrap();
966 assert_eq!(overridden_int.get(), 10);
967
968 let new_param = node
969 .declare_parameter("new_param")
970 .default(2.0)
971 .mandatory()
972 .unwrap();
973 assert_eq!(new_param.get(), 2.0);
974
975 assert_eq!(
977 node.use_undeclared_parameters().get::<f64>("new_param"),
978 Some(2.0)
979 );
980
981 assert!(node
983 .use_undeclared_parameters()
984 .get::<i64>("new_param")
985 .is_none());
986 assert!(matches!(
987 node.use_undeclared_parameters().set("new_param", 42),
988 Err(ParameterValueError::TypeMismatch)
989 ));
990
991 assert!(node
994 .use_undeclared_parameters()
995 .set("new_param", 10.0)
996 .is_ok());
997 assert_eq!(
998 node.use_undeclared_parameters().get("new_param"),
999 Some(10.0)
1000 );
1001 assert_eq!(new_param.get(), 10.0);
1002 new_param.set(5.0).unwrap();
1003 assert_eq!(new_param.get(), 5.0);
1004 assert_eq!(node.use_undeclared_parameters().get("new_param"), Some(5.0));
1005
1006 assert_eq!(
1008 node.use_undeclared_parameters()
1009 .get::<f64>("non_existing_param"),
1010 None
1011 );
1012
1013 assert_eq!(
1016 node.use_undeclared_parameters()
1017 .get::<Arc<str>>("non_declared_string"),
1018 None
1019 );
1020
1021 {
1024 node.use_undeclared_parameters()
1025 .set("new_bool", true)
1026 .unwrap();
1027 let bool_param = node
1028 .declare_parameter("new_bool")
1029 .default(false)
1030 .mandatory()
1031 .unwrap();
1032 assert!(bool_param.get());
1033 }
1034 {
1035 node.use_undeclared_parameters()
1036 .set("new_bool", true)
1037 .unwrap();
1038 let bool_param = node
1039 .declare_parameter("new_bool")
1040 .default(false)
1041 .optional()
1042 .unwrap();
1043 assert_eq!(bool_param.get(), Some(true));
1044 }
1045
1046 let optional_param = node
1047 .declare_parameter("non_existing_bool")
1048 .optional()
1049 .unwrap();
1050 assert_eq!(optional_param.get(), None);
1051 optional_param.set(true).unwrap();
1052 assert_eq!(optional_param.get(), Some(true));
1053 optional_param.unset();
1054 assert_eq!(optional_param.get(), None);
1055
1056 let optional_param2 = node
1057 .declare_parameter("non_existing_bool2")
1058 .default(false)
1059 .optional()
1060 .unwrap();
1061 assert_eq!(optional_param2.get(), Some(false));
1062
1063 let optional_param3 = node
1065 .declare_parameter("optional_bool")
1066 .default(false)
1067 .optional()
1068 .unwrap();
1069 assert_eq!(optional_param3.get(), Some(true));
1070
1071 let array_param = node
1073 .declare_parameter("double_array")
1074 .default_from_iter(vec![10.0, 20.0])
1075 .mandatory()
1076 .unwrap();
1077 assert_eq!(array_param.get()[0], 1.0);
1078 assert_eq!(array_param.get()[1], 2.0);
1079
1080 let array_param = node
1081 .declare_parameter("string_array")
1082 .default_string_array(vec!["Hello", "World"])
1083 .mandatory()
1084 .unwrap();
1085 assert_eq!(array_param.get()[0], "Hello".into());
1086 assert_eq!(array_param.get()[1], "World".into());
1087
1088 node.use_undeclared_parameters()
1091 .set("undeclared_int", 42)
1092 .unwrap();
1093 let undeclared_int = node
1094 .declare_parameter("undeclared_int")
1095 .default(10)
1096 .mandatory()
1097 .unwrap();
1098 assert_eq!(undeclared_int.get(), 42);
1099 }
1100
1101 #[test]
1102 fn test_override_undeclared_set_priority() {
1103 let ctx = Context::new([
1104 String::from("--ros-args"),
1105 String::from("-p"),
1106 String::from("declared_int:=10"),
1107 ])
1108 .unwrap();
1109 let node = create_node(&ctx, "param_test_node").unwrap();
1110 node.use_undeclared_parameters()
1113 .set("declared_int", 20)
1114 .unwrap();
1115 let param = node
1116 .declare_parameter("declared_int")
1117 .default(30)
1118 .mandatory()
1119 .unwrap();
1120 assert_eq!(param.get(), 20);
1121 }
1122
1123 #[test]
1124 fn test_parameter_scope_redeclaring() {
1125 let ctx = Context::new([
1126 String::from("--ros-args"),
1127 String::from("-p"),
1128 String::from("declared_int:=10"),
1129 ])
1130 .unwrap();
1131 let node = create_node(&ctx, "param_test_node").unwrap();
1132 {
1133 let param = node
1135 .declare_parameter("declared_int")
1136 .default(1)
1137 .mandatory()
1138 .unwrap();
1139 assert_eq!(param.get(), 10);
1140 param.set(2).unwrap();
1141 assert_eq!(param.get(), 2);
1142 assert!(matches!(
1144 node.declare_parameter("declared_int")
1145 .default(1)
1146 .mandatory(),
1147 Err(DeclarationError::AlreadyDeclared)
1148 ));
1149 }
1150 {
1151 let param = node
1154 .declare_parameter::<i64>("declared_int")
1155 .mandatory()
1156 .unwrap();
1157 assert_eq!(param.get(), 10);
1158 }
1159 assert!(node
1162 .use_undeclared_parameters()
1163 .get::<i64>("declared_int")
1164 .is_none());
1165 node.use_undeclared_parameters()
1166 .set("declared_int", 1.0)
1167 .unwrap();
1168 assert_eq!(
1169 node.use_undeclared_parameters().get::<f64>("declared_int"),
1170 Some(1.0)
1171 );
1172 }
1173
1174 #[test]
1175 fn test_parameter_ranges() {
1176 let ctx = Context::new([]).unwrap();
1177 let node = create_node(&ctx, "param_test_node").unwrap();
1178 let range = ParameterRange {
1180 lower: Some(10),
1181 upper: Some(-10),
1182 step: Some(3),
1183 };
1184 assert!(matches!(
1185 node.declare_parameter("int_param")
1186 .default(5)
1187 .range(range)
1188 .mandatory(),
1189 Err(DeclarationError::InvalidRange)
1190 ));
1191 let range = ParameterRange {
1192 lower: Some(-10),
1193 upper: Some(10),
1194 step: Some(-1),
1195 };
1196 assert!(matches!(
1197 node.declare_parameter("int_param")
1198 .default(5)
1199 .range(range)
1200 .mandatory(),
1201 Err(DeclarationError::InvalidRange)
1202 ));
1203 let range = ParameterRange {
1205 lower: Some(-10),
1206 upper: Some(10),
1207 step: Some(3),
1208 };
1209 assert!(matches!(
1210 node.declare_parameter("out_of_range_int")
1211 .default(100)
1212 .range(range.clone())
1213 .mandatory(),
1214 Err(DeclarationError::InitialValueOutOfRange)
1215 ));
1216 assert!(matches!(
1217 node.declare_parameter("wrong_step_int")
1218 .default(-9)
1219 .range(range.clone())
1220 .mandatory(),
1221 Err(DeclarationError::InitialValueOutOfRange)
1222 ));
1223 let param = node
1224 .declare_parameter("int_param")
1225 .default(-7)
1226 .range(range)
1227 .mandatory()
1228 .unwrap();
1229 assert!(param.set(10).is_ok());
1231 assert!(matches!(
1233 node.use_undeclared_parameters().set("int_param", 100),
1234 Err(ParameterValueError::OutOfRange)
1235 ));
1236 assert!(matches!(
1237 node.use_undeclared_parameters().set("int_param", -9),
1238 Err(ParameterValueError::OutOfRange)
1239 ));
1240 assert!(node
1241 .use_undeclared_parameters()
1242 .set("int_param", -4)
1243 .is_ok());
1244
1245 let range = ParameterRange {
1247 lower: Some(-10.0),
1248 upper: Some(10.0),
1249 step: Some(3.0),
1250 };
1251 assert!(matches!(
1252 node.declare_parameter("out_of_range_double")
1253 .default(100.0)
1254 .range(range.clone())
1255 .optional(),
1256 Err(DeclarationError::InitialValueOutOfRange)
1257 ));
1258 assert!(matches!(
1259 node.declare_parameter("wrong_step_double")
1260 .default(-9.0)
1261 .range(range.clone())
1262 .read_only(),
1263 Err(DeclarationError::InitialValueOutOfRange)
1264 ));
1265 let param = node
1266 .declare_parameter("double_param")
1267 .default(-7.0)
1268 .range(range.clone())
1269 .mandatory()
1270 .unwrap();
1271 assert!(param.set(10.0).is_ok());
1273 assert!(matches!(
1275 param.set(-7.001),
1276 Err(ParameterValueError::OutOfRange)
1277 ));
1278 assert!(param.set(-7.0 - f64::EPSILON * 10.0).is_ok());
1280 assert!(param.set(-7.0 + f64::EPSILON * 10.0).is_ok());
1281 assert!(param.set(10.0 - f64::EPSILON * 10.0).is_ok());
1283 assert!(param.set(10.0 + f64::EPSILON * 10.0).is_ok());
1284 assert!(param.set(-10.0 - f64::EPSILON * 10.0).is_ok());
1286 assert!(param.set(-10.0 + f64::EPSILON * 10.0).is_ok());
1287 assert!(matches!(
1289 node.use_undeclared_parameters().set("double_param", 100.0),
1290 Err(ParameterValueError::OutOfRange)
1291 ));
1292 assert!(matches!(
1293 node.use_undeclared_parameters().set("double_param", -9.0),
1294 Err(ParameterValueError::OutOfRange)
1295 ));
1296 assert!(node
1297 .use_undeclared_parameters()
1298 .set("double_param", -4.0)
1299 .is_ok());
1300 }
1301
1302 #[test]
1303 fn test_readonly_parameters() {
1304 let ctx = Context::new([]).unwrap();
1305 let node = create_node(&ctx, "param_test_node").unwrap();
1306 let param = node
1307 .declare_parameter("int_param")
1308 .default(100)
1309 .read_only()
1310 .unwrap();
1311 assert!(matches!(
1313 node.declare_parameter("int_param").default(100).read_only(),
1314 Err(DeclarationError::AlreadyDeclared)
1315 ));
1316 assert_eq!(param.get(), 100);
1318 assert_eq!(
1319 node.use_undeclared_parameters().get::<i64>("int_param"),
1320 Some(100)
1321 );
1322 assert!(matches!(
1324 node.use_undeclared_parameters().set("int_param", 10),
1325 Err(ParameterValueError::ReadOnly)
1326 ));
1327 }
1328
1329 #[test]
1330 fn test_preexisting_value_error() {
1331 let ctx = Context::new([]).unwrap();
1332 let node = create_node(&ctx, "param_test_node").unwrap();
1333 node.use_undeclared_parameters()
1334 .set("int_param", 100)
1335 .unwrap();
1336
1337 assert!(matches!(
1338 node.declare_parameter("int_param").default(1.0).mandatory(),
1339 Err(DeclarationError::PriorValueTypeMismatch)
1340 ));
1341
1342 let range = ParameterRange {
1343 lower: Some(-10),
1344 upper: Some(10),
1345 step: Some(3),
1346 };
1347 assert!(matches!(
1348 node.declare_parameter("int_param")
1349 .default(-1)
1350 .range(range.clone())
1351 .discriminate(|available| { available.prior_value })
1352 .mandatory(),
1353 Err(DeclarationError::InitialValueOutOfRange)
1354 ));
1355
1356 {
1357 let param = node
1360 .declare_parameter("int_param")
1361 .default(1.0)
1362 .discard_mismatching_prior_value()
1363 .mandatory()
1364 .unwrap();
1365 assert_eq!(param.get(), 1.0);
1366 }
1367 {
1368 node.use_undeclared_parameters()
1370 .set("int_param", 100)
1371 .unwrap();
1372 let param = node
1373 .declare_parameter("int_param")
1374 .default(5)
1375 .range(range)
1376 .mandatory()
1377 .unwrap();
1378 assert_eq!(param.get(), 5);
1379 }
1380 }
1381
1382 #[test]
1383 fn test_optional_parameter_apis() {
1384 let ctx = Context::new([]).unwrap();
1385 let node = create_node(&ctx, "param_test_node").unwrap();
1386 node.declare_parameter::<i64>("int_param")
1387 .optional()
1388 .unwrap();
1389 }
1390}