- use core::usize;
-
- use embassy_futures::select::{select, select_slice, Either};
- use embassy_time::{Duration, Timer};
- use esp_hal::gpio::{AnyPin, Output, PushPull};
-
- use crate::{
- error::ErrorExt,
- state::{ValveEvent, VALVE_EVENT_CHANNEL},
- utils::OptFuture,
- };
-
- pub struct ValveControl<const SIZE: usize> {
- pins: [AnyPin<Output<PushPull>>; SIZE],
- deadlines: [OptFuture<Timer>; SIZE],
- }
-
- impl<const SIZE: usize> ValveControl<SIZE> {
- pub fn new(mut pins: [AnyPin<Output<PushPull>>; SIZE]) -> Self {
- for pin in &mut pins {
- pin.set_high();
- }
-
- let deadlines = [(); SIZE].map(|_| OptFuture::default());
- Self { pins, deadlines }
- }
-
- pub fn control(&mut self, id: u8) -> Option<Valve<'_>> {
- let pin = self.pins.get_mut(id as usize)?;
- let deadline = self.deadlines.get_mut(id as usize)?;
-
- Some(Valve { id, pin, deadline })
- }
-
- async fn wait_for_next_close(&mut self) -> Valve<'_> {
- let ((), index) = select_slice(&mut self.deadlines).await;
- self.deadlines[index] = OptFuture::default();
-
- Valve {
- id: index as u8,
- pin: &mut self.pins[index],
- deadline: &mut self.deadlines[index],
- }
- }
- }
-
- pub struct Valve<'a> {
- pub id: u8,
- pin: &'a mut AnyPin<Output<PushPull>>,
- deadline: &'a mut OptFuture<Timer>,
- }
-
- impl<'a> Valve<'a> {
- pub fn open(&mut self, duration: Duration) {
- log::info!("opening valve {} for {} secs", self.id, duration.as_secs());
- *self.deadline = Timer::after(duration).into();
- self.pin.set_low();
- }
-
- pub fn close(&mut self) {
- log::info!("closing valve {}", self.id);
- self.pin.set_high();
- *self.deadline = OptFuture::default();
- }
- }
-
- #[embassy_executor::task]
- pub async fn task(mut valves: ValveControl<4>) -> ! {
- let mut subscriber = VALVE_EVENT_CHANNEL.subscriber().log_unwrap();
-
- macro_rules! try_get_valve {
- ($id:expr) => {
- match valves.control($id) {
- Some(valve) => valve,
- None => {
- log::warn!("tried to control invalid pin {}", $id);
- continue;
- }
- }
- };
- }
-
- loop {
- match select(valves.wait_for_next_close(), subscriber.next_message_pure()).await {
- Either::First(mut valve) => {
- log::info!("deadline reached for valve {}", valve.id);
- valve.close()
- }
- Either::Second(ValveEvent::Open { id, duration }) => {
- let mut valve = try_get_valve!(id);
- valve.open(duration);
- }
- Either::Second(ValveEvent::Close(id)) => {
- let mut valve = try_get_valve!(id);
- valve.close();
- }
- }
- }
- }