use std::{
future::Future,
pin::Pin,
task::{Context, Poll},
time::{Duration, Instant},
};
use ozton_control::{
Tolerances,
loops::{AngularPid, Feedback, Pid},
};
use ozton_drivetrain::{Drivetrain, model::Arcade};
use ozton_tracking::{TracksForwardTravel, TracksHeading, TracksVelocity};
use vexide::{
math::Angle,
time::{Sleep, sleep},
};
pub(crate) struct DriveState {
pub sleep: Sleep,
pub initial_forward_travel: f64,
pub start_time: Instant,
pub prev_time: Instant,
pub linear_settled: bool,
pub angular_settled: bool,
}
#[must_use = "futures do nothing unless you `.await` or poll them"]
pub struct DriveFuture<'a, M, L, A, T>
where
M: Arcade,
L: Feedback<State = f64, Signal = f64> + Unpin,
A: Feedback<State = Angle, Signal = f64> + Unpin,
T: TracksForwardTravel + TracksHeading + TracksVelocity,
{
pub(crate) target_distance: f64,
pub(crate) target_heading: Angle,
pub(crate) timeout: Option<Duration>,
pub(crate) linear_tolerances: Tolerances,
pub(crate) angular_tolerances: Tolerances,
pub(crate) linear_controller: L,
pub(crate) angular_controller: A,
pub(crate) drivetrain: &'a mut Drivetrain<M, T>,
pub(crate) state: Option<DriveState>,
}
impl<M, L, A, T> Future for DriveFuture<'_, M, L, A, T>
where
M: Arcade,
L: Feedback<State = f64, Signal = f64> + Unpin,
A: Feedback<State = Angle, Signal = f64> + Unpin,
T: TracksForwardTravel + TracksHeading + TracksVelocity,
{
type Output = ();
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
let state = this.state.get_or_insert_with(|| {
let now = Instant::now();
DriveState {
sleep: sleep(Duration::from_millis(5)),
initial_forward_travel: this.drivetrain.tracking.forward_travel(),
start_time: now,
prev_time: now,
linear_settled: false,
angular_settled: false,
}
});
if Pin::new(&mut state.sleep).poll(cx).is_pending() {
return Poll::Pending;
}
let dt = state.prev_time.elapsed();
let forward_travel = this.drivetrain.tracking.forward_travel();
let heading = this.drivetrain.tracking.heading();
let linear_error = (this.target_distance + state.initial_forward_travel) - forward_travel;
let angular_error = (this.target_heading - heading).wrapped_half();
if this
.linear_tolerances
.check(linear_error, this.drivetrain.tracking.linear_velocity())
{
state.linear_settled = true;
}
if this.angular_tolerances.check(
angular_error.as_radians(),
this.drivetrain.tracking.angular_velocity(),
) {
state.angular_settled = true;
}
if (state.linear_settled && state.angular_settled)
|| this
.timeout
.is_some_and(|timeout| state.start_time.elapsed() > timeout)
{
drop(this.drivetrain.model.drive_arcade(0.0, 0.0));
return Poll::Ready(());
}
let linear_output = this.linear_controller.update(
forward_travel,
this.target_distance + state.initial_forward_travel,
dt,
);
let angular_output = this
.angular_controller
.update(heading, this.target_heading, dt);
drop(
this.drivetrain
.model
.drive_arcade(linear_output, angular_output),
);
state.sleep = sleep(Duration::from_millis(5));
state.prev_time = Instant::now();
cx.waker().wake_by_ref();
Poll::Pending
}
}
impl<M, L, A, T> DriveFuture<'_, M, L, A, T>
where
M: Arcade,
L: Feedback<State = f64, Signal = f64> + Unpin,
A: Feedback<State = Angle, Signal = f64> + Unpin,
T: TracksForwardTravel + TracksHeading + TracksVelocity,
{
pub fn with_linear_controller(&mut self, controller: L) -> &mut Self {
self.linear_controller = controller;
self
}
pub fn with_angular_controller(&mut self, controller: A) -> &mut Self {
self.angular_controller = controller;
self
}
pub const fn with_timeout(&mut self, timeout: Duration) -> &mut Self {
self.timeout = Some(timeout);
self
}
pub const fn without_timeout(&mut self) -> &mut Self {
self.timeout = None;
self
}
pub const fn with_linear_tolerances(&mut self, tolerances: Tolerances) -> &mut Self {
self.linear_tolerances = tolerances;
self
}
pub const fn with_linear_error_tolerance(&mut self, tolerance: f64) -> &mut Self {
self.linear_tolerances.error_tolerance = Some(tolerance);
self
}
pub const fn without_linear_error_tolerance(&mut self) -> &mut Self {
self.linear_tolerances.error_tolerance = None;
self
}
pub const fn with_linear_velocity_tolerance(&mut self, tolerance: f64) -> &mut Self {
self.linear_tolerances.velocity_tolerance = Some(tolerance);
self
}
pub const fn without_linear_velocity_tolerance(&mut self) -> &mut Self {
self.linear_tolerances.velocity_tolerance = None;
self
}
pub const fn with_linear_tolerance_duration(&mut self, duration: Duration) -> &mut Self {
self.linear_tolerances.duration = Some(duration);
self
}
pub const fn without_linear_tolerance_duration(&mut self) -> &mut Self {
self.linear_tolerances.duration = None;
self
}
pub const fn without_tolerance_duration(&mut self) -> &mut Self {
self.linear_tolerances.duration = None;
self.angular_tolerances.duration = None;
self
}
pub const fn with_angular_tolerances(&mut self, tolerances: Tolerances) -> &mut Self {
self.angular_tolerances = tolerances;
self
}
pub const fn with_angular_error_tolerance(&mut self, tolerance: f64) -> &mut Self {
self.angular_tolerances.error_tolerance = Some(tolerance);
self
}
pub const fn without_angular_error_tolerance(&mut self) -> &mut Self {
self.angular_tolerances.error_tolerance = None;
self
}
pub const fn with_angular_velocity_tolerance(&mut self, tolerance: f64) -> &mut Self {
self.angular_tolerances.velocity_tolerance = Some(tolerance);
self
}
pub const fn without_angular_velocity_tolerance(&mut self) -> &mut Self {
self.angular_tolerances.velocity_tolerance = None;
self
}
pub const fn with_angular_tolerance_duration(&mut self, duration: Duration) -> &mut Self {
self.angular_tolerances.duration = Some(duration);
self
}
pub const fn without_angular_tolerance_duration(&mut self) -> &mut Self {
self.angular_tolerances.duration = None;
self
}
}
impl<M, A, T> DriveFuture<'_, M, Pid, A, T>
where
M: Arcade,
A: Feedback<State = Angle, Signal = f64> + Unpin,
T: TracksForwardTravel + TracksHeading + TracksVelocity,
{
pub const fn with_linear_gains(&mut self, kp: f64, ki: f64, kd: f64) -> &mut Self {
self.linear_controller.set_gains(kp, ki, kd);
self
}
pub const fn with_linear_kp(&mut self, kp: f64) -> &mut Self {
self.linear_controller.set_kp(kp);
self
}
pub const fn with_linear_ki(&mut self, ki: f64) -> &mut Self {
self.linear_controller.set_ki(ki);
self
}
pub const fn with_linear_kd(&mut self, kd: f64) -> &mut Self {
self.linear_controller.set_kd(kd);
self
}
pub const fn with_linear_integration_range(&mut self, integration_range: f64) -> &mut Self {
self.linear_controller
.set_integration_range(Some(integration_range));
self
}
pub const fn without_linear_integration_range(&mut self) -> &mut Self {
self.linear_controller.set_integration_range(None);
self
}
pub const fn with_linear_output_limit(&mut self, limit: f64) -> &mut Self {
self.linear_controller.set_output_limit(Some(limit));
self
}
pub const fn without_linear_output_limit(&mut self) -> &mut Self {
self.linear_controller.set_output_limit(None);
self
}
}
impl<M, L, T> DriveFuture<'_, M, L, AngularPid, T>
where
M: Arcade,
L: Feedback<State = f64, Signal = f64> + Unpin,
T: TracksForwardTravel + TracksHeading + TracksVelocity,
{
pub const fn with_angular_gains(&mut self, kp: f64, ki: f64, kd: f64) -> &mut Self {
self.angular_controller.set_gains(kp, ki, kd);
self
}
pub const fn with_angular_kp(&mut self, kp: f64) -> &mut Self {
self.angular_controller.set_kp(kp);
self
}
pub const fn with_angular_ki(&mut self, ki: f64) -> &mut Self {
self.angular_controller.set_ki(ki);
self
}
pub const fn with_angular_kd(&mut self, kd: f64) -> &mut Self {
self.angular_controller.set_kd(kd);
self
}
pub const fn with_angular_integration_range(&mut self, integration_range: Angle) -> &mut Self {
self.angular_controller
.set_integration_range(Some(integration_range));
self
}
pub const fn with_angular_output_limit(&mut self, limit: f64) -> &mut Self {
self.angular_controller.set_output_limit(Some(limit));
self
}
pub const fn without_angular_integration_range(&mut self) -> &mut Self {
self.angular_controller.set_integration_range(None);
self
}
pub const fn without_angular_output_limit(&mut self) -> &mut Self {
self.angular_controller.set_output_limit(None);
self
}
}