pub mod analog;
pub mod digital;
use std::fmt::Display;
#[allow(clippy::upper_case_acronyms, missing_docs)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum GPIOPortType {
Analog,
Digital,
PWM,
}
impl Display for GPIOPortType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Analog => write!(f, "Analog Port"),
Self::Digital => write!(f, "Digital Port"),
Self::PWM => write!(f, "PWM Port"),
}
}
}
#[allow(variant_size_differences, missing_docs)]
#[non_exhaustive]
#[derive(Debug, thiserror::Error, Clone, Copy, PartialEq)]
pub enum GPIOError {
#[error("GPIO port {0} is not available")]
PortNotAvailable(u8),
#[error("Value {0} is out of range for {1} port {2}")]
ValueOutOfRange(Volt, GPIOPortType, u8),
#[error("Wrong mode for {0} port {1}, expected output to be {2}")]
WrongMode(GPIOPortType, u8, bool),
#[error("GPIO port {0} is already in use")]
PortInUse(u8),
}
pub trait Channel {
fn channel_id(&self) -> u8;
}
use analog::{AnalogInput, AnalogOutput};
use digital::{DigitalInput, DigitalOutput};
use crate::units::energy::Volt;
use super::NotSimError;
pub trait GPIODriver {
const ANALOG_PORTS: &'static [u8];
const DIGITAL_PORTS: &'static [u8];
const PWM_PORTS: &'static [u8];
const RELAY_PORTS: &'static [u8];
fn new_analog_input(port: u8) -> Result<AnalogInput, GPIOError>;
fn new_analog_output(port: u8) -> Result<AnalogOutput, GPIOError>;
fn new_digital_input(port: u8) -> Result<DigitalInput, GPIOError>;
fn new_digital_output(port: u8) -> Result<DigitalOutput, GPIOError>;
#[allow(clippy::missing_errors_doc)]
fn analog_available(port: u8) -> Result<(), GPIOError> {
if Self::ANALOG_PORTS.contains(&port) {
Ok(())
} else {
Err(GPIOError::PortNotAvailable(port))
}
}
#[allow(clippy::missing_errors_doc)]
fn digital_available(port: u8) -> Result<(), GPIOError> {
if Self::DIGITAL_PORTS.contains(&port) {
Ok(())
} else {
Err(GPIOError::PortNotAvailable(port))
}
}
#[allow(clippy::missing_errors_doc)]
fn pwm_available(port: u8) -> Result<(), GPIOError> {
if Self::PWM_PORTS.contains(&port) {
Ok(())
} else {
Err(GPIOError::PortNotAvailable(port))
}
}
}
pub trait SimGPIODriver: GPIODriver {
fn sim_analog_input(port: u8) -> AnalogOutput;
fn sim_analog_output(port: u8) -> AnalogInput;
fn sim_digital_input(port: u8) -> DigitalOutput;
fn sim_digital_output(port: u8) -> DigitalInput;
}
#[derive(Debug, Clone, Copy)]
pub struct GPIOVTable {
pub(crate) new_analog_input: fn(u8) -> Result<AnalogInput, GPIOError>,
pub(crate) new_analog_output: fn(u8) -> Result<AnalogOutput, GPIOError>,
pub(crate) new_digital_input: fn(u8) -> Result<DigitalInput, GPIOError>,
pub(crate) new_digital_output: fn(u8) -> Result<DigitalOutput, GPIOError>,
pub(crate) sim_analog_input: Option<fn(u8) -> AnalogOutput>,
pub(crate) sim_analog_output: Option<fn(u8) -> AnalogInput>,
pub(crate) sim_digital_input: Option<fn(u8) -> DigitalOutput>,
pub(crate) sim_digital_output: Option<fn(u8) -> DigitalInput>,
}
impl GPIOVTable {
pub(crate) fn from_driver<T: GPIODriver>() -> Self {
assert!(
std::mem::size_of::<T>() == 0,
"GPIO Driver must be zero sized"
);
Self {
new_analog_input: T::new_analog_input,
new_analog_output: T::new_analog_output,
new_digital_input: T::new_digital_input,
new_digital_output: T::new_digital_output,
sim_analog_input: None,
sim_analog_output: None,
sim_digital_input: None,
sim_digital_output: None,
}
}
pub(crate) fn from_sim_driver<T: SimGPIODriver>() -> Self {
assert!(
std::mem::size_of::<T>() == 0,
"GPIO Driver must be zero sized"
);
Self {
new_analog_input: T::new_analog_input,
new_analog_output: T::new_analog_output,
new_digital_input: T::new_digital_input,
new_digital_output: T::new_digital_output,
sim_analog_input: Some(T::sim_analog_input),
sim_analog_output: Some(T::sim_analog_output),
sim_digital_input: Some(T::sim_digital_input),
sim_digital_output: Some(T::sim_digital_output),
}
}
pub fn new_analog_input(&self, port: u8) -> Result<AnalogInput, GPIOError> {
(self.new_analog_input)(port)
}
pub fn new_analog_output(&self, port: u8) -> Result<AnalogOutput, GPIOError> {
(self.new_analog_output)(port)
}
pub fn new_digital_input(&self, port: u8) -> Result<DigitalInput, GPIOError> {
(self.new_digital_input)(port)
}
pub fn new_digital_output(&self, port: u8) -> Result<DigitalOutput, GPIOError> {
(self.new_digital_output)(port)
}
#[allow(clippy::missing_errors_doc)]
pub fn sim_analog_input(&self, port: u8) -> Result<AnalogOutput, NotSimError> {
self.sim_analog_input
.map_or(Err(NotSimError), |f| Ok((f)(port)))
}
#[allow(clippy::missing_errors_doc)]
pub fn sim_analog_output(&self, port: u8) -> Result<AnalogInput, NotSimError> {
self.sim_analog_output
.map_or(Err(NotSimError), |f| Ok((f)(port)))
}
#[allow(clippy::missing_errors_doc)]
pub fn sim_digital_input(&self, port: u8) -> Result<DigitalOutput, NotSimError> {
self.sim_digital_input
.map_or(Err(NotSimError), |f| Ok((f)(port)))
}
#[allow(clippy::missing_errors_doc)]
pub fn sim_digital_output(&self, port: u8) -> Result<DigitalInput, NotSimError> {
self.sim_digital_output
.map_or(Err(NotSimError), |f| Ok((f)(port)))
}
}