1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use crate::{
    bindings,
    error::{get_errno, Error},
    prelude::DataSource,
};

/// A struct which represents a V5 ADI port configured as an ADI digital input.
#[derive(Debug)]
pub struct AdiDigitalInput {
    port: u8,
    expander_port: u8,
}

impl AdiDigitalInput {
    /// Initializes an ADI digital input on an ADI port.
    ///
    /// # Safety
    ///
    /// This function is unsafe because it allows the user to create multiple
    /// mutable references to the same ADI digital input. You likely want to
    /// implement [`Robot::new()`](crate::robot::Robot::new()) instead.
    pub unsafe fn new(port: u8, expander_port: u8) -> Result<Self, AdiDigitalInputError> {
        match bindings::ext_adi_port_set_config(
            expander_port,
            port,
            bindings::adi_port_config_e_E_ADI_DIGITAL_IN,
        ) {
            bindings::PROS_ERR_ => Err(AdiDigitalInputError::from_errno()),
            _ => Ok(Self {
                port,
                expander_port,
            }),
        }
    }

    /// Gets the digital value (true or false) of the input.
    pub fn read(&self) -> Result<bool, AdiDigitalInputError> {
        match unsafe { bindings::ext_adi_digital_read(self.expander_port, self.port) } {
            bindings::PROS_ERR_ => Err(AdiDigitalInputError::from_errno()),
            0 => Ok(false),
            _ => Ok(true),
        }
    }
}

impl DataSource for AdiDigitalInput {
    type Data = bool;

    type Error = AdiDigitalInputError;

    fn read(&self) -> Result<Self::Data, Self::Error> {
        self.read()
    }
}

/// Represents possible errors for ADI digital input operations.
#[derive(Debug)]
pub enum AdiDigitalInputError {
    /// Port is out of range (1-8).
    PortsOutOfRange,
    /// Port cannot be configured as an ADI digital input.
    PortsNotDigitalInput,
    /// Unknown error.
    Unknown(i32),
}

impl AdiDigitalInputError {
    fn from_errno() -> Self {
        match get_errno() {
            libc::ENXIO => Self::PortsOutOfRange,
            libc::EADDRINUSE => Self::PortsNotDigitalInput,
            x => Self::Unknown(x),
        }
    }
}

impl From<AdiDigitalInputError> for Error {
    fn from(err: AdiDigitalInputError) -> Self {
        match err {
            AdiDigitalInputError::PortsOutOfRange => Error::Custom("port is out of range".into()),
            AdiDigitalInputError::PortsNotDigitalInput => {
                Error::Custom("port is not an ADI digital input".into())
            }
            AdiDigitalInputError::Unknown(n) => Error::System(n),
        }
    }
}