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
11pub struct AdiPort {
13 port: u8,
14 expander_port: u8,
15}
16
17impl AdiPort {
18 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 #[inline]
44 pub fn into_adi_analog(self) -> Result<AdiAnalog, AdiAnalogError> {
45 self.try_into()
46 }
47
48 #[inline]
50 pub fn into_adi_digital_input(self) -> Result<AdiDigitalInput, AdiDigitalInputError> {
51 self.try_into()
52 }
53 #[inline]
55 pub fn into_adi_digital_output(self) -> Result<AdiDigitalOutput, AdiDigitalOutputError> {
56 self.try_into()
57 }
58
59 #[inline]
61 pub fn into_adi_encoder(self, bottom: Self) -> Result<AdiEncoder, AdiEncoderError> {
62 (self, bottom).try_into()
63 }
64
65 #[inline]
67 pub fn into_adi_gyro(self, multiplier: f64) -> Result<AdiGyro, AdiGyroError> {
68 (self, multiplier).try_into()
69 }
70
71 #[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 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 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 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 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}