#[cfg(target_os = "ios")]
use crate::serialport;
use serialport::{ClearBuffer, DataBits, FlowControl, Parity, SerialPort, StopBits};
use std::io::{self, Read, Write};
use std::net::{TcpStream, ToSocketAddrs};
use std::time::Duration;
pub const TCP_SCHEME: &str = "tcp://";
pub fn tcp_address(path: &str) -> Option<&str> {
path.strip_prefix(TCP_SCHEME)
}
pub struct TcpSerialPort {
stream: TcpStream,
path: String,
baud_rate: u32,
data_bits: DataBits,
flow_control: FlowControl,
parity: Parity,
stop_bits: StopBits,
timeout: Duration,
}
fn socket_timeout(t: Duration) -> Option<Duration> {
Some(t.max(Duration::from_millis(1)))
}
impl TcpSerialPort {
pub fn connect(
path: &str,
baud_rate: u32,
data_bits: DataBits,
flow_control: FlowControl,
parity: Parity,
stop_bits: StopBits,
timeout: Duration,
) -> io::Result<Self> {
let addr = tcp_address(path)
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "not a tcp:// path"))?;
let mut last_err = io::Error::new(io::ErrorKind::NotFound, format!("{addr}: no address"));
let connect_timeout = timeout.max(Duration::from_secs(3));
for sock in addr.to_socket_addrs()? {
match TcpStream::connect_timeout(&sock, connect_timeout) {
Ok(stream) => {
stream.set_nodelay(true)?;
stream.set_read_timeout(socket_timeout(timeout))?;
stream.set_write_timeout(socket_timeout(timeout))?;
return Ok(Self {
stream,
path: path.to_string(),
baud_rate,
data_bits,
flow_control,
parity,
stop_bits,
timeout,
});
}
Err(e) => last_err = e,
}
}
Err(last_err)
}
}
impl Read for TcpSerialPort {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
match self.stream.read(buf) {
Ok(0) if !buf.is_empty() => Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"TCP serial bridge closed the connection",
)),
Err(e) if e.kind() == io::ErrorKind::WouldBlock => {
Err(io::Error::new(io::ErrorKind::TimedOut, e))
}
other => other,
}
}
}
impl Write for TcpSerialPort {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
self.stream.write(buf)
}
fn flush(&mut self) -> io::Result<()> {
self.stream.flush()
}
}
impl SerialPort for TcpSerialPort {
fn name(&self) -> Option<String> {
Some(self.path.clone())
}
fn baud_rate(&self) -> serialport::Result<u32> {
Ok(self.baud_rate)
}
fn data_bits(&self) -> serialport::Result<DataBits> {
Ok(self.data_bits)
}
fn flow_control(&self) -> serialport::Result<FlowControl> {
Ok(self.flow_control)
}
fn parity(&self) -> serialport::Result<Parity> {
Ok(self.parity)
}
fn stop_bits(&self) -> serialport::Result<StopBits> {
Ok(self.stop_bits)
}
fn timeout(&self) -> Duration {
self.timeout
}
fn set_baud_rate(&mut self, baud_rate: u32) -> serialport::Result<()> {
self.baud_rate = baud_rate;
Ok(())
}
fn set_data_bits(&mut self, data_bits: DataBits) -> serialport::Result<()> {
self.data_bits = data_bits;
Ok(())
}
fn set_flow_control(&mut self, flow_control: FlowControl) -> serialport::Result<()> {
self.flow_control = flow_control;
Ok(())
}
fn set_parity(&mut self, parity: Parity) -> serialport::Result<()> {
self.parity = parity;
Ok(())
}
fn set_stop_bits(&mut self, stop_bits: StopBits) -> serialport::Result<()> {
self.stop_bits = stop_bits;
Ok(())
}
fn set_timeout(&mut self, timeout: Duration) -> serialport::Result<()> {
self.stream.set_read_timeout(socket_timeout(timeout))?;
self.stream.set_write_timeout(socket_timeout(timeout))?;
self.timeout = timeout;
Ok(())
}
fn write_request_to_send(&mut self, _level: bool) -> serialport::Result<()> {
Ok(())
}
fn write_data_terminal_ready(&mut self, _level: bool) -> serialport::Result<()> {
Ok(())
}
fn read_clear_to_send(&mut self) -> serialport::Result<bool> {
Ok(true)
}
fn read_data_set_ready(&mut self) -> serialport::Result<bool> {
Ok(true)
}
fn read_ring_indicator(&mut self) -> serialport::Result<bool> {
Ok(false)
}
fn read_carrier_detect(&mut self) -> serialport::Result<bool> {
Ok(true)
}
fn bytes_to_read(&self) -> serialport::Result<u32> {
self.stream.set_nonblocking(true)?;
let mut buf = [0u8; 4096];
let pending = match self.stream.peek(&mut buf) {
Ok(n) => n as u32,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => 0,
Err(e) => {
let _ = self.stream.set_nonblocking(false);
return Err(e.into());
}
};
self.stream.set_nonblocking(false)?;
Ok(pending)
}
fn bytes_to_write(&self) -> serialport::Result<u32> {
Ok(0)
}
fn clear(&self, buffer_to_clear: ClearBuffer) -> serialport::Result<()> {
if matches!(buffer_to_clear, ClearBuffer::Output) {
return Ok(());
}
self.stream.set_nonblocking(true)?;
let mut buf = [0u8; 4096];
let result = loop {
match (&self.stream).read(&mut buf) {
Ok(0) => break Ok(()),
Ok(_) => continue,
Err(e) if e.kind() == io::ErrorKind::WouldBlock => break Ok(()),
Err(e) => break Err(e.into()),
}
};
self.stream.set_nonblocking(false)?;
result
}
fn try_clone(&self) -> serialport::Result<Box<dyn SerialPort>> {
Ok(Box::new(Self {
stream: self.stream.try_clone()?,
path: self.path.clone(),
baud_rate: self.baud_rate,
data_bits: self.data_bits,
flow_control: self.flow_control,
parity: self.parity,
stop_bits: self.stop_bits,
timeout: self.timeout,
}))
}
fn set_break(&self) -> serialport::Result<()> {
Ok(())
}
fn clear_break(&self) -> serialport::Result<()> {
Ok(())
}
}