Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/common/step advance builder #431

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions common/ferrostar/src/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,8 @@ fn get_route_polyline(route: &Route, precision: u32) -> Result<String, ModelErro
#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))]
#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))]
pub struct RouteStep {
/// The current step advance index.
pub step_advance_index: Option<u8>,
/// The full route geometry for this step.
pub geometry: Vec<GeographicCoordinate>,
/// The distance, in meters, to travel along the route after the maneuver to reach the next step.
Expand Down
2 changes: 2 additions & 0 deletions common/ferrostar/src/navigation_controller/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,7 @@ impl NavigationController {
visual_instruction: _,
spoken_instruction: _,
annotation_json: _,
current_condition: _,
} => {
// Recalculate deviation. This happens later, as the current step may have changed.
// The distance to the next maneuver will be updated by advance_to_next_step if needed.
Expand Down Expand Up @@ -295,6 +296,7 @@ impl NavigationController {
visual_instruction,
spoken_instruction,
annotation_json,
current_condition: self.config.step_advance_condition.new_instance(),
}
}
TripState::Complete => TripState::Complete,
Expand Down
152 changes: 113 additions & 39 deletions common/ferrostar/src/navigation_controller/models.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use crate::deviation_detection::{RouteDeviation, RouteDeviationTracking};
use crate::models::{RouteStep, SpokenInstruction, UserLocation, VisualInstruction, Waypoint};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
use std::cell::RefCell;
use geo::LineString;
#[cfg(any(feature = "wasm-bindgen", test))]
use serde::{Deserialize, Serialize};
Expand Down Expand Up @@ -38,52 +39,64 @@ pub struct TripProgress {
#[cfg_attr(any(feature = "wasm-bindgen", test), derive(Serialize, Deserialize))]
#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))]
#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))]
#[allow(clippy::large_enum_variant)]
pub enum TripState {
/// The navigation controller is idle and there is no active trip.
Idle,
#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))]
/// The navigation controller is actively navigating a trip.
Navigating {
/// The index of the closest coordinate to the user's snapped location.
///
/// This index is relative to the *current* [`RouteStep`]'s geometry.
current_step_geometry_index: Option<u64>,
/// A location on the line string that
snapped_user_location: UserLocation,
/// The ordered list of steps that remain in the trip.
///
/// The step at the front of the list is always the current step.
/// We currently assume that you cannot move backward to a previous step.
remaining_steps: Vec<RouteStep>,
/// Remaining waypoints to visit on the route.
///
/// The waypoint at the front of the list is always the *next* waypoint "goal."
/// Unlike the current step, there is no value in tracking the "current" waypoint,
/// as the main use of waypoints is recalculation when the user deviates from the route.
/// (In most use cases, a route will have only two waypoints, but more complex use cases
/// may have multiple intervening points that are visited along the route.)
/// This list is updated as the user advances through the route.
remaining_waypoints: Vec<Waypoint>,
/// The trip progress includes information that is useful for showing the
/// user's progress along the full navigation trip, the route and its components.
progress: TripProgress,
/// The route deviation status: is the user following the route or not?
deviation: RouteDeviation,
/// The visual instruction that should be displayed in the user interface.
visual_instruction: Option<VisualInstruction>,
/// The most recent spoken instruction that should be synthesized using TTS.
///
/// Note it is the responsibility of the platform layer to ensure that utterances are not synthesized multiple times. This property simply reports the current spoken instruction.
spoken_instruction: Option<SpokenInstruction>,
/// Annotation data at the current location.
/// This is represented as a json formatted byte array to allow for flexible encoding of custom annotations.
annotation_json: Option<String>,
},
/// TODO: Maybe break these fields out into a separate state
Navigating(NavigatingTripState),
/// The navigation controller has reached the end of the trip.
Complete,
}

#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(any(feature = "wasm-bindgen", test), derive(Serialize, Deserialize))]
#[cfg_attr(feature = "wasm-bindgen", derive(Tsify))]
#[cfg_attr(feature = "wasm-bindgen", tsify(into_wasm_abi, from_wasm_abi))]
pub struct NavigatingTripState {
// TODO: Probably make these accessor methods
/// The index of the closest coordinate to the user's snapped location.
///
/// This index is relative to the *current* [`RouteStep`]'s geometry.
pub current_step_geometry_index: Option<u64>,
/// A location on the line string that
pub snapped_user_location: UserLocation,
/// The ordered list of steps that remain in the trip.
///
/// The step at the front of the list is always the current step.
/// We currently assume that you cannot move backward to a previous step.
pub remaining_steps: Vec<RouteStep>,
/// Remaining waypoints to visit on the route.
///
/// The waypoint at the front of the list is always the *next* waypoint "goal."
/// Unlike the current step, there is no value in tracking the "current" waypoint,
/// as the main use of waypoints is recalculation when the user deviates from the route.
/// (In most use cases, a route will have only two waypoints, but more complex use cases
/// may have multiple intervening points that are visited along the route.)
/// This list is updated as the user advances through the route.
pub remaining_waypoints: Vec<Waypoint>,
/// The trip progress includes information that is useful for showing the
/// user's progress along the full navigation trip, the route and its components.
pub progress: TripProgress,
/// The route deviation status: is the user following the route or not?
pub deviation: RouteDeviation,
/// The visual instruction that should be displayed in the user interface.
pub visual_instruction: Option<VisualInstruction>,
/// The most recent spoken instruction that should be synthesized using TTS.
///
/// Note it is the responsibility of the platform layer to ensure that utterances are not synthesized multiple times. This property simply reports the current spoken instruction.
pub spoken_instruction: Option<SpokenInstruction>,
/// Annotation data at the current location.
/// This is represented as a json formatted byte array to allow for flexible encoding of custom annotations.
pub annotation_json: Option<String>,
/// The step advance condition(s; this may be a conditional tree effectively).
///
/// This gets replaced with the default value on advancing to a new step.
pub current_condition: Box<dyn StepAdvanceCondition>,
}

#[allow(clippy::large_enum_variant)]
pub enum StepAdvanceStatus {
/// Navigation has advanced, and the information on the next step is embedded.
Expand Down Expand Up @@ -169,14 +182,75 @@ pub enum SpecialAdvanceConditions {
MinimumDistanceFromCurrentStepLine(u16),
}

/// When implementing custom step advance logic, this trait allows you to define
/// whether the condition should advance to the next condition, the next step or not.
// TODO: Figure out how to export the trait with UniFFI or introduce a "wrapper" exportable implementation
// #[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
pub trait StepAdvanceCondition: Default {
/// Immediately advance entirely to the next step regardless of following conditions.
///
/// This is a way to short circuit the condition evaluation and advance to the next step immediately.
fn should_advance_step(&mut self, user_location: &UserLocation, current_step: &NavigatingTripState) -> bool;

/// Creates a fresh instance free of any internal state modifications.
///
/// Uses [Default::default] internally.
fn new_instance(&self) -> Self {
Self::default()
}
}

#[derive(Default)]
struct EntryAndExitCondition {
has_entered: bool,
}

impl StepAdvanceCondition for EntryAndExitCondition {
// FIXME: navigating state
fn should_advance_step(&mut self, user_location: &UserLocation, current_step: &NavigatingTripState) -> bool {
// Do some real check her
if self.has_entered {
true
} else {
self.has_entered = true;
false
}
}
}

#[derive(Default)]
struct OrAdvanceConditions {
conditions: Vec<Box<dyn StepAdvanceCondition>>,
}

impl StepAdvanceCondition for OrAdvanceConditions {
fn should_advance_step(&mut self, user_location: &UserLocation, current_step: &RouteStep) -> bool {
self.conditions
.iter()
.any(|c| c.should_advance_step(user_location, current_step))
}
}

#[derive(Default)]
struct AndAdvanceConditions {
conditions: Vec<Box<dyn StepAdvanceCondition>>,
}

impl StepAdvanceCondition for AndAdvanceConditions {
fn should_advance_step(&mut self, user_location: &UserLocation, current_step: &RouteStep) -> bool {
self.conditions
.iter()
.all(|c| c.should_advance_step(user_location, current_step))
}
}

#[derive(Clone)]
#[cfg_attr(feature = "uniffi", derive(uniffi::Record))]
#[cfg_attr(feature = "wasm-bindgen", derive(Deserialize, Tsify))]
#[cfg_attr(feature = "wasm-bindgen", serde(rename_all = "camelCase"))]
#[cfg_attr(feature = "wasm-bindgen", tsify(from_wasm_abi))]
pub struct NavigationControllerConfig {
/// Configures when navigation advances to the next step in the route.
pub step_advance: StepAdvanceMode,
pub step_advance_condition: Box<dyn StepAdvanceCondition>,
/// Configures when the user is deemed to be off course.
///
/// NOTE: This is distinct from the action that is taken.
Expand Down
Loading