1use std::path::{Path, PathBuf};
41use std::time::Duration;
42
43use serde::{Deserialize, Serialize};
44use thiserror::Error;
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
48#[serde(rename_all = "lowercase")]
49pub enum Parity {
50 #[default]
52 None,
53 Even,
55 Odd,
57 Mark,
59 Space,
61}
62
63impl Parity {
64 pub fn bits(&self) -> u32 {
66 match self {
67 Parity::None => 0,
68 _ => 1,
69 }
70 }
71}
72
73#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
75#[serde(rename_all = "lowercase")]
76pub enum StopBits {
77 #[default]
79 One,
80 OnePointFive,
82 Two,
84}
85
86impl StopBits {
87 pub fn bits(&self) -> f32 {
89 match self {
90 StopBits::One => 1.0,
91 StopBits::OnePointFive => 1.5,
92 StopBits::Two => 2.0,
93 }
94 }
95}
96
97#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
99#[serde(rename_all = "lowercase")]
100pub enum DataBits {
101 Five = 5,
102 Six = 6,
103 Seven = 7,
104 Eight = 8,
105}
106
107impl Default for DataBits {
108 fn default() -> Self {
109 Self::Eight
110 }
111}
112
113impl DataBits {
114 pub fn bits(&self) -> u32 {
116 *self as u32
117 }
118}
119
120#[derive(Debug, Clone, Serialize, Deserialize)]
122pub struct SerialConfig {
123 pub baud_rate: u32,
125
126 pub data_bits: DataBits,
128
129 pub parity: Parity,
131
132 pub stop_bits: StopBits,
134
135 pub flow_control: bool,
137}
138
139impl Default for SerialConfig {
140 fn default() -> Self {
141 Self {
142 baud_rate: 9600,
143 data_bits: DataBits::Eight,
144 parity: Parity::None,
145 stop_bits: StopBits::One,
146 flow_control: false,
147 }
148 }
149}
150
151impl SerialConfig {
152 pub fn new(baud_rate: u32) -> Self {
154 Self {
155 baud_rate,
156 ..Default::default()
157 }
158 }
159
160 pub fn baud_9600() -> Self {
162 Self::new(9600)
163 }
164
165 pub fn baud_19200() -> Self {
167 Self::new(19200)
168 }
169
170 pub fn baud_38400() -> Self {
172 Self::new(38400)
173 }
174
175 pub fn baud_115200() -> Self {
177 Self::new(115200)
178 }
179
180 pub fn with_parity(mut self, parity: Parity) -> Self {
182 self.parity = parity;
183 self
184 }
185
186 pub fn with_stop_bits(mut self, stop_bits: StopBits) -> Self {
188 self.stop_bits = stop_bits;
189 self
190 }
191
192 pub fn with_data_bits(mut self, data_bits: DataBits) -> Self {
194 self.data_bits = data_bits;
195 self
196 }
197
198 pub fn bits_per_character(&self) -> f32 {
200 1.0 + self.data_bits.bits() as f32
202 + self.parity.bits() as f32
203 + self.stop_bits.bits()
204 }
205
206 pub fn char_time(&self) -> Duration {
208 let bits = self.bits_per_character();
209 let seconds = bits / self.baud_rate as f32;
210 Duration::from_secs_f32(seconds)
211 }
212
213 pub fn transmission_time(&self, bytes: usize) -> Duration {
215 self.char_time() * bytes as u32
216 }
217
218 pub fn inter_frame_timeout(&self) -> Duration {
220 if self.baud_rate > 19200 {
222 Duration::from_micros(1750)
223 } else {
224 let char_time = self.char_time();
225 Duration::from_secs_f32(char_time.as_secs_f32() * 3.5)
226 }
227 }
228
229 pub fn inter_char_timeout(&self) -> Duration {
231 if self.baud_rate > 19200 {
233 Duration::from_micros(750)
234 } else {
235 let char_time = self.char_time();
236 Duration::from_secs_f32(char_time.as_secs_f32() * 1.5)
237 }
238 }
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct VirtualSerialConfig {
244 #[serde(flatten)]
246 pub serial: SerialConfig,
247
248 pub slave_path: PathBuf,
250
251 pub symlink_path: Option<PathBuf>,
253
254 pub simulate_delays: bool,
256
257 pub buffer_size: usize,
259}
260
261impl Default for VirtualSerialConfig {
262 fn default() -> Self {
263 Self {
264 serial: SerialConfig::default(),
265 slave_path: PathBuf::from("/tmp/modbus_rtu_slave"),
266 symlink_path: None,
267 simulate_delays: true,
268 buffer_size: 4096,
269 }
270 }
271}
272
273impl VirtualSerialConfig {
274 pub fn new<P: AsRef<Path>>(slave_path: P) -> Self {
276 Self {
277 slave_path: slave_path.as_ref().to_path_buf(),
278 ..Default::default()
279 }
280 }
281
282 pub fn with_baud_rate(mut self, baud_rate: u32) -> Self {
284 self.serial.baud_rate = baud_rate;
285 self
286 }
287
288 pub fn with_slave_path<P: AsRef<Path>>(mut self, path: P) -> Self {
290 self.slave_path = path.as_ref().to_path_buf();
291 self
292 }
293
294 pub fn with_symlink<P: AsRef<Path>>(mut self, path: P) -> Self {
296 self.symlink_path = Some(path.as_ref().to_path_buf());
297 self
298 }
299
300 pub fn with_serial_config(mut self, config: SerialConfig) -> Self {
302 self.serial = config;
303 self
304 }
305
306 pub fn with_delay_simulation(mut self, enabled: bool) -> Self {
308 self.simulate_delays = enabled;
309 self
310 }
311
312 pub fn with_buffer_size(mut self, size: usize) -> Self {
314 self.buffer_size = size;
315 self
316 }
317}
318
319#[derive(Debug, Error)]
321pub enum VirtualSerialError {
322 #[error("I/O error: {0}")]
324 Io(#[from] std::io::Error),
325
326 #[error("Failed to create PTY: {0}")]
328 PtyCreation(String),
329
330 #[error("Failed to create symlink: {source}")]
332 Symlink {
333 path: PathBuf,
334 #[source]
335 source: std::io::Error,
336 },
337
338 #[error("Virtual serial port not available on this platform")]
340 NotAvailable,
341
342 #[error("Port already in use: {0}")]
344 InUse(PathBuf),
345}
346
347#[derive(Debug)]
351pub struct VirtualSerial {
352 config: VirtualSerialConfig,
354
355 inner: VirtualSerialInner,
357}
358
359#[cfg(unix)]
360#[derive(Debug)]
361struct VirtualSerialInner {
362 master_fd: std::os::unix::io::RawFd,
364
365 slave_device_path: PathBuf,
367
368 has_symlink: bool,
370}
371
372#[cfg(not(unix))]
373#[derive(Debug)]
374struct VirtualSerialInner {
375 _marker: std::marker::PhantomData<()>,
377}
378
379impl VirtualSerial {
380 #[cfg(unix)]
382 pub fn create(config: VirtualSerialConfig) -> Result<Self, VirtualSerialError> {
383 use std::os::unix::io::AsRawFd;
384
385 let master = nix::pty::posix_openpt(nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_NOCTTY)
387 .map_err(|e| VirtualSerialError::PtyCreation(e.to_string()))?;
388
389 nix::pty::grantpt(&master)
391 .map_err(|e| VirtualSerialError::PtyCreation(format!("grantpt failed: {}", e)))?;
392
393 nix::pty::unlockpt(&master)
395 .map_err(|e| VirtualSerialError::PtyCreation(format!("unlockpt failed: {}", e)))?;
396
397 let slave_name = unsafe {
399 nix::pty::ptsname(&master)
400 .map_err(|e| VirtualSerialError::PtyCreation(format!("ptsname failed: {}", e)))?
401 };
402
403 let slave_device_path = PathBuf::from(&slave_name);
404
405 let has_symlink = if let Some(ref symlink_path) = config.symlink_path {
407 let _ = std::fs::remove_file(symlink_path);
409
410 std::os::unix::fs::symlink(&slave_device_path, symlink_path).map_err(|e| {
411 VirtualSerialError::Symlink {
412 path: symlink_path.clone(),
413 source: e,
414 }
415 })?;
416 true
417 } else if config.slave_path != slave_device_path {
418 let _ = std::fs::remove_file(&config.slave_path);
420
421 std::os::unix::fs::symlink(&slave_device_path, &config.slave_path).map_err(|e| {
422 VirtualSerialError::Symlink {
423 path: config.slave_path.clone(),
424 source: e,
425 }
426 })?;
427 true
428 } else {
429 false
430 };
431
432 let inner = VirtualSerialInner {
433 master_fd: master.as_raw_fd(),
434 slave_device_path,
435 has_symlink,
436 };
437
438 std::mem::forget(master);
440
441 Ok(Self { config, inner })
442 }
443
444 #[cfg(not(unix))]
446 pub fn create(_config: VirtualSerialConfig) -> Result<Self, VirtualSerialError> {
447 Err(VirtualSerialError::NotAvailable)
448 }
449
450 pub fn slave_path(&self) -> &Path {
452 &self.config.slave_path
453 }
454
455 #[cfg(unix)]
457 pub fn device_path(&self) -> &Path {
458 &self.inner.slave_device_path
459 }
460
461 pub fn config(&self) -> &VirtualSerialConfig {
463 &self.config
464 }
465
466 pub fn serial_config(&self) -> &SerialConfig {
468 &self.config.serial
469 }
470
471 pub fn transmission_delay(&self, bytes: usize) -> Duration {
473 if self.config.simulate_delays {
474 self.config.serial.transmission_time(bytes)
475 } else {
476 Duration::ZERO
477 }
478 }
479
480 #[cfg(unix)]
482 pub fn master_fd(&self) -> std::os::unix::io::RawFd {
483 self.inner.master_fd
484 }
485
486 #[cfg(unix)]
488 pub fn into_async_io(self) -> std::io::Result<tokio::fs::File> {
489 use std::os::unix::io::FromRawFd;
490
491 let file = unsafe { std::fs::File::from_raw_fd(self.inner.master_fd) };
493
494 std::mem::forget(self);
496
497 Ok(tokio::fs::File::from_std(file))
499 }
500}
501
502#[cfg(unix)]
503impl Drop for VirtualSerial {
504 fn drop(&mut self) {
505 if self.inner.has_symlink {
507 if let Some(ref symlink_path) = self.config.symlink_path {
508 let _ = std::fs::remove_file(symlink_path);
509 } else {
510 let _ = std::fs::remove_file(&self.config.slave_path);
511 }
512 }
513
514 unsafe {
516 libc::close(self.inner.master_fd);
517 }
518 }
519}
520
521#[derive(Debug)]
526pub struct SerialPair {
527 server_tx: tokio::sync::mpsc::Sender<Vec<u8>>,
529 server_rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
530
531 client_tx: tokio::sync::mpsc::Sender<Vec<u8>>,
533 client_rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
534
535 config: SerialConfig,
537}
538
539impl SerialPair {
540 pub fn new(config: SerialConfig) -> (SerialPairServer, SerialPairClient) {
542 let (server_tx, client_rx) = tokio::sync::mpsc::channel(256);
543 let (client_tx, server_rx) = tokio::sync::mpsc::channel(256);
544
545 let server = SerialPairServer {
546 tx: server_tx,
547 rx: server_rx,
548 config: config.clone(),
549 };
550
551 let client = SerialPairClient {
552 tx: client_tx,
553 rx: client_rx,
554 config,
555 };
556
557 (server, client)
558 }
559}
560
561#[derive(Debug)]
563pub struct SerialPairServer {
564 tx: tokio::sync::mpsc::Sender<Vec<u8>>,
565 rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
566 config: SerialConfig,
567}
568
569impl SerialPairServer {
570 pub async fn send(&self, data: &[u8]) -> Result<(), tokio::sync::mpsc::error::SendError<Vec<u8>>> {
572 if self.config.baud_rate > 0 {
574 let delay = self.config.transmission_time(data.len());
575 tokio::time::sleep(delay).await;
576 }
577
578 self.tx.send(data.to_vec()).await
579 }
580
581 pub async fn recv(&mut self) -> Option<Vec<u8>> {
583 self.rx.recv().await
584 }
585
586 pub fn try_recv(&mut self) -> Result<Vec<u8>, tokio::sync::mpsc::error::TryRecvError> {
588 self.rx.try_recv()
589 }
590}
591
592#[derive(Debug)]
594pub struct SerialPairClient {
595 tx: tokio::sync::mpsc::Sender<Vec<u8>>,
596 rx: tokio::sync::mpsc::Receiver<Vec<u8>>,
597 config: SerialConfig,
598}
599
600impl SerialPairClient {
601 pub async fn send(&self, data: &[u8]) -> Result<(), tokio::sync::mpsc::error::SendError<Vec<u8>>> {
603 if self.config.baud_rate > 0 {
605 let delay = self.config.transmission_time(data.len());
606 tokio::time::sleep(delay).await;
607 }
608
609 self.tx.send(data.to_vec()).await
610 }
611
612 pub async fn recv(&mut self) -> Option<Vec<u8>> {
614 self.rx.recv().await
615 }
616
617 pub fn try_recv(&mut self) -> Result<Vec<u8>, tokio::sync::mpsc::error::TryRecvError> {
619 self.rx.try_recv()
620 }
621}
622
623#[cfg(test)]
624mod tests {
625 use super::*;
626
627 #[test]
628 fn test_serial_config_defaults() {
629 let config = SerialConfig::default();
630 assert_eq!(config.baud_rate, 9600);
631 assert_eq!(config.data_bits, DataBits::Eight);
632 assert_eq!(config.parity, Parity::None);
633 assert_eq!(config.stop_bits, StopBits::One);
634 }
635
636 #[test]
637 fn test_bits_per_character() {
638 let config = SerialConfig::default();
640 assert_eq!(config.bits_per_character(), 10.0);
641
642 let config = SerialConfig::default().with_parity(Parity::Even);
644 assert_eq!(config.bits_per_character(), 11.0);
645
646 let config = SerialConfig::default().with_stop_bits(StopBits::Two);
648 assert_eq!(config.bits_per_character(), 11.0);
649 }
650
651 #[test]
652 fn test_char_time() {
653 let config = SerialConfig::new(9600);
654 let char_time = config.char_time();
655
656 let ms = char_time.as_micros();
658 assert!(ms > 1000 && ms < 1100);
659 }
660
661 #[test]
662 fn test_inter_frame_timeout() {
663 let config = SerialConfig::new(9600);
665 let timeout = config.inter_frame_timeout();
666 let ms = timeout.as_micros();
667 assert!(ms > 3500 && ms < 4000);
668
669 let config = SerialConfig::new(115200);
671 let timeout = config.inter_frame_timeout();
672 assert_eq!(timeout, Duration::from_micros(1750));
673 }
674
675 #[test]
676 fn test_virtual_serial_config() {
677 let config = VirtualSerialConfig::default()
678 .with_baud_rate(19200)
679 .with_slave_path("/tmp/test_serial")
680 .with_delay_simulation(true);
681
682 assert_eq!(config.serial.baud_rate, 19200);
683 assert_eq!(config.slave_path, PathBuf::from("/tmp/test_serial"));
684 assert!(config.simulate_delays);
685 }
686
687 #[tokio::test]
688 async fn test_serial_pair() {
689 let (server, mut client) = SerialPair::new(SerialConfig::new(115200));
690
691 let data = vec![0x01, 0x02, 0x03];
693 server.send(&data).await.unwrap();
694
695 let received = client.recv().await.unwrap();
696 assert_eq!(received, data);
697 }
698
699 #[tokio::test]
700 async fn test_serial_pair_bidirectional() {
701 let (mut server, mut client) = SerialPair::new(SerialConfig::new(115200));
702
703 let client_data = vec![0x01, 0x03, 0x00, 0x00, 0x00, 0x0A];
705 client.send(&client_data).await.unwrap();
706
707 let received = server.recv().await.unwrap();
708 assert_eq!(received, client_data);
709
710 let server_data = vec![0x01, 0x03, 0x14]; server.send(&server_data).await.unwrap();
713
714 let received = client.recv().await.unwrap();
715 assert_eq!(received, server_data);
716 }
717}