use std::fmt::{Display, Formatter};
use std::sync::Arc;
use parking_lot::RwLock;
use crate::devices::input::{Input, InputEvent};
use crate::devices::Device;
use crate::errors::Error;
use crate::hardware::Hardware;
use crate::io::{IoProtocol, PinIdOrName, PinModeId};
use crate::pause;
use crate::utils::task;
use crate::utils::{EventHandler, EventManager, State, TaskHandler};
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct AnalogInput {
pin: u8,
#[cfg_attr(feature = "serde", serde(with = "crate::devices::arc_rwlock_serde"))]
state: Arc<RwLock<u16>>,
#[cfg_attr(feature = "serde", serde(skip))]
protocol: Box<dyn IoProtocol>,
#[cfg_attr(feature = "serde", serde(skip))]
handler: Arc<RwLock<Option<TaskHandler>>>,
#[cfg_attr(feature = "serde", serde(skip))]
events: EventManager,
}
impl AnalogInput {
pub fn new<T: Into<PinIdOrName>>(board: &dyn Hardware, analog_pin: T) -> Result<Self, Error> {
let pin = board.get_io().read().get_pin(analog_pin)?.clone();
let mut sensor = Self {
pin: pin.id,
state: Arc::new(RwLock::new(pin.value)),
protocol: board.get_protocol(),
handler: Arc::new(RwLock::new(None)),
events: Default::default(),
};
sensor
.protocol
.set_pin_mode(sensor.pin, PinModeId::ANALOG)?;
sensor.protocol.report_analog(pin.channel.unwrap(), true)?;
sensor.attach();
Ok(sensor)
}
pub fn get_pin(&self) -> u8 {
self.pin
}
pub fn attach(&self) {
if self.handler.read().is_none() {
let self_clone = self.clone();
*self.handler.write() = Some(
task::run(async move {
loop {
let pin_value = self_clone
.protocol
.get_io()
.read()
.get_pin(self_clone.pin)?
.value;
let state_value = *self_clone.state.read();
if pin_value != state_value {
*self_clone.state.write() = pin_value;
self_clone.events.emit(InputEvent::OnChange, pin_value);
}
pause!(100);
}
#[allow(unreachable_code)]
Ok(())
})
.unwrap(),
);
}
}
pub fn detach(&self) {
if let Some(handler) = self.handler.read().as_ref() {
handler.abort();
}
*self.handler.write() = None
}
pub fn on<S, F, T, Fut>(&self, event: S, callback: F) -> EventHandler
where
S: Into<String>,
T: 'static + Send + Sync + Clone,
F: FnMut(T) -> Fut + Send + 'static,
Fut: std::future::Future<Output = Result<(), Error>> + Send + 'static,
{
self.events.on(event, callback)
}
}
impl Display for AnalogInput {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"AnalogInput (pin={}) [state={}]",
self.pin,
self.state.read(),
)
}
}
#[cfg_attr(feature = "serde", typetag::serde)]
impl Device for AnalogInput {}
#[cfg_attr(feature = "serde", typetag::serde)]
impl Input for AnalogInput {
fn get_state(&self) -> State {
State::from(*self.state.read())
}
}
#[cfg(test)]
mod tests {
use crate::devices::input::analog::AnalogInput;
use crate::devices::input::Input;
use crate::devices::input::InputEvent;
use crate::hardware::Board;
use crate::mocks::plugin_io::MockIoProtocol;
use crate::pause;
use std::sync::atomic::{AtomicU16, Ordering};
use std::sync::Arc;
#[hermes_five_macros::test]
fn test_new_analog_input() {
let board = Board::new(MockIoProtocol::default());
let sensor = AnalogInput::new(&board, 14);
assert!(sensor.is_ok());
let sensor = sensor.unwrap();
assert_eq!(sensor.get_pin(), 14);
assert_eq!(sensor.get_state().as_integer(), 100);
sensor.detach();
let sensor = AnalogInput::new(&board, "A22").unwrap();
assert_eq!(sensor.get_pin(), 22);
assert_eq!(sensor.get_state().as_integer(), 222);
sensor.detach();
board.close();
}
#[hermes_five_macros::test]
fn test_analog_display() {
let board = Board::new(MockIoProtocol::default());
let sensor = AnalogInput::new(&board, "A15").unwrap();
assert_eq!(sensor.get_state().as_integer(), 200);
assert_eq!(
format!("{}", sensor),
String::from("AnalogInput (pin=15) [state=200]")
);
sensor.detach();
board.close();
}
#[hermes_five_macros::test]
fn test_analog_events() {
let pin = "A14";
let board = Board::new(MockIoProtocol::default());
let sensor = AnalogInput::new(&board, pin).unwrap();
assert_eq!(sensor.get_state().as_integer(), 100);
let change_flag = Arc::new(AtomicU16::new(100));
let moved_change_flag = change_flag.clone();
sensor.on(InputEvent::OnChange, move |new_state: u16| {
let captured_flag = moved_change_flag.clone();
async move {
captured_flag.store(new_state, Ordering::SeqCst);
Ok(())
}
});
assert_eq!(change_flag.load(Ordering::SeqCst), 100);
sensor
.protocol
.get_io()
.write()
.get_pin_mut(pin)
.unwrap()
.value = 0xFF;
pause!(500);
assert_eq!(change_flag.load(Ordering::SeqCst), 0xFF);
sensor.detach();
}
}