pros_devices/
peripherals.rs

1//! Peripherals implementations.
2//!
3//! Peripherals are the best way to create devices because they allow you to do it safely.
4//! Both kinds of peripherals, [`Peripherals`] and [`DynamicPeripherals`], guarentee that a given port is only used to create one device.
5//! This is important because creating multiple devices on the same port can cause bugs and unexpected behavior.
6//! Devices can still be created unsafely without using peripherals, but it isn't recommended.
7//!
8//! ## Examples
9//!
10//! ### Using [`Peripherals`]
11//! ```rust
12//! # use pros::prelude::*;
13//! let mut peripherals = Peripherals::take().unwrap();
14//! let motor = Motor::new(peripherals.port_1);
15//! let adi_digital_in = AdiDigitalIn::new(peripherals.adi_d);
16//! ```
17//! ### Using [`DynamicPeripherals`]
18//! ```rust
19//! # use pros::prelude::*;
20//! let mut peripherals = DynamicPeripherals::new(Peripherals::take().unwrap());
21//! let motor = peripherals.take_smart_port(1).unwrap();
22//! let adi_digital_in = peripherals.take_adi_port(4).unwrap();
23//! ```
24
25use core::sync::atomic::AtomicBool;
26
27use crate::{adi::AdiPort, screen::Screen, smart::SmartPort};
28
29static PERIPHERALS_TAKEN: AtomicBool = AtomicBool::new(false);
30
31#[derive(Debug)]
32/// A struct that contains all ports on the V5 Brain
33/// and guarentees **at compile time** that each port is only used once.
34/// Because of the fact that this checks at compile time, it cannot be moved once it has been used to create a device.
35/// If you need to store a peripherals struct for use in multiple functions, use [`DynamicPeripherals`] instead.
36/// This struct is always preferred over [`DynamicPeripherals`] when possible.
37pub struct Peripherals {
38    /// Brain screen
39    pub screen: Screen,
40
41    /// Smart port 1 on the brain
42    pub port_1: SmartPort,
43    /// Smart port 2 on the brain
44    pub port_2: SmartPort,
45    /// Smart port 3 on the brain
46    pub port_3: SmartPort,
47    /// Smart port 4 on the brain
48    pub port_4: SmartPort,
49    /// Smart port 5 on the brain
50    pub port_5: SmartPort,
51    /// Smart port 6 on the brain
52    pub port_6: SmartPort,
53    /// Smart port 7 on the brain
54    pub port_7: SmartPort,
55    /// Smart port 8 on the brain
56    pub port_8: SmartPort,
57    /// Smart port 9 on the brain
58    pub port_9: SmartPort,
59    /// Smart port 10 on the brain
60    pub port_10: SmartPort,
61    /// Smart port 11 on the brain
62    pub port_11: SmartPort,
63    /// Smart port 12 on the brain
64    pub port_12: SmartPort,
65    /// Smart port 13 on the brain
66    pub port_13: SmartPort,
67    /// Smart port 14 on the brain
68    pub port_14: SmartPort,
69    /// Smart port 15 on the brain
70    pub port_15: SmartPort,
71    /// Smart port 16 on the brain
72    pub port_16: SmartPort,
73    /// Smart port 17 on the brain
74    pub port_17: SmartPort,
75    /// Smart port 18 on the brain
76    pub port_18: SmartPort,
77    /// Smart port 19 on the brain
78    pub port_19: SmartPort,
79    /// Smart port 20 on the brain
80    pub port_20: SmartPort,
81    /// Smart port 21 on the brain
82    pub port_21: SmartPort,
83
84    /// Adi port A on the brain.
85    pub adi_a: AdiPort,
86    /// Adi port B on the brain.
87    pub adi_b: AdiPort,
88    /// Adi port C on the brain.
89    pub adi_c: AdiPort,
90    /// Adi port D on the brain.
91    pub adi_d: AdiPort,
92    /// Adi port E on the brain.
93    pub adi_e: AdiPort,
94    /// Adi port F on the brain.
95    pub adi_f: AdiPort,
96    /// Adi port G on the brain.
97    pub adi_g: AdiPort,
98    /// Adi port H on the brain.
99    pub adi_h: AdiPort,
100}
101
102impl Peripherals {
103    // SAFETY: caller must ensure that the SmartPorts and AdiPorts created are unique
104    unsafe fn new() -> Self {
105        // SAFETY: caller must ensure that this function is only called once
106        unsafe {
107            Self {
108                screen: Screen::new(),
109
110                port_1: SmartPort::new(1),
111                port_2: SmartPort::new(2),
112                port_3: SmartPort::new(3),
113                port_4: SmartPort::new(4),
114                port_5: SmartPort::new(5),
115                port_6: SmartPort::new(6),
116                port_7: SmartPort::new(7),
117                port_8: SmartPort::new(8),
118                port_9: SmartPort::new(9),
119                port_10: SmartPort::new(10),
120                port_11: SmartPort::new(11),
121                port_12: SmartPort::new(12),
122                port_13: SmartPort::new(13),
123                port_14: SmartPort::new(14),
124                port_15: SmartPort::new(15),
125                port_16: SmartPort::new(16),
126                port_17: SmartPort::new(17),
127                port_18: SmartPort::new(18),
128                port_19: SmartPort::new(19),
129                port_20: SmartPort::new(20),
130                port_21: SmartPort::new(21),
131
132                adi_a: AdiPort::new(1, None),
133                adi_b: AdiPort::new(2, None),
134                adi_c: AdiPort::new(3, None),
135                adi_d: AdiPort::new(4, None),
136                adi_e: AdiPort::new(5, None),
137                adi_f: AdiPort::new(6, None),
138                adi_g: AdiPort::new(7, None),
139                adi_h: AdiPort::new(8, None),
140            }
141        }
142    }
143
144    /// Attempts to create a new [`Peripherals`] struct, returning `None` if one has already been created.
145    ///
146    /// After calling this function, future calls to [`Peripherals::take`] will return `None`.
147    pub fn take() -> Option<Self> {
148        if PERIPHERALS_TAKEN.swap(true, core::sync::atomic::Ordering::AcqRel) {
149            None
150        } else {
151            Some(unsafe { Self::new() })
152        }
153    }
154
155    /// Creates a new [`Peripherals`] struct without ensuring that is the only unique instance.
156    ///
157    /// After calling this function, future calls to [`Peripherals::take`] will return `None`.
158    ///
159    /// # Safety
160    ///
161    /// Creating new [`SmartPort`]s and [`Peripherals`] instances is inherently unsafe due to the possibility of constructing more than
162    /// one device on the same port index and allowing multiple mutable references to the same hardware device.
163    /// The caller must ensure that only one mutable reference to each port is used.
164    pub unsafe fn steal() -> Self {
165        PERIPHERALS_TAKEN.store(true, core::sync::atomic::Ordering::Release);
166        // SAFETY: caller must ensure that this call is safe
167        unsafe { Self::new() }
168    }
169}
170
171/// Guarentees that ports are only used once **at runtime**
172/// This is useful for when you want to store a peripherals struct for use in multiple functions.
173/// When possible, use [`Peripherals`] instead.
174#[derive(Debug)]
175pub struct DynamicPeripherals {
176    screen: bool,
177    smart_ports: [bool; 21],
178    adi_slots: [bool; 8],
179}
180impl DynamicPeripherals {
181    /// Creates a new dynamic peripherals
182    /// In order to guarentee that no ports created by this struct,
183    /// this function takes a [`Peripherals`].
184    /// This guarentees safety because [`Peripherals`] cannot be passed by value
185    /// after they have been used to create devices.
186    pub fn new(_peripherals: Peripherals) -> Self {
187        let smart_ports = [false; 21];
188        let adi_slots = [false; 8];
189        Self {
190            screen: false,
191            smart_ports,
192            adi_slots,
193        }
194    }
195
196    /// Creates a [`SmartPort`] only if one has not been created on the given port before.
197    ///
198    /// # Panics
199    ///
200    /// This function panics if the provided port is outside the range 1-21.
201    /// Ports outside of this range are invalid and cannot be created.
202    pub fn take_smart_port(&mut self, port_index: u8) -> Option<SmartPort> {
203        let port_index = port_index as usize - 1;
204        if self.smart_ports[port_index] {
205            return None;
206        };
207        self.smart_ports[port_index] = true;
208        Some(unsafe { SmartPort::new(port_index as u8 + 1) })
209    }
210
211    /// Creates an [`AdiPort`] only if one has not been created on the given slot before.
212    ///
213    /// # Panics
214    ///
215    /// This function panics if the provided port is outside the range 1-8.
216    /// Slots outside of this range are invalid and cannot be created.
217    pub fn take_adi_port(&mut self, port_index: u8) -> Option<AdiPort> {
218        let port_index = port_index as usize - 1;
219        if self.adi_slots[port_index] {
220            return None;
221        }
222        self.smart_ports[port_index] = true;
223        Some(unsafe { AdiPort::new(port_index as u8 + 1, None) })
224    }
225
226    /// Creates a [`Screen`] only if one has not been created before.
227    pub fn take_screen(&mut self) -> Option<Screen> {
228        if self.screen {
229            return None;
230        }
231        self.screen = true;
232        Some(unsafe { Screen::new() })
233    }
234}
235impl From<Peripherals> for DynamicPeripherals {
236    fn from(peripherals: Peripherals) -> Self {
237        Self::new(peripherals)
238    }
239}