diff --git a/Cargo.lock b/Cargo.lock index 254f13e..bfbaa09 100644 Binary files a/Cargo.lock and b/Cargo.lock differ diff --git a/src/app_state.rs b/src/app_state.rs index ec9e262..c47cd9b 100644 --- a/src/app_state.rs +++ b/src/app_state.rs @@ -3,7 +3,6 @@ use std::time::{Duration, Instant}; use actix::{Actor, Context, Handler, Message}; use emgauwa_common::constants; -use emgauwa_common::drivers::RelayDriver; use emgauwa_common::errors::EmgauwaError; use emgauwa_common::models::Controller; use emgauwa_common::types::RelayStates; @@ -11,6 +10,7 @@ use futures::executor::block_on; use sqlx::{Pool, Sqlite}; use tokio::sync::Notify; +use crate::drivers::RelayDriver; use crate::settings::Settings; #[derive(Message)] diff --git a/src/drivers/gpio.rs b/src/drivers/gpio.rs new file mode 100644 index 0000000..0507dc3 --- /dev/null +++ b/src/drivers/gpio.rs @@ -0,0 +1,31 @@ +use rppal::gpio::{Gpio, OutputPin}; + +use crate::drivers::RelayDriver; +use crate::errors::EmgauwaControllerError; + +pub struct GpioDriver { + pub gpio: OutputPin, + pub inverted: bool, +} + +impl GpioDriver { + pub fn new(pin: u8, inverted: bool) -> Result { + let gpio = Gpio::new()?.get(pin)?.into_output(); + Ok(Self { gpio, inverted }) + } +} + +impl RelayDriver for GpioDriver { + fn set(&mut self, value: bool) -> Result<(), EmgauwaControllerError> { + if self.get_high(value) { + self.gpio.set_high(); + } else { + self.gpio.set_low(); + } + Ok(()) + } + + fn get_inverted(&self) -> bool { + self.inverted + } +} diff --git a/src/driver.rs b/src/drivers/mod.rs similarity index 56% rename from src/driver.rs rename to src/drivers/mod.rs index 9df8d31..9c0c971 100644 --- a/src/driver.rs +++ b/src/drivers/mod.rs @@ -1,4 +1,21 @@ +pub use gpio::GpioDriver; +pub use null::NullDriver; +pub use piface::PiFaceDriver; use serde::{Deserialize, Deserializer}; +use crate::errors::EmgauwaControllerError; + +mod gpio; +mod null; +mod piface; + +pub trait RelayDriver { + fn get_high(&self, value: bool) -> bool { + value ^ self.get_inverted() + } + + fn set(&mut self, value: bool) -> Result<(), EmgauwaControllerError>; + fn get_inverted(&self) -> bool; +} #[derive(Debug, Clone, Copy)] pub enum Driver { diff --git a/src/drivers/null.rs b/src/drivers/null.rs new file mode 100644 index 0000000..1e8e2c2 --- /dev/null +++ b/src/drivers/null.rs @@ -0,0 +1,22 @@ +use crate::drivers::RelayDriver; +use crate::errors::EmgauwaControllerError; + +pub struct NullDriver { + pub pin: u8, +} + +impl NullDriver { + pub fn new(pin: u8) -> Self { + Self { pin } + } +} + +impl RelayDriver for NullDriver { + fn set(&mut self, _value: bool) -> Result<(), EmgauwaControllerError> { + Ok(()) + } + + fn get_inverted(&self) -> bool { + false + } +} diff --git a/src/drivers/piface.rs b/src/drivers/piface.rs new file mode 100644 index 0000000..4a0baf0 --- /dev/null +++ b/src/drivers/piface.rs @@ -0,0 +1,48 @@ +use rppal_pfd::{ + ChipSelect, HardwareAddress, OutputPin, PiFaceDigital, PiFaceDigitalError, SpiBus, SpiMode, +}; + +use crate::drivers::RelayDriver; +use crate::errors::EmgauwaControllerError; + +pub struct PiFaceDriver { + pub pfd_pin: OutputPin, +} + +impl PiFaceDriver { + pub fn new(pin: u8, pfd: &Option) -> Result { + let pfd = pfd.as_ref().ok_or(EmgauwaControllerError::Hardware(String::from( + "PiFaceDigital not initialized", + )))?; + let pfd_pin = pfd.get_output_pin(pin)?; + Ok(Self { pfd_pin }) + } + + pub fn init_piface() -> Result { + let mut pfd = PiFaceDigital::new( + HardwareAddress::new(0)?, + SpiBus::Spi0, + ChipSelect::Cs0, + 100_000, + SpiMode::Mode0, + )?; + pfd.init()?; + + Ok(pfd) + } +} + +impl RelayDriver for PiFaceDriver { + fn set(&mut self, value: bool) -> Result<(), EmgauwaControllerError> { + if self.get_high(value) { + self.pfd_pin.set_high().map_err(PiFaceDigitalError::from)?; + } else { + self.pfd_pin.set_low().map_err(PiFaceDigitalError::from)?; + } + Ok(()) + } + + fn get_inverted(&self) -> bool { + false + } +} diff --git a/src/errors.rs b/src/errors.rs new file mode 100644 index 0000000..3916180 --- /dev/null +++ b/src/errors.rs @@ -0,0 +1,50 @@ +use std::fmt::{Display, Formatter}; +use rppal::gpio; +use rppal_mcp23s17::Mcp23s17Error; +use rppal_pfd::PiFaceDigitalError; + +use emgauwa_common::errors::EmgauwaError; + +#[derive(Debug)] +pub enum EmgauwaControllerError { + Hardware(String), +} + +impl Display for EmgauwaControllerError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", EmgauwaError::from(self)) + } +} + +impl From for EmgauwaControllerError { + fn from(value: gpio::Error) -> Self { + Self::Hardware(value.to_string()) + } +} + +impl From for EmgauwaControllerError { + fn from(value: PiFaceDigitalError) -> Self { + match value { + PiFaceDigitalError::Mcp23s17Error { source } => match source { + Mcp23s17Error::SpiError { source } => Self::Hardware(source.to_string()), + _ => Self::Hardware(source.to_string()), + }, + PiFaceDigitalError::GpioError { source } => Self::Hardware(source.to_string()), + _ => Self::Hardware(value.to_string()), + } + } +} + +impl From<&EmgauwaControllerError> for EmgauwaError { + fn from(value: &EmgauwaControllerError) -> Self { + match value { + EmgauwaControllerError::Hardware(value) => EmgauwaError::Hardware(value.to_string()), + } + } +} + +impl From for EmgauwaError { + fn from(value: EmgauwaControllerError) -> Self { + EmgauwaError::from(&value) + } +} diff --git a/src/main.rs b/src/main.rs index 1ba2d42..b49a92b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,11 +14,12 @@ use crate::settings::Settings; use crate::ws::run_ws_loop; mod app_state; -mod driver; +mod drivers; mod relay_loop; mod settings; mod utils; mod ws; +mod errors; async fn create_this_controller( conn: &mut PoolConnection, @@ -42,9 +43,7 @@ async fn create_this_relay( let relay = DbRelay::create( conn, &settings_relay.name, - settings_relay.number.ok_or(EmgauwaError::Internal( - "Relay number is missing".to_string(), - ))?, + settings_relay.number, this_controller, ) .await?; @@ -87,9 +86,7 @@ async fn main() -> Result<(), std::io::Error> { if DbRelay::get_by_controller_and_num( &mut conn, &db_controller, - relay.number.ok_or(EmgauwaError::Internal( - "Relay number is missing".to_string(), - ))?, + relay.number, ) .await .map_err(EmgauwaError::from)? diff --git a/src/settings.rs b/src/settings.rs index fa414f2..445f531 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -1,15 +1,14 @@ use emgauwa_common::errors::EmgauwaError; -use emgauwa_common::{drivers, settings}; +use emgauwa_common::settings; use rppal_pfd::PiFaceDigital; use serde_derive::Deserialize; - -use crate::driver::Driver; +use crate::drivers; #[derive(Clone, Debug, Deserialize)] #[serde(default)] #[allow(unused)] pub struct Relay { - pub driver: Driver, + pub driver: drivers::Driver, pub name: String, pub number: i64, pub pin: u8, @@ -47,8 +46,8 @@ impl Default for Settings { impl Default for Relay { fn default() -> Self { Relay { - driver: Driver::Gpio, - number: None, + driver: drivers::Driver::Gpio, + number: 0, name: String::from("Relay"), pin: 0, inverted: false, @@ -84,9 +83,9 @@ impl Relay { pfd: &mut Option, ) -> Result, EmgauwaError> { let driver: Box = match self.driver { - Driver::Null => Box::new(drivers::NullDriver::new(self.pin)), - Driver::Gpio => Box::new(drivers::GpioDriver::new(self.pin, self.inverted)?), - Driver::PiFace => { + drivers::Driver::Null => Box::new(drivers::NullDriver::new(self.pin)), + drivers::Driver::Gpio => Box::new(drivers::GpioDriver::new(self.pin, self.inverted)?), + drivers::Driver::PiFace => { if pfd.is_none() { *pfd = Some(drivers::PiFaceDriver::init_piface()?); }