#[cfg(doctest)]
#[doc = include_str!("../README.md")]
struct ReadMe;
use std::{
collections::vec_deque::VecDeque,
io,
sync::{Arc, Condvar, Mutex},
time::Duration,
};
use rand::Rng;
use serialport::{
ClearBuffer, DataBits, Error, ErrorKind, FlowControl, Parity, Result, SerialPort, StopBits,
};
struct Config {
baud_rate: u32,
data_bits: DataBits,
flow_control: FlowControl,
parity: Parity,
stop_bits: StopBits,
timeout: Duration,
simulate_delay: bool,
noise_on_config_mismatch: bool,
}
impl Config {
fn new(baud_rate: u32) -> Self {
Self {
baud_rate,
data_bits: DataBits::Eight,
flow_control: FlowControl::None,
parity: Parity::None,
stop_bits: StopBits::One,
timeout: Duration::from_millis(0),
simulate_delay: false,
noise_on_config_mismatch: false,
}
}
fn bits_per_byte(&self) -> u32 {
1 + match self.data_bits {
DataBits::Five => 5,
DataBits::Six => 6,
DataBits::Seven => 7,
DataBits::Eight => 8,
} + match self.parity {
Parity::Odd | Parity::Even => 1,
Parity::None => 0,
} + match self.stop_bits {
StopBits::One => 1,
StopBits::Two => 2,
}
}
fn byte_duration(&self) -> Option<Duration> {
self.simulate_delay.then(|| {
Duration::from_micros(((1_000_000 / self.baud_rate) * self.bits_per_byte()) as u64)
})
}
fn physical_settings_mismatch(&self, other: &Config) -> bool {
self.baud_rate != other.baud_rate
|| self.data_bits != other.data_bits
|| self.parity != other.parity
|| self.stop_bits != other.stop_bits
}
}
#[derive(Clone)]
pub struct VirtualPort {
config: Arc<Mutex<Config>>,
paired_port_config: Option<Arc<Mutex<Config>>>,
read_buffer: Arc<(Mutex<VecDeque<u8>>, Condvar, Condvar)>,
write_buffer: Arc<(Mutex<VecDeque<u8>>, Condvar, Condvar)>,
rts: Arc<Mutex<bool>>,
cts: Arc<Mutex<bool>>,
dtr: Arc<Mutex<bool>>,
dsr_cd: Arc<Mutex<bool>>,
}
impl VirtualPort {
pub fn open_loopback(baud_rate: u32, buffer_capacity: usize) -> Result<Self> {
let buffer = Arc::new((
Mutex::new(VecDeque::with_capacity(buffer_capacity)),
Condvar::new(),
Condvar::new(),
));
let rts_cts = Arc::new(Mutex::new(true));
let dtr_dsr_cd = Arc::new(Mutex::new(true));
Ok(Self {
config: Arc::new(Mutex::new(Config::new(baud_rate))),
paired_port_config: None,
read_buffer: buffer.clone(),
write_buffer: buffer.clone(),
rts: rts_cts.clone(),
cts: rts_cts.clone(),
dtr: dtr_dsr_cd.clone(),
dsr_cd: dtr_dsr_cd.clone(),
})
}
pub fn open_pair(baud_rate: u32, buffer_capacity: usize) -> Result<(Self, Self)> {
let read_buffer = Arc::new((
Mutex::new(VecDeque::with_capacity(buffer_capacity)),
Condvar::new(),
Condvar::new(),
));
let write_buffer = Arc::new((
Mutex::new(VecDeque::with_capacity(buffer_capacity)),
Condvar::new(),
Condvar::new(),
));
let rts = Arc::new(Mutex::new(true));
let cts = Arc::new(Mutex::new(true));
let dtr = Arc::new(Mutex::new(true));
let dsr_cd = Arc::new(Mutex::new(true));
let mut port1 = Self {
config: Arc::new(Mutex::new(Config::new(baud_rate))),
paired_port_config: None,
read_buffer: read_buffer.clone(),
write_buffer: write_buffer.clone(),
rts: rts.clone(),
cts: cts.clone(),
dtr: dtr.clone(),
dsr_cd: dsr_cd.clone(),
};
let mut port2 = Self {
config: Arc::new(Mutex::new(Config::new(baud_rate))),
paired_port_config: None,
read_buffer: write_buffer.clone(),
write_buffer: read_buffer.clone(),
rts: cts.clone(),
cts: rts.clone(),
dtr: dsr_cd.clone(),
dsr_cd: dtr.clone(),
};
port1.paired_port_config = Some(port2.config.clone());
port2.paired_port_config = Some(port1.config.clone());
Ok((port1, port2))
}
pub fn simulate_delay(&self) -> bool {
self.config.lock().unwrap().simulate_delay
}
pub fn set_simulate_delay(&mut self, value: bool) {
self.config.lock().unwrap().simulate_delay = value;
}
pub fn noise_on_config_mismatch(&self) -> bool {
self.config.lock().unwrap().noise_on_config_mismatch
}
pub fn set_noise_on_config_mismatch(&mut self, value: bool) {
self.config.lock().unwrap().noise_on_config_mismatch = value;
}
}
impl io::Read for VirtualPort {
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
let (buffer_mutex, can_read, can_write) = &*self.read_buffer.clone();
let mut buffer_guard = buffer_mutex.lock().unwrap();
if buffer_guard.is_empty() {
let (new_buffer_guard, timeout_result) =
can_read.wait_timeout(buffer_guard, self.timeout()).unwrap();
if timeout_result.timed_out() {
return Err(io::Error::from(io::ErrorKind::TimedOut));
}
buffer_guard = new_buffer_guard;
}
let bytes_to_read = buf.len().min(buffer_guard.len());
for byte in buf.iter_mut().take(bytes_to_read) {
*byte = buffer_guard.pop_front().unwrap();
}
let (noise_required, delay_per_byte) = {
let config = self.config.lock().unwrap();
let noise_required = if config.noise_on_config_mismatch {
if let Some(paired_port_config) = &self.paired_port_config {
let paired_config = paired_port_config.lock().unwrap();
config.physical_settings_mismatch(&paired_config)
} else {
false
}
} else {
false
};
let delay_per_byte = config.byte_duration();
(noise_required, delay_per_byte)
};
if noise_required {
let mut rng = rand::thread_rng();
buf.iter_mut()
.take(bytes_to_read)
.for_each(|byte| *byte = rng.gen());
}
can_write.notify_one();
if let Some(delay) = delay_per_byte {
std::thread::sleep(delay * bytes_to_read as u32);
}
Ok(bytes_to_read)
}
}
impl io::Write for VirtualPort {
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
let (buffer_mutex, can_read, can_write) = &*self.write_buffer;
let mut buffer_guard = buffer_mutex.lock().unwrap();
if buffer_guard.capacity() - buffer_guard.len() == 0 {
let (new_buffer_guard, timeout_result) = can_write
.wait_timeout(buffer_guard, self.timeout())
.unwrap();
if timeout_result.timed_out() {
return Err(io::Error::from(io::ErrorKind::TimedOut));
}
buffer_guard = new_buffer_guard;
}
let bytes_to_write = buf.len().min(buffer_guard.capacity() - buffer_guard.len());
buffer_guard.extend(&buf[0..bytes_to_write]);
can_read.notify_one();
Ok(bytes_to_write)
}
fn flush(&mut self) -> io::Result<()> {
let (buffer_mutex, _, can_write) = &*self.write_buffer;
let mut buffer_guard = buffer_mutex.lock().unwrap();
while buffer_guard.len() > 0 {
buffer_guard = can_write.wait(buffer_guard).unwrap();
}
Ok(())
}
}
impl SerialPort for VirtualPort {
fn name(&self) -> Option<String> {
None
}
fn baud_rate(&self) -> Result<u32> {
Ok(self.config.lock().unwrap().baud_rate)
}
fn data_bits(&self) -> Result<DataBits> {
Ok(self.config.lock().unwrap().data_bits)
}
fn flow_control(&self) -> Result<FlowControl> {
Ok(self.config.lock().unwrap().flow_control)
}
fn parity(&self) -> Result<Parity> {
Ok(self.config.lock().unwrap().parity)
}
fn stop_bits(&self) -> Result<StopBits> {
Ok(self.config.lock().unwrap().stop_bits)
}
fn timeout(&self) -> Duration {
self.config.lock().unwrap().timeout
}
fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
self.config.lock().unwrap().baud_rate = baud_rate;
Ok(())
}
fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()> {
self.config.lock().unwrap().flow_control = flow_control;
Ok(())
}
fn set_parity(&mut self, parity: Parity) -> Result<()> {
self.config.lock().unwrap().parity = parity;
Ok(())
}
fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()> {
self.config.lock().unwrap().data_bits = data_bits;
Ok(())
}
fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()> {
self.config.lock().unwrap().stop_bits = stop_bits;
Ok(())
}
fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
self.config.lock().unwrap().timeout = timeout;
Ok(())
}
fn write_request_to_send(&mut self, level: bool) -> Result<()> {
*self.rts.lock().unwrap() = level;
Ok(())
}
fn write_data_terminal_ready(&mut self, level: bool) -> Result<()> {
*self.dtr.lock().unwrap() = level;
Ok(())
}
fn read_clear_to_send(&mut self) -> Result<bool> {
Ok(*self.cts.lock().unwrap())
}
fn read_data_set_ready(&mut self) -> Result<bool> {
Ok(*self.dsr_cd.lock().unwrap())
}
fn read_ring_indicator(&mut self) -> Result<bool> {
Ok(false)
}
fn read_carrier_detect(&mut self) -> Result<bool> {
Ok(*self.dsr_cd.lock().unwrap())
}
fn bytes_to_read(&self) -> Result<u32> {
u32::try_from(self.read_buffer.0.lock().unwrap().len())
.map_err(|_| Error::new(ErrorKind::Unknown, "buffer is too large"))
}
fn bytes_to_write(&self) -> Result<u32> {
u32::try_from(self.write_buffer.0.lock().unwrap().len())
.map_err(|_| Error::new(ErrorKind::Unknown, "buffer is too large"))
}
fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()> {
let (read_buffer, write_buffer) = if Arc::ptr_eq(&self.read_buffer, &self.write_buffer) {
(None, Some(&*self.write_buffer))
} else {
match buffer_to_clear {
ClearBuffer::Input => (Some(&*self.read_buffer), None),
ClearBuffer::Output => (None, Some(&*self.write_buffer)),
ClearBuffer::All => (Some(&*self.read_buffer), Some(&*self.write_buffer)),
}
};
if let Some(buffer) = read_buffer {
let (buffer_mutex, _, _) = buffer;
buffer_mutex.lock().unwrap().clear()
}
if let Some(buffer) = write_buffer {
let (buffer_mutex, _, can_write) = buffer;
buffer_mutex.lock().unwrap().clear();
can_write.notify_one();
}
Ok(())
}
fn try_clone(&self) -> Result<Box<dyn SerialPort>> {
Ok(Box::new(self.clone()))
}
fn set_break(&self) -> Result<()> {
Ok(())
}
fn clear_break(&self) -> Result<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use std::io::{Read, Write};
use super::*;
#[test]
fn test_loopback() {
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
let write_data = b"hello";
let mut read_data = [0u8; 5];
port.write_all(write_data).unwrap();
port.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, write_data);
}
#[test]
fn test_open_pair() {
let (mut port1, mut port2) = VirtualPort::open_pair(9600, 1024).unwrap();
let write_data = b"hello";
let mut read_data = [0u8; 5];
port1.write_all(write_data).unwrap();
port2.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, write_data);
}
#[test]
fn test_timeout() {
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
port.set_timeout(Duration::from_millis(100)).unwrap();
let mut read_data = [0u8; 5];
assert_eq!(
port.read_exact(&mut read_data).unwrap_err().kind(),
io::ErrorKind::TimedOut
);
}
#[test]
fn test_control_lines() {
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
port.write_request_to_send(true).unwrap();
assert!(port.read_clear_to_send().unwrap());
port.write_data_terminal_ready(true).unwrap();
assert!(port.read_data_set_ready().unwrap());
port.write_request_to_send(false).unwrap();
assert!(!port.read_clear_to_send().unwrap());
port.write_data_terminal_ready(false).unwrap();
assert!(!port.read_data_set_ready().unwrap());
}
#[test]
fn test_buffer_clearing() {
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
port.set_timeout(Duration::from_millis(100)).unwrap();
let write_data = b"test";
let mut read_data = [0u8; 4];
port.write_all(write_data).unwrap();
port.clear(ClearBuffer::All).unwrap();
assert_eq!(
port.read_exact(&mut read_data).unwrap_err().kind(),
io::ErrorKind::TimedOut
);
}
#[test]
fn test_clone() {
let port = VirtualPort::open_loopback(9600, 1024).unwrap();
let port_clone = port.try_clone().unwrap();
assert_eq!(port.baud_rate().unwrap(), port_clone.baud_rate().unwrap());
}
#[test]
fn test_multiple_threads() {
use std::{thread, time};
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
let mut port_clone = port.try_clone().unwrap();
let writer = thread::spawn(move || {
let write_data = b"hello";
port.write_all(write_data).unwrap();
});
let reader = thread::spawn(move || {
let mut read_data = [0u8; 5];
thread::sleep(time::Duration::from_millis(100));
port_clone.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, b"hello");
});
writer.join().unwrap();
reader.join().unwrap();
}
#[test]
fn test_config_change() {
let mut port = VirtualPort::open_loopback(9600, 1024).unwrap();
port.set_baud_rate(19200).unwrap();
assert_eq!(port.baud_rate().unwrap(), 19200);
port.set_data_bits(DataBits::Seven).unwrap();
assert_eq!(port.data_bits().unwrap(), DataBits::Seven);
port.set_flow_control(FlowControl::Software).unwrap();
assert_eq!(port.flow_control().unwrap(), FlowControl::Software);
port.set_parity(Parity::Odd).unwrap();
assert_eq!(port.parity().unwrap(), Parity::Odd);
port.set_stop_bits(StopBits::Two).unwrap();
assert_eq!(port.stop_bits().unwrap(), StopBits::Two);
}
#[test]
fn test_delay_simulation() {
use std::time::Instant;
let mut port = VirtualPort::open_loopback(50, 1024).unwrap();
assert!(!port.simulate_delay());
port.set_simulate_delay(true);
assert!(port.simulate_delay());
let write_data = b"hello";
port.write_all(write_data).unwrap();
let mut read_data = [0u8; 5];
let start = Instant::now();
port.read_exact(&mut read_data).unwrap();
let duration = start.elapsed();
assert_eq!(&read_data, write_data);
assert!(duration.as_millis() > 700);
}
#[test]
fn test_noise_on_config_mismatch() {
let (mut port1, mut port2) = VirtualPort::open_pair(9600, 1024).unwrap();
assert!(!port1.noise_on_config_mismatch());
assert!(!port2.noise_on_config_mismatch());
let write_data = b"hello world";
let mut read_data = [0u8; 11];
port1.write_all(write_data).unwrap();
read_data.fill(0);
port2.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, write_data);
port2.set_baud_rate(19200).unwrap();
port1.write_all(write_data).unwrap();
read_data.fill(0);
port2.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, write_data);
port2.set_noise_on_config_mismatch(true);
assert!(!port1.noise_on_config_mismatch());
assert!(port2.noise_on_config_mismatch());
port2.set_baud_rate(port1.baud_rate().unwrap()).unwrap();
port1.write_all(write_data).unwrap();
read_data.fill(0);
port2.read_exact(&mut read_data).unwrap();
assert_eq!(&read_data, write_data);
port2.set_baud_rate(19200).unwrap();
port1.write_all(write_data).unwrap();
read_data.fill(0);
port2.read_exact(&mut read_data).unwrap();
assert_ne!(&read_data, write_data);
assert!(read_data.iter().any(|&byte| byte != 0));
}
}