vex_rt/adi/
port.rs

1use core::cmp::Ordering;
2
3use super::{
4    AdiAnalog, AdiAnalogError, AdiDigitalInput, AdiDigitalInputError, AdiDigitalOutput,
5    AdiDigitalOutputError, AdiEncoder, AdiEncoderError, AdiGyro, AdiGyroError, AdiUltrasonic,
6    AdiUltrasonicError,
7};
8
9use crate::bindings;
10
11/// A struct which represents an unconfigured ADI port.
12pub struct AdiPort {
13    port: u8,
14    expander_port: u8,
15}
16
17impl AdiPort {
18    /// Constructs a new ADI port.
19    ///
20    /// # Safety
21    ///
22    /// This function is unsafe because it allows the user to create multiple
23    /// mutable references to an V5 ADI port. You likely want to implement
24    /// [`Robot::new()`](crate::robot::Robot::new()) instead.
25    pub unsafe fn new(port: u8, expander_port: u8) -> Self {
26        assert!(
27            (1..9).contains(&port),
28            "Cannot construct an ADI port on port {}",
29            port
30        );
31        assert!(
32            (1..22).contains(&expander_port) || expander_port == bindings::INTERNAL_ADI_PORT as u8,
33            "Cannot construct an ADI port with ADI expander on smart port {}",
34            expander_port
35        );
36        Self {
37            port,
38            expander_port,
39        }
40    }
41
42    /// Turns this port into an ADI analog input.
43    #[inline]
44    pub fn into_adi_analog(self) -> Result<AdiAnalog, AdiAnalogError> {
45        self.try_into()
46    }
47
48    /// Turns this port into an ADI digital input.
49    #[inline]
50    pub fn into_adi_digital_input(self) -> Result<AdiDigitalInput, AdiDigitalInputError> {
51        self.try_into()
52    }
53    /// Turns this port into an ADI digital output.
54    #[inline]
55    pub fn into_adi_digital_output(self) -> Result<AdiDigitalOutput, AdiDigitalOutputError> {
56        self.try_into()
57    }
58
59    /// Turns this and another port into an ADI encoder.
60    #[inline]
61    pub fn into_adi_encoder(self, bottom: Self) -> Result<AdiEncoder, AdiEncoderError> {
62        (self, bottom).try_into()
63    }
64
65    /// Turns this port into an ADI gyro.
66    #[inline]
67    pub fn into_adi_gyro(self, multiplier: f64) -> Result<AdiGyro, AdiGyroError> {
68        (self, multiplier).try_into()
69    }
70
71    /// Turns this and another port into an ADI ultrasonic sensor.
72    #[inline]
73    pub fn into_adi_ultrasonic(self, bottom: Self) -> Result<AdiUltrasonic, AdiUltrasonicError> {
74        (self, bottom).try_into()
75    }
76}
77
78impl TryFrom<AdiPort> for AdiAnalog {
79    type Error = AdiAnalogError;
80
81    /// Converts a `AdiPort` into a [`AdiAnalog`].
82    fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
83        unsafe { AdiAnalog::new(port.port, port.expander_port) }
84    }
85}
86
87impl TryFrom<AdiPort> for AdiDigitalInput {
88    type Error = AdiDigitalInputError;
89
90    /// Converts a `AdiPort` into a [`AdiDigitalInput`].
91    fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
92        unsafe { AdiDigitalInput::new(port.port, port.expander_port) }
93    }
94}
95
96impl TryFrom<AdiPort> for AdiDigitalOutput {
97    type Error = AdiDigitalOutputError;
98
99    /// Converts a `AdiPort` into a [`AdiDigitalOutput`].
100    fn try_from(port: AdiPort) -> Result<Self, Self::Error> {
101        unsafe { AdiDigitalOutput::new(port.port, port.expander_port) }
102    }
103}
104
105impl TryFrom<(AdiPort, AdiPort)> for AdiEncoder {
106    type Error = AdiEncoderError;
107
108    /// Converts an `(AdiPort, AdiPort)` into an
109    /// [`AdiEncoder`](crate::adi::AdiEncoder).
110    fn try_from(ports: (AdiPort, AdiPort)) -> Result<Self, Self::Error> {
111        if ports.0.expander_port != ports.1.expander_port {
112            return Err(AdiEncoderError::PortNonMatchingExtenders);
113        }
114
115        let top_port;
116        let bottom_port;
117        let reversed;
118
119        match ports.0.port.cmp(&ports.1.port) {
120            Ordering::Less => {
121                top_port = ports.0;
122                bottom_port = ports.1;
123                reversed = false;
124            }
125            Ordering::Greater => {
126                top_port = ports.1;
127                bottom_port = ports.0;
128                reversed = true;
129            }
130            Ordering::Equal => return Err(AdiEncoderError::PortsOutOfRange),
131        }
132
133        if bottom_port.port - top_port.port != 1 || bottom_port.port % 2 != 0 {
134            return Err(AdiEncoderError::PortsOutOfRange);
135        }
136
137        unsafe {
138            AdiEncoder::new(
139                top_port.port,
140                bottom_port.port,
141                reversed,
142                top_port.expander_port,
143            )
144        }
145    }
146}
147
148impl TryFrom<(AdiPort, f64)> for AdiGyro {
149    type Error = AdiGyroError;
150
151    #[inline]
152    fn try_from((port, multiplier): (AdiPort, f64)) -> Result<Self, Self::Error> {
153        unsafe { AdiGyro::new(port.port, multiplier, port.expander_port) }
154    }
155}
156
157impl TryFrom<(AdiPort, AdiPort)> for AdiUltrasonic {
158    type Error = AdiUltrasonicError;
159
160    fn try_from(ports: (AdiPort, AdiPort)) -> Result<Self, Self::Error> {
161        if ports.0.expander_port != ports.1.expander_port {
162            Err(AdiUltrasonicError::PortNonMatchingExtenders)
163        } else {
164            unsafe { AdiUltrasonic::new(ports.0.port, ports.1.port, ports.0.expander_port) }
165        }
166    }
167}