dygma_focus/
lib.rs

1use crate::hardware::Device;
2use anyhow::{anyhow, bail, Result};
3use maybe_async::{async_impl, sync_impl};
4use std::str;
5use std::time::Duration;
6use tracing::{error, trace};
7
8#[cfg(feature = "is_async")]
9use tokio_serial::{
10    SerialPort, SerialPortBuilderExt, SerialPortInfo, SerialPortType, SerialStream,
11};
12
13#[cfg(feature = "is_sync")]
14use serialport::{SerialPort, SerialPortInfo, SerialPortType};
15
16#[cfg(all(feature = "is_sync", windows))]
17use serialport::COMPort;
18#[cfg(all(feature = "is_sync", not(windows)))]
19use serialport::TTYPort;
20
21pub mod api;
22pub mod color;
23pub mod enums;
24pub mod hardware;
25pub mod helpers;
26pub mod prelude;
27pub mod settings;
28
29pub const MAX_LAYERS: u8 = 10 - 1;
30
31/// The Dygma Focus API.
32#[derive(Debug)]
33pub struct Focus {
34    #[cfg(feature = "is_async")]
35    pub(crate) serial: SerialStream,
36    #[cfg(all(feature = "is_sync", windows))]
37    pub(crate) serial: COMPort,
38    #[cfg(all(feature = "is_sync", not(windows)))]
39    pub(crate) serial: TTYPort,
40    pub(crate) response_buffer: Vec<u8>,
41}
42
43/// Constructors
44impl Focus {
45    /// Find all supported devices.
46    #[async_impl]
47    pub fn find_all_devices() -> Result<Vec<Device>> {
48        let ports = match tokio_serial::available_ports() {
49            Ok(ports) => ports,
50            Err(e) => {
51                let err_msg = format!("Failed to enumerate serial ports: {:?}", e);
52                error!("{}", err_msg);
53                bail!(err_msg)
54            }
55        };
56
57        Self::collect_devices(ports)
58    }
59
60    /// Find all supported devices.
61    #[sync_impl]
62    pub fn find_all_devices() -> Result<Vec<Device>> {
63        let ports = match serialport::available_ports() {
64            Ok(ports) => ports,
65            Err(e) => {
66                let err_msg = format!("Failed to enumerate serial ports: {:?}", e);
67                error!("{}", err_msg);
68                bail!(err_msg)
69            }
70        };
71
72        Self::collect_devices(ports)
73    }
74
75    fn collect_devices(ports: Vec<SerialPortInfo>) -> Result<Vec<Device>> {
76        trace!("Available serial ports: {:?}", ports);
77
78        let devices: Vec<Device> = ports
79            .into_iter()
80            .filter_map(|port| match &port.port_type {
81                SerialPortType::UsbPort(info) => {
82                    let matching_devices: Vec<Device> =
83                        hardware::types::hardware_physical::DEVICES_PHYSICAL
84                            .iter()
85                            .filter_map(|device| {
86                                if device.usb.vendor_id == info.vid
87                                    && device.usb.product_id == info.pid
88                                {
89                                    Some(Device {
90                                        hardware: device.to_owned(),
91                                        serial_port: port.port_name.to_owned(),
92                                    })
93                                } else {
94                                    None
95                                }
96                            })
97                            .collect();
98
99                    if matching_devices.is_empty() {
100                        None
101                    } else {
102                        Some(matching_devices)
103                    }
104                }
105                _ => None,
106            })
107            .flatten()
108            .collect();
109
110        trace!("Found devices: {:?}", devices);
111
112        Ok(devices)
113    }
114
115    /// Find the first supported device.
116    pub fn find_first_device() -> Result<Device> {
117        let devices = match Self::find_all_devices() {
118            Ok(devices) => devices,
119            Err(e) => {
120                let err_msg = format!("No device found: {:?}", e);
121                error!("{}", err_msg);
122                bail!(err_msg)
123            }
124        };
125
126        let device = devices.into_iter().nth(0).ok_or_else(|| {
127            let err_msg = "No supported devices found";
128            error!("{}", err_msg);
129            anyhow!(err_msg)
130        })?;
131
132        Ok(device)
133    }
134
135    /// Creates a new instance of the Focus API, connecting to the device via the named serial port.
136    #[async_impl]
137    pub fn new_via_port(port: &str) -> Result<Self> {
138        let port_settings = tokio_serial::new(port, 115_200)
139            .data_bits(tokio_serial::DataBits::Eight)
140            .flow_control(tokio_serial::FlowControl::None)
141            .parity(tokio_serial::Parity::None)
142            .stop_bits(tokio_serial::StopBits::One)
143            .timeout(Duration::from_secs(5));
144
145        let mut serial = port_settings.open_native_async().map_err(|e| {
146            let err_msg = format!("Failed to open serial port: {} ({:?})", &port, e);
147            error!("{}", err_msg);
148            anyhow!(err_msg)
149        })?;
150
151        serial.write_data_terminal_ready(true)?;
152
153        #[cfg(unix)]
154        serial.set_exclusive(false)?;
155
156        Ok(Self {
157            serial,
158            response_buffer: Vec::with_capacity(1_024 * 8),
159        })
160    }
161
162    /// Creates a new instance of the Focus API, connecting to the device via the named serial port.
163    #[sync_impl]
164    pub fn new_via_port(port: &str) -> Result<Self> {
165        let port_settings = serialport::new(port, 115_200)
166            .data_bits(serialport::DataBits::Eight)
167            .flow_control(serialport::FlowControl::None)
168            .parity(serialport::Parity::None)
169            .stop_bits(serialport::StopBits::One)
170            .timeout(Duration::from_millis(40));
171        // https://github.com/serialport/serialport-rs/pull/79 merge before raising the timeout
172
173        let mut serial = port_settings.open_native().map_err(|e| {
174            let err_msg = format!("Failed to open serial port: {} ({:?})", &port, e);
175            error!("{}", err_msg);
176            anyhow!(err_msg)
177        })?;
178
179        serial.write_data_terminal_ready(true)?;
180
181        #[cfg(unix)]
182        serial.set_exclusive(false)?;
183
184        Ok(Self {
185            serial,
186            response_buffer: Vec::with_capacity(1_024 * 8),
187        })
188    }
189
190    /// Creates a new instance of the Focus API, connecting to the device via a reference to the device struct.
191    pub fn new_via_device(device: &Device) -> Result<Self> {
192        Self::new_via_port(&device.serial_port)
193    }
194
195    /// Creates a new instance of the Focus API, connecting to the device via first available device.
196    pub fn new_first_available() -> Result<Self> {
197        Self::new_via_device(Self::find_all_devices()?.first().ok_or_else(|| {
198            let err_msg = "No supported devices found";
199            error!("{}", err_msg);
200            anyhow!(err_msg)
201        })?)
202    }
203}