use core::cmp::Ordering;
use super::{
AdiAnalog, AdiAnalogError, AdiDigitalInput, AdiDigitalInputError, AdiDigitalOutput,
AdiDigitalOutputError, AdiEncoder, AdiEncoderError, AdiGyro, AdiGyroError, AdiUltrasonic,
AdiUltrasonicError,
};
use crate::bindings;
pub struct AdiPort {
port: u8,
expander_port: u8,
}
impl AdiPort {
pub unsafe fn new(port: u8, expander_port: u8) -> Self {
assert!(
(1..9).contains(&port),
"Cannot construct an ADI port on port {}",
port
);
assert!(
(1..22).contains(&expander_port) || expander_port == bindings::INTERNAL_ADI_PORT as u8,
"Cannot construct an ADI port with ADI expander on smart port {}",
expander_port
);
Self {
port,
expander_port,
}
}
#[inline]
pub fn into_adi_analog(self) -> Result<AdiAnalog, AdiAnalogError> {
self.try_into()
}
#[inline]
pub fn into_adi_digital_input(self) -> Result<AdiDigitalInput, AdiDigitalInputError> {
self.try_into()
}
#[inline]
pub fn into_adi_digital_output(self) -> Result<AdiDigitalOutput, AdiDigitalOutputError> {
self.try_into()
}
#[inline]
pub fn into_adi_encoder(self, bottom: Self) -> Result<AdiEncoder, AdiEncoderError> {
(self, bottom).try_into()
}
#[inline]
pub fn into_adi_gyro(self, multiplier: f64) -> Result<AdiGyro, AdiGyroError> {
(self, multiplier).try_into()
}
#[inline]
pub fn into_adi_ultrasonic(self, bottom: Self) -> Result<AdiUltrasonic, AdiUltrasonicError> {
(self, bottom).try_into()
}
}
impl TryFrom<AdiPort> for AdiAnalog {
type Error = AdiAnalogError;
fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
unsafe { AdiAnalog::new(port.port, port.expander_port) }
}
}
impl TryFrom<AdiPort> for AdiDigitalInput {
type Error = AdiDigitalInputError;
fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
unsafe { AdiDigitalInput::new(port.port, port.expander_port) }
}
}
impl TryFrom<AdiPort> for AdiDigitalOutput {
type Error = AdiDigitalOutputError;
fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
unsafe { AdiDigitalOutput::new(port.port, port.expander_port) }
}
}
impl TryFrom<(AdiPort, AdiPort)> for AdiEncoder {
type Error = AdiEncoderError;
fn try_from(ports: (AdiPort, AdiPort)) -> Result<Self, Self::Error> {
if ports.0.expander_port != ports.1.expander_port {
return Err(AdiEncoderError::PortNonMatchingExtenders);
}
let top_port;
let bottom_port;
let reversed;
match ports.0.port.cmp(&ports.1.port) {
Ordering::Less => {
top_port = ports.0;
bottom_port = ports.1;
reversed = false;
}
Ordering::Greater => {
top_port = ports.1;
bottom_port = ports.0;
reversed = true;
}
Ordering::Equal => return Err(AdiEncoderError::PortsOutOfRange),
}
if bottom_port.port - top_port.port != 1 || bottom_port.port % 2 != 0 {
return Err(AdiEncoderError::PortsOutOfRange);
}
unsafe {
AdiEncoder::new(
top_port.port,
bottom_port.port,
reversed,
top_port.expander_port,
)
}
}
}
impl TryFrom<(AdiPort, f64)> for AdiGyro {
type Error = AdiGyroError;
#[inline]
fn try_from((port, multiplier): (AdiPort, f64)) -> Result<Self, Self::Error> {
unsafe { AdiGyro::new(port.port, multiplier, port.expander_port) }
}
}
impl TryFrom<(AdiPort, AdiPort)> for AdiUltrasonic {
type Error = AdiUltrasonicError;
fn try_from(ports: (AdiPort, AdiPort)) -> Result<Self, Self::Error> {
if ports.0.expander_port != ports.1.expander_port {
Err(AdiUltrasonicError::PortNonMatchingExtenders)
} else {
unsafe { AdiUltrasonic::new(ports.0.port, ports.1.port, ports.0.expander_port) }
}
}
}