use anyhow::Result;
use std::collections::HashSet;
use esp32_simple::{
ble::Advertiser,
clock::Timer,
color::{Rgb, GREEN, RED},
infra::{self, Switch},
light::Led,
message::Dispatcher,
trigger_enum,
};
macro_rules! func {
() => {{
fn f() {}
fn type_name_of<T>(_: T) -> &'static str {
std::any::type_name::<T>()
}
let name = type_name_of(f);
match &name[..name.len() - 3].rfind(':') {
Some(pos) => &name[pos + 1..name.len() - 3],
None => &name[..name.len() - 3],
}
}};
}
pub(crate) use func;
macro_rules! trace_func {
() => {
log::debug!("{}", $crate::common::logic::func!())
};
}
pub(crate) use trace_func;
trigger_enum! {
#[derive(Debug, Eq, Hash, PartialEq)]
pub enum Trigger {
ButtonPressed = 1 << 0,
TimerTicked = 1 << 1,
DeviceFoundActive = 1 << 2,
DeviceFoundInactive = 1 << 3,
DeviceNotFound = 1 << 4,
GpsDataAvailable = 1 << 5,
}
}
#[derive(PartialEq)]
pub enum DeviceNearby {
Active,
Inactive,
}
pub type State = infra::State<DeviceNearby>;
pub trait StateExt {
fn to_str(&self) -> &'static str;
fn to_color(&self) -> Rgb;
}
impl StateExt for State {
fn to_str(&self) -> &'static str {
match self {
State::Off => "Off",
State::On(None) => "On",
State::On(Some(DeviceNearby::Active)) => "ActiveDeviceNearby",
State::On(Some(DeviceNearby::Inactive)) => "InactiveDeviceNearby",
}
}
fn to_color(&self) -> Rgb {
match self {
State::On(None | Some(DeviceNearby::Active)) => GREEN,
State::Off | State::On(Some(DeviceNearby::Inactive)) => RED,
}
}
}
pub struct Core<'a> {
pub state: State,
pub dispatcher: Dispatcher<Trigger>,
pub advertiser: Advertiser,
pub led: Led<'a>,
pub timer: Timer<'a, Trigger>,
}
impl<'a> Core<'a> {
pub fn new(
state: State,
dispatcher: Dispatcher<Trigger>,
advertiser: Advertiser,
mut led: Led<'a>,
timer: Timer<'a, Trigger>,
) -> Result<Self> {
led.set_color(state.to_color())?;
led.on()?;
Ok(Self {
state,
dispatcher,
advertiser,
led,
timer,
})
}
pub fn handle_timer_ticked(&mut self) -> Result<()> {
trace_func!();
match self.state {
State::On(Some(_)) => self.led.toggle(),
_ => Ok(()),
}
}
pub fn handle_device_found_inactive(&mut self) {
trace_func!();
if self.state.is_on() {
self.state = State::On(Some(DeviceNearby::Inactive));
}
}
pub fn handle_device_not_found(&mut self) {
trace_func!();
if self.state.is_on() {
self.state = State::on();
}
}
pub fn handle_common_triggers(
&mut self,
triggers: &HashSet<&'static Trigger>,
on_button_pressed: impl FnOnce(&mut Self) -> Result<()>,
on_device_found_active: impl FnOnce(&mut Self) -> Result<()>,
) -> Result<bool> {
log::debug!(
"{}: triggers: {:?}, state: {}",
func!(),
triggers,
self.state.to_str()
);
let mut handled = true;
if triggers.contains(&Trigger::ButtonPressed) {
on_button_pressed(self)?;
} else if triggers.contains(&Trigger::DeviceFoundActive) {
on_device_found_active(self)?;
} else if triggers.contains(&Trigger::DeviceFoundInactive) {
self.handle_device_found_inactive();
} else if triggers.contains(&Trigger::DeviceNotFound) {
self.handle_device_not_found();
} else if triggers.contains(&Trigger::TimerTicked) {
self.handle_timer_ticked()?;
} else {
handled = false;
}
Ok(handled)
}
pub fn update_led(&mut self) -> Result<()> {
self.led.set_color(self.state.to_color())?;
if matches!(self.state, State::On(None) | State::Off) {
self.timer.off()?;
self.led.on()?;
} else {
self.timer.on()?;
}
Ok(())
}
pub fn run<F>(&mut self, mut handle_triggers: F) -> Result<()>
where
F: FnMut(&mut Self, &HashSet<&'static Trigger>) -> Result<()>,
{
loop {
let triggers = self.dispatcher.collect()?;
handle_triggers(self, &triggers)?;
self.update_led()?;
}
}
}