robokit 0.3.0

Build custom firmware for simple robotic automation
Documentation
use core::fmt::Debug;
use core::task::Poll;
use defmt::Format;
use heapless::Deque;

use crate::actuators::{
    axis::AxisAction, led::LedAction, spindle::SpindleAction, Actuator, ActuatorSet,
};

#[derive(Clone, Copy, Debug, Format)]
pub enum Command<const LED_TIMER_HZ: u32, LedId, AxisId, SpindleId>
where
    LedId: Debug + Format,
    AxisId: Debug + Format,
    SpindleId: Debug + Format,
{
    Led(LedId, LedAction<LED_TIMER_HZ>),
    Axis(AxisId, AxisAction),
    Spindle(SpindleId, SpindleAction),
}

pub struct Runner<
    const LED_TIMER_HZ: u32,
    const ACTIVE_COMMMANDS_COUNT: usize,
    LedSet,
    AxisSet,
    SpindleSet,
> where
    LedSet: ActuatorSet<Action = LedAction<LED_TIMER_HZ>>,
    AxisSet: ActuatorSet<Action = AxisAction>,
    SpindleSet: ActuatorSet<Action = SpindleAction>,
{
    active_commands: Deque<
        Command<LED_TIMER_HZ, LedSet::Id, AxisSet::Id, SpindleSet::Id>,
        ACTIVE_COMMMANDS_COUNT,
    >,
    leds: LedSet,
    axes: AxisSet,
    spindles: SpindleSet,
}

impl<const LED_TIMER_HZ: u32, const ACTIVE_COMMMANDS_COUNT: usize, LedSet, AxisSet, SpindleSet>
    Runner<LED_TIMER_HZ, ACTIVE_COMMMANDS_COUNT, LedSet, AxisSet, SpindleSet>
where
    LedSet: ActuatorSet<Action = LedAction<LED_TIMER_HZ>>,
    AxisSet: ActuatorSet<Action = AxisAction>,
    SpindleSet: ActuatorSet<Action = SpindleAction>,
{
    pub fn new(leds: LedSet, axes: AxisSet, spindles: SpindleSet) -> Self {
        Self {
            active_commands: Deque::new(),
            leds,
            axes,
            spindles,
        }
    }
}

#[derive(Clone, Copy, Debug, Format)]
pub enum RunnerAction<Command> {
    Run(Command),
    Reset,
}

#[derive(Clone, Copy, Debug)]
pub enum RunnerError<LedId, LedSetError, AxisId, AxisSetError, SpindleId, SpindleSetError>
where
    LedId: Debug + Format,
    LedSetError: Debug,
    AxisId: Debug + Format,
    AxisSetError: Debug,
    SpindleId: Debug + Format,
    SpindleSetError: Debug,
{
    Led(LedId, LedSetError),
    Axis(AxisId, AxisSetError),
    Spindle(SpindleId, SpindleSetError),
}

impl<const LED_TIMER_HZ: u32, const ACTIVE_COMMMANDS_COUNT: usize, LedSet, AxisSet, SpindleSet>
    Actuator for Runner<LED_TIMER_HZ, ACTIVE_COMMMANDS_COUNT, LedSet, AxisSet, SpindleSet>
where
    LedSet: ActuatorSet<Action = LedAction<LED_TIMER_HZ>>,
    AxisSet: ActuatorSet<Action = AxisAction>,
    SpindleSet: ActuatorSet<Action = SpindleAction>,
{
    type Action = RunnerAction<Command<LED_TIMER_HZ, LedSet::Id, AxisSet::Id, SpindleSet::Id>>;
    type Error = RunnerError<
        LedSet::Id,
        LedSet::Error,
        AxisSet::Id,
        AxisSet::Error,
        SpindleSet::Id,
        SpindleSet::Error,
    >;

    fn run(
        &mut self,
        action: &RunnerAction<Command<LED_TIMER_HZ, LedSet::Id, AxisSet::Id, SpindleSet::Id>>,
    ) {
        match action {
            RunnerAction::Run(command) => {
                match command {
                    Command::Led(id, action) => self.leds.run(id, action),
                    Command::Axis(id, action) => self.axes.run(id, action),
                    Command::Spindle(id, action) => self.spindles.run(id, action),
                }

                self.active_commands.push_back(*command).unwrap();
            }
            RunnerAction::Reset => self.active_commands.clear(),
        }
    }

    fn poll(&mut self) -> Poll<Result<(), Self::Error>> {
        let num_commands = self.active_commands.len();
        for _command_index in 0..num_commands {
            let command = self.active_commands.pop_front().unwrap();
            let result = match command {
                Command::Led(id, _) => self.leds.poll(&id).map_err(|err| RunnerError::Led(id, err)),
                Command::Axis(id, _) => self
                    .axes
                    .poll(&id)
                    .map_err(|err| RunnerError::Axis(id, err)),
                Command::Spindle(id, _) => self
                    .spindles
                    .poll(&id)
                    .map_err(|err| RunnerError::Spindle(id, err)),
            };

            match result {
                Poll::Ready(Ok(())) => {}
                Poll::Ready(Err(err)) => {
                    self.active_commands.push_back(command).unwrap();

                    return Poll::Ready(Err(err.into()));
                }
                Poll::Pending => {
                    self.active_commands.push_back(command).unwrap();
                }
            }
        }

        if self.active_commands.len() == 0 {
            Poll::Ready(Ok(()))
        } else {
            Poll::Pending
        }
    }
}