From bfa8a91c8304bc96749862c5b583f2014e76c8de Mon Sep 17 00:00:00 2001 From: Franco Cipollone <53065142+francocipollone@users.noreply.github.com> Date: Tue, 11 Jun 2024 15:53:03 -0300 Subject: [PATCH] Add rust API for DiscreteValue and Range. (#120) Signed-off-by: Franco Cipollone --- maliput-sys/src/api/rules/mod.rs | 7 +- maliput/src/api/rules/mod.rs | 139 ++++++++++++++++++++++ maliput/tests/discrete_value_rule_test.rs | 123 +++++++++++++++++++ maliput/tests/range_value_rule_test.rs | 63 ++++++++++ 4 files changed, 330 insertions(+), 2 deletions(-) create mode 100644 maliput/tests/discrete_value_rule_test.rs create mode 100644 maliput/tests/range_value_rule_test.rs diff --git a/maliput-sys/src/api/rules/mod.rs b/maliput-sys/src/api/rules/mod.rs index 1e38f19..7e6e67a 100644 --- a/maliput-sys/src/api/rules/mod.rs +++ b/maliput-sys/src/api/rules/mod.rs @@ -69,7 +69,7 @@ pub mod ffi { /// This is needed because maps can't be binded directly. struct RelatedUniqueId { pub group_name: String, - pub unique_id: Vec, + pub unique_ids: Vec, } #[repr(i32)] @@ -187,7 +187,10 @@ pub mod ffi { fn RangeValueRuleRange_description(range: &RangeValueRuleRange) -> String; fn RangeValueRuleRange_min(range: &RangeValueRuleRange) -> f64; fn RangeValueRuleRange_max(range: &RangeValueRuleRange) -> f64; - + fn RangeValueRuleRange_severity(range: &RangeValueRuleRange) -> i32; + fn RangeValueRuleRange_related_rules(range: &RangeValueRuleRange) -> UniquePtr>; + fn RangeValueRuleRange_related_unique_ids(range: &RangeValueRuleRange) + -> UniquePtr>; // RangeValueRule::Range bindings definitions. type RangeValueRule; fn RangeValueRule_id(rule: &RangeValueRule) -> String; diff --git a/maliput/src/api/rules/mod.rs b/maliput/src/api/rules/mod.rs index 14dd466..b21b32c 100644 --- a/maliput/src/api/rules/mod.rs +++ b/maliput/src/api/rules/mod.rs @@ -547,6 +547,27 @@ impl DiscreteValueRule { pub fn type_id(&self) -> String { maliput_sys::api::rules::ffi::DiscreteValueRule_type_id(&self.discrete_value_rule) } + /// Returns a [LaneSRoute] that represents the zone that the rule applies to. + pub fn zone(&self) { + unimplemented!("Not yet implemented") + } + /// Returns the states of the rule. + pub fn states(&self) -> Vec { + let states_cpp = &self.discrete_value_rule.states(); + states_cpp + .into_iter() + .map(|dv| DiscreteValue { + rule_state: RuleStateBase { + severity: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_severity(dv), + related_rules: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_rules(dv), + related_unique_ids: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_related_unique_ids( + dv, + ), + }, + value: maliput_sys::api::rules::ffi::DiscreteValueRuleDiscreteValue_value(dv), + }) + .collect::>() + } } /// ## Rule @@ -580,4 +601,122 @@ impl RangeValueRule { pub fn type_id(&self) -> String { maliput_sys::api::rules::ffi::RangeValueRule_type_id(&self.range_value_rule) } + /// Returns a [LaneSRoute] that represents the zone that the rule applies to. + pub fn zone(&self) { + unimplemented!("Not yet implemented") + } + /// Returns the states of the rule. + pub fn states(&self) -> Vec { + let states_cpp = &self.range_value_rule.states(); + states_cpp + .into_iter() + .map(|r| Range { + rule_state: RuleStateBase { + severity: maliput_sys::api::rules::ffi::RangeValueRuleRange_severity(r), + related_rules: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_rules(r), + related_unique_ids: maliput_sys::api::rules::ffi::RangeValueRuleRange_related_unique_ids(r), + }, + description: maliput_sys::api::rules::ffi::RangeValueRuleRange_description(r), + min: maliput_sys::api::rules::ffi::RangeValueRuleRange_min(r), + max: maliput_sys::api::rules::ffi::RangeValueRuleRange_max(r), + }) + .collect::>() + } +} + +/// Defines a base state for a rule. +/// +/// ## RuleStateBase +/// +/// - `severity` - The severity of the rule state. +/// - `related_rules` - A map of related rules. The key is the group name and the value is a vector of rule ids. +/// - `related_unique_ids` - A map of related unique ids. The key is the group name and the value is a vector of unique ids. +/// +/// See [DiscreteValueRule] and [RangeValueRule] for more information. +pub struct RuleStateBase { + severity: i32, + related_rules: cxx::UniquePtr>, + related_unique_ids: cxx::UniquePtr>, +} + +/// Defines the interface for a rule state. +/// ## To implement by the trait user. +/// - `get_rule_state` - Returns the base state of the rule. +/// To be implemented by the concrete rule state. +pub trait RuleState { + /// Returns the base state of the rule. + /// To be implemented by the concrete rule state. + fn get_rule_state(&self) -> &RuleStateBase; + + /// Returns the severity of the rule state. + fn severity(&self) -> i32 { + self.get_rule_state().severity + } + + /// Returns a map of related unique ids. The key is the group name and the value is a vector of unique ids. + fn related_rules(&self) -> std::collections::HashMap<&String, &Vec> { + self.get_rule_state() + .related_rules + .iter() + .map(|rr| (&rr.group_name, &rr.rule_ids)) + .collect::>>() + } + /// Returns a map of related unique ids. The key is the group name and the value is a vector of unique ids. + fn related_unique_ids(&self) -> std::collections::HashMap<&String, &Vec> { + self.get_rule_state() + .related_unique_ids + .iter() + .map(|rui| (&rui.group_name, &rui.unique_ids)) + .collect::>>() + } +} + +/// Defines a discrete value for a [DiscreteValueRule]. +/// It extends the [RuleStateBase] with the value of the discrete value. +pub struct DiscreteValue { + rule_state: RuleStateBase, + value: String, +} + +impl RuleState for DiscreteValue { + fn get_rule_state(&self) -> &RuleStateBase { + &self.rule_state + } +} + +impl DiscreteValue { + /// Returns the value of the discrete value. + pub fn value(&self) -> &String { + &self.value + } +} + +/// Defines a range value for a [RangeValueRule]. +/// It extends the [RuleStateBase] with the description, and min and max values of the range. +pub struct Range { + rule_state: RuleStateBase, + description: String, + min: f64, + max: f64, +} + +impl RuleState for Range { + fn get_rule_state(&self) -> &RuleStateBase { + &self.rule_state + } +} + +impl Range { + /// Returns the description of the range value. + pub fn description(&self) -> &String { + &self.description + } + /// Returns the minimum value of the range. + pub fn min(&self) -> f64 { + self.min + } + /// Returns the maximum value of the range. + pub fn max(&self) -> f64 { + self.max + } } diff --git a/maliput/tests/discrete_value_rule_test.rs b/maliput/tests/discrete_value_rule_test.rs new file mode 100644 index 0000000..661e820 --- /dev/null +++ b/maliput/tests/discrete_value_rule_test.rs @@ -0,0 +1,123 @@ +// BSD 3-Clause License +// +// Copyright (c) 2024, Woven by Toyota. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod common; + +// LoopRoadPedestrianCrosswalk map is being used to test the discrete value rule API and its components. +// YAML information about the RoadRulebook can be found at: +// https://github.com/maliput/maliput_malidrive/blob/352601969b1363cc13fe2008c198a3d95843bf5b/resources/LoopRoadPedestrianCrosswalk.yaml#L64 + +#[test] +fn discrete_value_rule_test_api() { + use maliput::api::rules::RuleState; + + let road_network = common::create_loop_road_pedestrian_crosswalk_road_network_with_books(); + + let book = road_network.rulebook(); + let expected_rule_id = String::from("Right-Of-Way Rule Type/WestToEastSouth"); + let expected_type_id = String::from("Right-Of-Way Rule Type"); + let rule = book.get_discrete_value_rule(&expected_rule_id); + assert_eq!(rule.id(), expected_rule_id); + assert_eq!(rule.type_id(), expected_type_id); + + let states = rule.states(); + assert_eq!(states.len(), 2); // Go and Stop + + // Find DiscreteValue with value "Go" + let expected_value = "Go"; + let go_discrete_value = states.iter().find(|state| state.value() == expected_value).unwrap(); + assert_eq!(go_discrete_value.value(), expected_value); + + let severity = go_discrete_value.severity(); + assert_eq!(severity, 0); + + let related_rules = go_discrete_value.related_rules(); + assert_eq!(related_rules.len(), 1); + let related_rule_group = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type"); + let related_rules_for_vehicle_stop_behavior_group = related_rules + .get(&related_rule_group) + .expect("Related rule group not found"); + assert_eq!(related_rules_for_vehicle_stop_behavior_group.len(), 1); + assert_eq!( + related_rules_for_vehicle_stop_behavior_group + .first() + .expect("Related rule not found"), + "Vehicle-Stop-In-Zone-Behavior Rule Type/WestToEastSouth" + ); + + let related_unique_ids = go_discrete_value.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 1); + let related_unique_id_group = String::from("Bulb Group"); + let related_unique_ids_for_bulb_group = related_unique_ids + .get(&related_unique_id_group) + .expect("Related unique id group not found"); + assert_eq!(related_unique_ids_for_bulb_group.len(), 1); + assert_eq!( + related_unique_ids_for_bulb_group + .first() + .expect("Related unique id not found"), + "WestFacingSouth-WestFacingBulbsSouth" + ); + + // Find DiscreteValue with value "Stop" + let expected_value = "Stop"; + let stop_discrete_value = states.iter().find(|state| state.value() == expected_value).unwrap(); + assert_eq!(stop_discrete_value.value(), expected_value); + + let severity = go_discrete_value.severity(); + assert_eq!(severity, 0); + + let related_rules = stop_discrete_value.related_rules(); + assert_eq!(related_rules.len(), 1); + let related_rule_group = String::from("Vehicle-Stop-In-Zone-Behavior Rule Type"); + let related_rules_for_vehicle_stop_behavior_group = related_rules + .get(&related_rule_group) + .expect("Related rule group not found"); + assert_eq!(related_rules_for_vehicle_stop_behavior_group.len(), 1); + assert_eq!( + related_rules_for_vehicle_stop_behavior_group + .first() + .expect("Related rule not found"), + "Vehicle-Stop-In-Zone-Behavior Rule Type/WestToEastSouth" + ); + + let related_unique_ids = stop_discrete_value.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 1); + let related_unique_id_group = String::from("Bulb Group"); + let related_unique_ids_for_bulb_group = related_unique_ids + .get(&related_unique_id_group) + .expect("Related unique id group not found"); + assert_eq!(related_unique_ids_for_bulb_group.len(), 1); + assert_eq!( + related_unique_ids_for_bulb_group + .first() + .expect("Related unique id not found"), + "WestFacingSouth-WestFacingBulbsSouth" + ); +} diff --git a/maliput/tests/range_value_rule_test.rs b/maliput/tests/range_value_rule_test.rs new file mode 100644 index 0000000..110345c --- /dev/null +++ b/maliput/tests/range_value_rule_test.rs @@ -0,0 +1,63 @@ +// BSD 3-Clause License +// +// Copyright (c) 2024, Woven by Toyota. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, this +// list of conditions and the following disclaimer. +// +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// +// * Neither the name of the copyright holder nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod common; + +// LoopRoadPedestrianCrosswalk map is being used to test the discrete value rule API and its components. +// YAML information about the RoadRulebook can be found at: +// https://github.com/maliput/maliput_malidrive/blob/352601969b1363cc13fe2008c198a3d95843bf5b/resources/LoopRoadPedestrianCrosswalk.yaml#L64 + +#[test] +fn range_value_rule_test_api() { + use maliput::api::rules::RuleState; + + let road_network = common::create_loop_road_pedestrian_crosswalk_road_network_with_books(); + + let book = road_network.rulebook(); + let expected_rule_id = String::from("Speed-Limit Rule Type/1_0_1_1"); + let expected_type_id = String::from("Speed-Limit Rule Type"); + let rule = book.get_range_value_rule(&expected_rule_id); + assert_eq!(rule.id(), expected_rule_id); + assert_eq!(rule.type_id(), expected_type_id); + + let states = rule.states(); + assert_eq!(states.len(), 1); // Only one speed limit state + let speed_limit_state = states.first().expect("State not found"); + assert_eq!(speed_limit_state.description(), "m/s"); + assert_eq!(speed_limit_state.min(), 0.); + assert_eq!(speed_limit_state.max(), 11.11111111111111); + + let severity = speed_limit_state.severity(); + assert_eq!(severity, 0); + + let related_rules = speed_limit_state.related_rules(); + assert_eq!(related_rules.len(), 0); + let related_unique_ids = speed_limit_state.related_unique_ids(); + assert_eq!(related_unique_ids.len(), 0); +}