use crate::error::{Error, Result};
use crate::port::{
DataBits, FlowControl, Parity, Port, PortEnumerator, PortInfo, SerialConfig, StopBits,
};
use log::trace;
use serialport::ClearBuffer;
use std::io::{Read, Write};
use std::time::Duration;
pub struct NativePort {
port: Box<dyn serialport::SerialPort>,
name: String,
timeout: Duration,
baud_rate: u32,
}
impl NativePort {
pub fn open(config: &SerialConfig) -> Result<Self> {
let port = serialport::new(&config.port_name, config.baud_rate)
.timeout(config.timeout)
.data_bits(config.data_bits.into())
.parity(config.parity.into())
.stop_bits(config.stop_bits.into())
.flow_control(config.flow_control.into())
.open()?;
Ok(Self {
port,
name: config.port_name.clone(),
timeout: config.timeout,
baud_rate: config.baud_rate,
})
}
pub fn open_simple(port_name: &str, baud_rate: u32) -> Result<Self> {
let config = SerialConfig::new(port_name, baud_rate);
Self::open(&config)
}
pub fn inner(&self) -> &dyn serialport::SerialPort {
self.port.as_ref()
}
pub fn inner_mut(&mut self) -> &mut dyn serialport::SerialPort {
self.port.as_mut()
}
}
impl Port for NativePort {
fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
self.port.set_timeout(timeout)?;
self.timeout = timeout;
Ok(())
}
fn timeout(&self) -> Duration {
self.timeout
}
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
self.port.set_baud_rate(baud_rate)?;
self.baud_rate = baud_rate;
Ok(())
}
fn baud_rate(&self) -> u32 {
self.baud_rate
}
fn clear_buffers(&mut self) -> Result<()> {
self.port.clear(ClearBuffer::All)?;
Ok(())
}
fn name(&self) -> &str {
&self.name
}
fn set_dtr(&mut self, level: bool) -> Result<()> {
trace!("Setting DTR to {level}");
self.port.write_data_terminal_ready(level)?;
Ok(())
}
fn set_rts(&mut self, level: bool) -> Result<()> {
trace!("Setting RTS to {level}");
self.port.write_request_to_send(level)?;
Ok(())
}
fn read_cts(&self) -> Result<bool> {
Ok(false)
}
fn read_dsr(&self) -> Result<bool> {
Ok(false)
}
}
impl Read for NativePort {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
self.port.read(buf)
}
}
impl Write for NativePort {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.port.write(buf)
}
fn flush(&mut self) -> std::io::Result<()> {
self.port.flush()
}
}
pub struct NativePortEnumerator;
impl PortEnumerator for NativePortEnumerator {
fn list_ports() -> Result<Vec<PortInfo>> {
let ports = serialport::available_ports().map_err(Error::Serial)?;
Ok(ports
.into_iter()
.map(|p| {
let (vid, pid, manufacturer, product, serial_number) = match &p.port_type {
serialport::SerialPortType::UsbPort(info) => (
Some(info.vid),
Some(info.pid),
info.manufacturer.clone(),
info.product.clone(),
info.serial_number.clone(),
),
_ => (None, None, None, None, None),
};
PortInfo {
name: p.port_name,
vid,
pid,
manufacturer,
product,
serial_number,
}
})
.collect())
}
}
impl From<DataBits> for serialport::DataBits {
fn from(bits: DataBits) -> Self {
match bits {
DataBits::Five => Self::Five,
DataBits::Six => Self::Six,
DataBits::Seven => Self::Seven,
DataBits::Eight => Self::Eight,
}
}
}
impl From<Parity> for serialport::Parity {
fn from(parity: Parity) -> Self {
match parity {
Parity::None => Self::None,
Parity::Odd => Self::Odd,
Parity::Even => Self::Even,
}
}
}
impl From<StopBits> for serialport::StopBits {
fn from(bits: StopBits) -> Self {
match bits {
StopBits::One => Self::One,
StopBits::Two => Self::Two,
}
}
}
impl From<FlowControl> for serialport::FlowControl {
fn from(flow: FlowControl) -> Self {
match flow {
FlowControl::None => Self::None,
FlowControl::Hardware => Self::Hardware,
FlowControl::Software => Self::Software,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_list_ports() {
let _ = NativePortEnumerator::list_ports();
}
#[test]
fn test_serial_config_default() {
let config = SerialConfig::default();
assert_eq!(config.baud_rate, 115200);
assert_eq!(config.data_bits, DataBits::Eight);
assert_eq!(config.parity, Parity::None);
assert_eq!(config.stop_bits, StopBits::One);
assert_eq!(config.flow_control, FlowControl::None);
}
#[test]
fn test_serial_config_builder() {
let config = SerialConfig::new("/dev/ttyUSB0", 921600).with_timeout(Duration::from_secs(5));
assert_eq!(config.port_name, "/dev/ttyUSB0");
assert_eq!(config.baud_rate, 921600);
assert_eq!(config.timeout, Duration::from_secs(5));
}
}