Skip to main content

ev3dev_lang_rust/
port.rs

1//! Collection of port utils
2
3use crate::{Driver, Ev3Error, Ev3Result};
4
5/// EV3 ports
6pub trait Port {
7    /// Returns the name of the port.
8    fn address(&self) -> String;
9
10    /// Get the port device for this port
11    /// # Examples
12    ///
13    /// ```no_run
14    /// use ev3dev_lang_rust::{Ev3Result, Port};
15    /// use ev3dev_lang_rust::sensors::{ColorSensor, SensorPort};
16    /// use std::thread;
17    /// use std::time::Duration;
18    ///
19    /// fn init_color_sensor(port: SensorPort) -> Ev3Result<ColorSensor> {
20    ///     let lego_port = port.get_lego_port()?;
21    ///     lego_port.set_mode("ev3-uart")?;
22    ///     lego_port.set_device("lego-ev3-color")?;
23    ///
24    ///     thread::sleep(Duration::from_millis(100));
25    ///
26    ///     ColorSensor::get(port)
27    /// }
28    ///
29    /// # fn main() -> Ev3Result<()> {
30    /// let color_sensor = init_color_sensor(SensorPort::In1)?;
31    /// # Ok(())
32    /// # }
33    fn get_lego_port(&self) -> Ev3Result<LegoPort>;
34}
35
36/// Lego ports
37#[derive(Debug, Clone)]
38pub struct LegoPort {
39    driver: Driver,
40}
41
42impl LegoPort {
43    fn new(driver: Driver) -> Self {
44        Self { driver }
45    }
46
47    fn map_error(e: Ev3Error) -> Ev3Error {
48        match e {
49            e @ Ev3Error::InternalError { .. } => e,
50            Ev3Error::NotConnected { device: _, port } => Ev3Error::NotConnected {
51                device: "LegoPort".to_owned(),
52                port,
53            },
54            Ev3Error::MultipleMatches { device: _, ports } => Ev3Error::MultipleMatches {
55                device: "LegoPort".to_owned(),
56                ports,
57            },
58        }
59    }
60
61    fn driver_names() -> Vec<&'static str> {
62        vec![
63            "ev3-input-port",
64            "ev3-output-port",
65            "modbrick-in-port",
66            "modbrick-out-port",
67        ]
68    }
69
70    /// Try to get a `Self` on the given port. Returns `None` if port is not used or another device is connected.
71    /// Get the port device for this port
72    /// # Examples
73    ///
74    /// ```no_run
75    /// use ev3dev_lang_rust::{Ev3Result, LegoPort};
76    /// use ev3dev_lang_rust::sensors::{ColorSensor, SensorPort};
77    /// use std::thread;
78    /// use std::time::Duration;
79    ///
80    /// fn init_color_sensor(port: SensorPort) -> Ev3Result<ColorSensor> {
81    ///     let lego_port = LegoPort::get(port)?;
82    ///     lego_port.set_mode("ev3-uart")?;
83    ///     lego_port.set_device("lego-ev3-color")?;
84    ///
85    ///     thread::sleep(Duration::from_millis(100));
86    ///
87    ///     ColorSensor::get(port)
88    /// }
89    ///
90    /// # fn main() -> Ev3Result<()> {
91    /// let color_sensor = init_color_sensor(SensorPort::In1)?;
92    /// # Ok(())
93    /// # }
94    pub fn get<T: Port>(port: T) -> Ev3Result<Self> {
95        let driver_name_vec = Self::driver_names();
96
97        let name = Driver::find_name_by_port_and_driver("lego-port", &port, &driver_name_vec)
98            .map_err(Self::map_error)?;
99
100        Ok(Self::new(Driver::new("lego-port", &name)))
101    }
102
103    /// Try to find a `Self`. Only returns a device if their is exactly one connected, `Error::NotFound` otherwise.
104    pub fn find() -> Ev3Result<Self> {
105        let driver_name_vec = Self::driver_names();
106
107        let name =
108            Driver::find_name_by_driver("lego-port", &driver_name_vec).map_err(Self::map_error)?;
109
110        Ok(Self::new(Driver::new("lego-port", &name)))
111    }
112
113    /// Extract list of connected 'Self'
114    pub fn list() -> Ev3Result<Vec<Self>> {
115        let driver_name_vec = Self::driver_names();
116
117        Ok(Driver::find_names_by_driver("lego-port", &driver_name_vec)?
118            .iter()
119            .map(|name| Self::new(Driver::new("lego-port", name)))
120            .collect())
121    }
122
123    /// Returns the name of the port that the device is connected to.
124    pub fn get_address(&self) -> Ev3Result<String> {
125        self.driver.get_attribute("address").get()
126    }
127
128    /// Returns the name of the driver that provides this device.
129    pub fn get_driver_name(&self) -> Ev3Result<String> {
130        self.driver.get_attribute("driver_name").get()
131    }
132
133    /// Returns the currently selected mode.
134    pub fn get_mode(&self) -> Ev3Result<String> {
135        self.driver.get_attribute("mode").get()
136    }
137
138    /// Sets the currently selected mode.
139    /// Generally speaking when the mode changes any sensor or motor devices
140    /// associated with the port will be removed new ones loaded, however this
141    /// this will depend on the individual driver implementing this class.
142    pub fn set_mode(&self, mode: &str) -> Ev3Result<()> {
143        self.driver.get_attribute("mode").set_str_slice(mode)
144    }
145
146    /// Returns a list of the available modes of the port.
147    pub fn get_modes(&self) -> Ev3Result<Vec<String>> {
148        self.driver.get_attribute("modes").get_vec()
149    }
150
151    /// For modes that support it, writing the name of a driver will cause a new
152    /// device to be registered for that driver and attached to this port. For
153    /// example, since NXT/Analog sensors cannot be auto-detected, you must use
154    /// this attribute to load the correct driver. Returns -EOPNOTSUPP if setting a
155    /// device is not supported.
156    pub fn set_device(&self, mode: &str) -> Ev3Result<()> {
157        self.driver.get_attribute("set_device").set_str_slice(mode)
158    }
159
160    /// In most cases, reading status will return the same value as `mode`. In
161    /// cases where there is an `auto` mode additional values may be returned,
162    /// such as `no-device` or `error`. See individual port driver documentation
163    /// for the full list of possible values.
164    pub fn get_status(&self) -> Ev3Result<String> {
165        self.driver.get_attribute("status").get()
166    }
167}