virtual_serialport/
lib.rs

1//! # Virtual Serial Port
2//!
3//! The Serial Port Simulator (virtual port) is designed to work alongside the
4//! [`serialport`](https://crates.io/crates/serialport) crate. It supports
5//! reading from and writing to the port using internal buffers, with optional
6//! timeout functionality.
7//!
8//! The simulator also allows configuring standard serial port parameters, such as:
9//!
10//! - baud rate
11//! - data bits
12//! - parity
13//! - stop bits
14//! - flow control
15//!
16//! Additional features include:
17//!
18//! - **Control Signal Simulation**: Simulates control signals (RTS/CTS,
19//!   DTR/DSR/CD). Note that actual flow control based on these signals is not
20//!   implemented.
21//!
22//! - **Transmission Delay Simulation**: When enabled, simulates transmission delay
23//!   based on the baud rate. This is implemented in a simplified manner by adding
24//!   a fixed delay for each symbol read (the delay is calculated according to the
25//!   baud rate).
26//!
27//! - **Noise Simulation**: If enabled, simulates noise when the physical settings
28//!   (baud rate, data bits, parity, and stop bits) of paired ports do not match.
29//!   This helps test how the system handles corrupted or invalid data under
30//!   mismatched configurations.
31//!
32//! ## Example Usage
33//!
34//! ### Loopback Example
35//! ```
36//! use std::io::{Read, Write};
37//!
38//! use virtual_serialport::VirtualPort;
39//!
40//! let mut port = VirtualPort::loopback(9600, 1024).unwrap();
41//! let write_data = b"hello";
42//! let mut read_data = [0u8; 5];
43//!
44//! port.write_all(write_data).unwrap();
45//! port.read_exact(&mut read_data).unwrap();
46//! assert_eq!(&read_data, write_data);
47//! ```
48//!
49//! ### Pair Example
50//! ```
51//! use std::io::{Read, Write};
52//!
53//! use virtual_serialport::VirtualPort;
54//!
55//! let (mut port1, mut port2) = VirtualPort::pair(9600, 1024).unwrap();
56//! let write_data = b"hello";
57//! let mut read_data = [0u8; 5];
58//!
59//! port1.write_all(write_data).unwrap();
60//! port2.read_exact(&mut read_data).unwrap();
61//! assert_eq!(&read_data, write_data);
62//! ```
63
64// To run doc tests on examples from README.md and verify their correctness
65#[cfg(doctest)]
66#[doc = include_str!("../README.md")]
67struct ReadMe;
68
69use std::{
70    io,
71    sync::{Arc, Mutex},
72    time::Duration,
73};
74
75use rand::Rng;
76
77use serialport::{ClearBuffer, DataBits, FlowControl, Parity, Result, SerialPort, StopBits};
78
79use mockpipe::MockPipe;
80
81struct Config {
82    // Baud rate in symbols per second
83    baud_rate: u32,
84
85    // Number of bits per character
86    data_bits: DataBits,
87
88    // Flow control mode
89    flow_control: FlowControl,
90
91    // Parity checking mode
92    parity: Parity,
93
94    // Number of stop bits
95    stop_bits: StopBits,
96
97    // Whether to simulate the delay of data transmission based on baud rate.
98    // If enabled, this will add a fixed delay for each symbol read to simulate
99    // the transmission delay. Note that this is a simplified simulation: in a real
100    // serial communication, transmission would continue even when read operations
101    // are not performed, allowing some data to be available immediately when
102    // a read is executed. This simulation does not account for such behavior and
103    // only introduces a delay per symbol as if transmission was paused during reads
104    simulate_delay: bool,
105
106    // Whether to simulate corrupted symbols if physical settings don't match
107    noise_on_config_mismatch: bool,
108}
109
110impl Config {
111    fn new(baud_rate: u32) -> Self {
112        Self {
113            baud_rate,
114            data_bits: DataBits::Eight,
115            flow_control: FlowControl::None,
116            parity: Parity::None,
117            stop_bits: StopBits::One,
118            simulate_delay: false,
119            noise_on_config_mismatch: false,
120        }
121    }
122
123    // Calculates the total number of bits per byte based on the current configuration.
124    // This includes:
125    // - 1 start bit (always present)
126    // - `data_bits` (5 to 8 data bits depending on configuration)
127    // - Optional parity bit (1 bit if parity is `Odd` or `Even`, 0 bits if `None`)
128    // - `stop_bits` (1 or 2 bits depending on configuration)
129    fn bits_per_byte(&self) -> u32 {
130        // 1 start bit + data bits + parity bit (if any) + stop bits
131        1 + match self.data_bits {
132            DataBits::Five => 5,
133            DataBits::Six => 6,
134            DataBits::Seven => 7,
135            DataBits::Eight => 8,
136        } + match self.parity {
137            Parity::Odd | Parity::Even => 1,
138            Parity::None => 0,
139        } + match self.stop_bits {
140            StopBits::One => 1,
141            StopBits::Two => 2,
142        }
143    }
144
145    // Calculates the time to transmit one byte in microseconds.
146    fn byte_duration(&self) -> Option<Duration> {
147        self.simulate_delay.then(|| {
148            Duration::from_micros(((1_000_000 / self.baud_rate) * self.bits_per_byte()) as u64)
149        })
150    }
151
152    /// Compares relevant physical settings between two configs.
153    /// Returns `true` if they don't match, `false` otherwise.
154    fn physical_settings_mismatch(&self, other: &Config) -> bool {
155        self.baud_rate != other.baud_rate
156            || self.data_bits != other.data_bits
157            || self.parity != other.parity
158            || self.stop_bits != other.stop_bits
159    }
160}
161
162/// `VirtualPort` simulates a serial port for testing purposes. It supports
163/// setting various serial port parameters like baud rate, data bits, flow control,
164/// parity, and stop bits. It also supports reading from and writing to buffers.
165///
166/// Port pair wiring diagram:
167///
168///  Port 1            Port 2
169/// ┌─────┐           ┌─────┐
170/// │ TXD ├──────────▶│ RXD │
171/// │ RXD │◂──────────┤ TXD │
172/// │ RTS ├──────────▶│ CTS │
173/// │ CTS │◂──────────┤ RTS │
174/// │ DTR ├─────────┬▶│ DSR │
175/// │     │         └▶│ CD  │
176/// │ DSR │◂┬─────────┤ DTR │
177/// │ CD  │◂┘         │     │
178/// │ RI  ├───────────┤ RI  │
179/// └─────┘           └─────┘
180#[derive(Clone)]
181pub struct VirtualPort {
182    // Configuration settings (baud rate, data bits etc.)
183    config: Arc<Mutex<Config>>,
184
185    // Reference to the paired port's configuration
186    paired_port_config: Option<Arc<Mutex<Config>>>,
187
188    pipe: MockPipe,
189
190    // Control lines (RTS<-->CTS, DTR<-->DSR/CD)
191    // RI (ring indicator) is always true in this implementation
192    rts: Arc<Mutex<bool>>,
193    cts: Arc<Mutex<bool>>,
194    dtr: Arc<Mutex<bool>>,
195    dsr_cd: Arc<Mutex<bool>>,
196}
197
198impl VirtualPort {
199    /// Opens a single loopback virtual port with the specified baud rate.
200    pub fn loopback(baud_rate: u32, buffer_capacity: u32) -> Result<Self> {
201        let rts_cts = Arc::new(Mutex::new(true));
202        let dtr_dsr_cd = Arc::new(Mutex::new(true));
203
204        Ok(Self {
205            config: Arc::new(Mutex::new(Config::new(baud_rate))),
206            paired_port_config: None,
207
208            pipe: MockPipe::loopback(buffer_capacity as usize),
209
210            rts: rts_cts.clone(),
211            cts: rts_cts.clone(),
212            dtr: dtr_dsr_cd.clone(),
213            dsr_cd: dtr_dsr_cd.clone(),
214        })
215    }
216
217    /// Opens a pair of connected virtual ports with the specified baud rate.
218    /// These ports can simulate a communication between two devices.
219    pub fn pair(baud_rate: u32, buffer_capacity: u32) -> Result<(Self, Self)> {
220        let config1 = Arc::new(Mutex::new(Config::new(baud_rate)));
221        let config2 = Arc::new(Mutex::new(Config::new(baud_rate)));
222
223        let (pipe1, pipe2) = MockPipe::pair(buffer_capacity as usize);
224
225        let rts = Arc::new(Mutex::new(true));
226        let cts = Arc::new(Mutex::new(true));
227        let dtr = Arc::new(Mutex::new(true));
228        let dsr_cd = Arc::new(Mutex::new(true));
229
230        let port1 = Self {
231            config: config1.clone(),
232            paired_port_config: Some(config2.clone()),
233
234            pipe: pipe1,
235
236            rts: rts.clone(),
237            cts: cts.clone(),
238            dtr: dtr.clone(),
239            dsr_cd: dsr_cd.clone(),
240        };
241
242        let port2 = Self {
243            config: config2,
244            paired_port_config: Some(config1),
245
246            pipe: pipe2,
247
248            rts: cts.clone(),
249            cts: rts.clone(),
250            dtr: dsr_cd.clone(),
251            dsr_cd: dtr.clone(),
252        };
253
254        Ok((port1, port2))
255    }
256
257    /// Boxes the instance as a `SerialPort`.
258    pub fn into_boxed(self) -> Box<dyn SerialPort> {
259        Box::new(self)
260    }
261
262    /// Returns whether transmission delay simulation is enabled.
263    pub fn simulate_delay(&self) -> bool {
264        self.config.lock().unwrap().simulate_delay
265    }
266
267    /// Sets whether to simulate the transmission delay for reading operations.
268    pub fn set_simulate_delay(&mut self, value: bool) {
269        self.config.lock().unwrap().simulate_delay = value;
270    }
271
272    /// Returns whether to simulate corrupted symbols if physical settings don't match.
273    pub fn noise_on_config_mismatch(&self) -> bool {
274        self.config.lock().unwrap().noise_on_config_mismatch
275    }
276
277    /// Sets whether to simulate corrupted symbols if physical settings don't match.
278    pub fn set_noise_on_config_mismatch(&mut self, value: bool) {
279        self.config.lock().unwrap().noise_on_config_mismatch = value;
280    }
281}
282
283impl io::Read for VirtualPort {
284    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
285        let bytes_to_read = self.pipe.read(buf)?;
286
287        // Lock the configuration once and get necessary parameters
288        let (noise_required, delay_per_byte) = {
289            let config = self.config.lock().unwrap();
290
291            // Determine if noise simulation is needed
292            let noise_required = if config.noise_on_config_mismatch {
293                if let Some(paired_port_config) = &self.paired_port_config {
294                    let paired_config = paired_port_config.lock().unwrap();
295                    config.physical_settings_mismatch(&paired_config)
296                } else {
297                    false
298                }
299            } else {
300                false
301            };
302
303            // Get the delay per byte
304            let delay_per_byte = config.byte_duration();
305
306            (noise_required, delay_per_byte)
307        };
308
309        // Fill the buffer with noise if required
310        if noise_required {
311            let mut rng = rand::thread_rng();
312            buf.iter_mut()
313                .take(bytes_to_read)
314                .for_each(|byte| *byte = rng.gen());
315        }
316
317        // Simulate the delay of data transmission based on baud rate
318        if let Some(delay) = delay_per_byte {
319            std::thread::sleep(delay * bytes_to_read as u32);
320        }
321
322        Ok(bytes_to_read)
323    }
324}
325
326impl io::Write for VirtualPort {
327    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
328        self.pipe.write(buf)
329    }
330
331    fn flush(&mut self) -> io::Result<()> {
332        self.pipe.flush()
333    }
334}
335
336impl SerialPort for VirtualPort {
337    fn name(&self) -> Option<String> {
338        None
339    }
340
341    fn baud_rate(&self) -> Result<u32> {
342        Ok(self.config.lock().unwrap().baud_rate)
343    }
344
345    fn data_bits(&self) -> Result<DataBits> {
346        Ok(self.config.lock().unwrap().data_bits)
347    }
348
349    fn flow_control(&self) -> Result<FlowControl> {
350        Ok(self.config.lock().unwrap().flow_control)
351    }
352
353    fn parity(&self) -> Result<Parity> {
354        Ok(self.config.lock().unwrap().parity)
355    }
356
357    fn stop_bits(&self) -> Result<StopBits> {
358        Ok(self.config.lock().unwrap().stop_bits)
359    }
360
361    fn timeout(&self) -> Duration {
362        self.pipe.timeout().unwrap_or(Duration::MAX)
363    }
364
365    fn set_baud_rate(&mut self, baud_rate: u32) -> Result<()> {
366        self.config.lock().unwrap().baud_rate = baud_rate;
367        Ok(())
368    }
369
370    fn set_flow_control(&mut self, flow_control: FlowControl) -> Result<()> {
371        self.config.lock().unwrap().flow_control = flow_control;
372        Ok(())
373    }
374
375    fn set_parity(&mut self, parity: Parity) -> Result<()> {
376        self.config.lock().unwrap().parity = parity;
377        Ok(())
378    }
379
380    fn set_data_bits(&mut self, data_bits: DataBits) -> Result<()> {
381        self.config.lock().unwrap().data_bits = data_bits;
382        Ok(())
383    }
384
385    fn set_stop_bits(&mut self, stop_bits: StopBits) -> Result<()> {
386        self.config.lock().unwrap().stop_bits = stop_bits;
387        Ok(())
388    }
389
390    fn set_timeout(&mut self, timeout: Duration) -> Result<()> {
391        self.pipe.set_timeout(match timeout {
392            Duration::MAX => None,
393            duration => Some(duration),
394        });
395        Ok(())
396    }
397
398    fn write_request_to_send(&mut self, level: bool) -> Result<()> {
399        *self.rts.lock().unwrap() = level;
400        Ok(())
401    }
402
403    fn write_data_terminal_ready(&mut self, level: bool) -> Result<()> {
404        *self.dtr.lock().unwrap() = level;
405        Ok(())
406    }
407
408    fn read_clear_to_send(&mut self) -> Result<bool> {
409        Ok(*self.cts.lock().unwrap())
410    }
411
412    fn read_data_set_ready(&mut self) -> Result<bool> {
413        Ok(*self.dsr_cd.lock().unwrap())
414    }
415
416    fn read_ring_indicator(&mut self) -> Result<bool> {
417        Ok(false)
418    }
419
420    fn read_carrier_detect(&mut self) -> Result<bool> {
421        Ok(*self.dsr_cd.lock().unwrap())
422    }
423
424    fn bytes_to_read(&self) -> Result<u32> {
425        // The `buffer_capacity` argument in the constructor methods of `VirtualPort`
426        // is limited to u32, ensuring that the number of bytes in the buffers never
427        // exceeds u32. Therefore, we can safely unwrap the result of `try_from`.
428        Ok(u32::try_from(self.pipe.read_buffer_len()).unwrap())
429    }
430
431    fn bytes_to_write(&self) -> Result<u32> {
432        // Safe to unwrap: see comment in `bytes_to_read`.
433        Ok(u32::try_from(self.pipe.write_buffer_len()).unwrap())
434    }
435
436    fn clear(&self, buffer_to_clear: ClearBuffer) -> Result<()> {
437        match buffer_to_clear {
438            ClearBuffer::Input => self.pipe.clear_read(),
439            ClearBuffer::Output => self.pipe.clear_write(),
440            ClearBuffer::All => self.pipe.clear(),
441        }
442        Ok(())
443    }
444
445    fn try_clone(&self) -> Result<Box<dyn SerialPort>> {
446        Ok(Box::new(self.clone()))
447    }
448
449    fn set_break(&self) -> Result<()> {
450        Ok(())
451    }
452
453    fn clear_break(&self) -> Result<()> {
454        Ok(())
455    }
456}
457
458#[cfg(test)]
459mod tests {
460    use std::io::{Read, Write};
461
462    use super::*;
463
464    #[test]
465    fn test_control_lines() {
466        let mut port = VirtualPort::loopback(9600, 1024).unwrap();
467
468        port.write_request_to_send(true).unwrap();
469        assert!(port.read_clear_to_send().unwrap());
470
471        port.write_data_terminal_ready(true).unwrap();
472        assert!(port.read_data_set_ready().unwrap());
473
474        port.write_request_to_send(false).unwrap();
475        assert!(!port.read_clear_to_send().unwrap());
476
477        port.write_data_terminal_ready(false).unwrap();
478        assert!(!port.read_data_set_ready().unwrap());
479    }
480
481    #[test]
482    fn test_buffer_clearing() {
483        let mut port = VirtualPort::loopback(9600, 1024).unwrap();
484        port.set_timeout(Duration::from_millis(100)).unwrap();
485        let write_data = b"test";
486        let mut read_data = [0u8; 4];
487
488        port.write_all(write_data).unwrap();
489        port.clear(ClearBuffer::All).unwrap();
490
491        assert_eq!(
492            port.read_exact(&mut read_data).unwrap_err().kind(),
493            io::ErrorKind::TimedOut
494        );
495    }
496
497    #[test]
498    fn test_clone() {
499        let port = VirtualPort::loopback(9600, 1024).unwrap();
500        let port_clone = port.try_clone().unwrap();
501
502        assert_eq!(port.baud_rate().unwrap(), port_clone.baud_rate().unwrap());
503    }
504
505    #[test]
506    fn test_config_change() {
507        let mut port = VirtualPort::loopback(9600, 1024).unwrap();
508
509        port.set_baud_rate(19200).unwrap();
510        assert_eq!(port.baud_rate().unwrap(), 19200);
511
512        port.set_data_bits(DataBits::Seven).unwrap();
513        assert_eq!(port.data_bits().unwrap(), DataBits::Seven);
514
515        port.set_flow_control(FlowControl::Software).unwrap();
516        assert_eq!(port.flow_control().unwrap(), FlowControl::Software);
517
518        port.set_parity(Parity::Odd).unwrap();
519        assert_eq!(port.parity().unwrap(), Parity::Odd);
520
521        port.set_stop_bits(StopBits::Two).unwrap();
522        assert_eq!(port.stop_bits().unwrap(), StopBits::Two);
523    }
524
525    #[test]
526    fn test_delay_simulation() {
527        use std::time::Instant;
528
529        let mut port = VirtualPort::loopback(50, 1024).unwrap();
530
531        // Initially, simulate_delay should be false by default
532        assert!(!port.simulate_delay());
533
534        // Enable simulation delay
535        port.set_simulate_delay(true);
536        assert!(port.simulate_delay());
537
538        // Write data to the port
539        // (for 5 symbols the transmission time is about 1 second for 50 baud rate)
540        let write_data = b"hello";
541        port.write_all(write_data).unwrap();
542
543        // Read data from the port and measure duration
544        let mut read_data = [0u8; 5];
545        let start = Instant::now();
546        port.read_exact(&mut read_data).unwrap();
547        let duration = start.elapsed();
548
549        assert_eq!(&read_data, write_data);
550        assert!(duration.as_millis() > 700);
551    }
552
553    #[test]
554    fn test_noise_on_config_mismatch() {
555        let (mut port1, mut port2) = VirtualPort::pair(9600, 1024).unwrap();
556
557        // Initially, noise simulation should be disabled by default
558        assert!(!port1.noise_on_config_mismatch());
559        assert!(!port2.noise_on_config_mismatch());
560
561        let write_data = b"hello world";
562        let mut read_data = [0u8; 11];
563
564        // Case 1: Verify data transfer when configurations match (noise simulation is not enabled)
565
566        // Write data to port1
567        port1.write_all(write_data).unwrap();
568
569        // Read data from port2
570        read_data.fill(0);
571        port2.read_exact(&mut read_data).unwrap();
572
573        // Ensure the data in the buffers are equal
574        assert_eq!(&read_data, write_data);
575
576        // Case 2: Verify behavior when configurations mismatch (noise simulation is not enabled)
577
578        // Set baud rate to a different value to mismatch configs
579        port2.set_baud_rate(19200).unwrap();
580
581        // Write data to port1
582        port1.write_all(write_data).unwrap();
583
584        // Read data from port2
585        read_data.fill(0);
586        port2.read_exact(&mut read_data).unwrap();
587
588        // Ensure the data in the buffers are equal
589        assert_eq!(&read_data, write_data);
590
591        // Case 3: Verify noise simulation when configs match again (noise simulation is enabled)
592
593        // Enable noise simulation for port2
594        port2.set_noise_on_config_mismatch(true);
595        assert!(!port1.noise_on_config_mismatch());
596        assert!(port2.noise_on_config_mismatch());
597
598        // Set baud rate to the original value to match configs
599        port2.set_baud_rate(port1.baud_rate().unwrap()).unwrap();
600
601        // Write data to port1
602        port1.write_all(write_data).unwrap();
603
604        // Read data from port2
605        read_data.fill(0);
606        port2.read_exact(&mut read_data).unwrap();
607
608        // Ensure the data in the buffers are equal
609        assert_eq!(&read_data, write_data);
610
611        // Case 4: Verify noise simulation when configs mismatch again (noise simulation is enabled)
612
613        // Set baud rate to a different value to mismatch configs
614        port2.set_baud_rate(19200).unwrap();
615
616        // Write data to port1
617        port1.write_all(write_data).unwrap();
618
619        // Read data from port2
620        read_data.fill(0);
621        port2.read_exact(&mut read_data).unwrap();
622
623        // Ensure the buffer differs and contains random data (simple test to check non-zero bytes)
624        assert_ne!(&read_data, write_data);
625        assert!(read_data.iter().any(|&byte| byte != 0));
626    }
627}