1use serialport::SerialPort;
7use std::time::Duration;
8use tuiserial_core::{FlowControl, Parity, SerialConfig};
9
10pub use serialport;
12pub use tokio;
13
14pub fn list_ports() -> Vec<String> {
16 match serialport::available_ports() {
17 Ok(ports) => ports.iter().map(|p| p.port_name.clone()).collect(),
18 Err(_) => Vec::new(),
19 }
20}
21
22pub fn open_port(config: &SerialConfig) -> Result<Box<dyn SerialPort>, String> {
24 serialport::new(&config.port, config.baud_rate)
25 .timeout(Duration::from_millis(10))
26 .data_bits(match config.data_bits {
27 5 => serialport::DataBits::Five,
28 6 => serialport::DataBits::Six,
29 7 => serialport::DataBits::Seven,
30 _ => serialport::DataBits::Eight,
31 })
32 .parity(match config.parity {
33 Parity::Even => serialport::Parity::Even,
34 Parity::Odd => serialport::Parity::Odd,
35 Parity::None => serialport::Parity::None,
36 })
37 .stop_bits(match config.stop_bits {
38 1 => serialport::StopBits::One,
39 2 => serialport::StopBits::Two,
40 _ => serialport::StopBits::One,
41 })
42 .flow_control(match config.flow_control {
43 FlowControl::Hardware => serialport::FlowControl::Hardware,
44 FlowControl::Software => serialport::FlowControl::Software,
45 FlowControl::None => serialport::FlowControl::None,
46 })
47 .open_native()
48 .map_err(|e| format!("Failed to open port: {}", e))
49 .map(|p| Box::new(p) as Box<dyn SerialPort>)
50}
51
52pub fn read_data(port: &mut dyn SerialPort) -> Result<Vec<u8>, String> {
54 let mut buf = vec![0u8; 256];
55 match port.read(buf.as_mut_slice()) {
56 Ok(n) if n > 0 => {
57 buf.truncate(n);
58 Ok(buf)
59 }
60 Ok(_) => Ok(Vec::new()),
61 Err(ref e) if e.kind() == std::io::ErrorKind::TimedOut => Ok(Vec::new()),
62 Err(e) => Err(format!("Read error: {}", e)),
63 }
64}
65
66pub fn write_data(port: &mut dyn SerialPort, data: &[u8]) -> Result<usize, String> {
68 port.write_all(data)
69 .map(|_| data.len())
70 .map_err(|e| format!("Write error: {}", e))
71}
72
73pub fn hex_to_bytes(hex_str: &str) -> Result<Vec<u8>, String> {
82 let hex_str = hex_str.trim().replace(" ", "");
83 if hex_str.len() % 2 != 0 {
84 return Err("Hex string must have even length".to_string());
85 }
86
87 hex_str
88 .chars()
89 .collect::<Vec<_>>()
90 .chunks(2)
91 .map(|chunk| {
92 u8::from_str_radix(&chunk.iter().collect::<String>(), 16)
93 .map_err(|_| "Invalid hex character".to_string())
94 })
95 .collect()
96}
97
98pub fn bytes_to_hex(bytes: &[u8]) -> String {
107 bytes
108 .iter()
109 .map(|b| format!("{:02X}", b))
110 .collect::<Vec<_>>()
111 .join(" ")
112}
113
114pub fn bytes_to_string(bytes: &[u8]) -> String {
123 bytes
124 .iter()
125 .map(|&b| {
126 if b >= 32 && b < 127 {
127 (b as char).to_string()
128 } else {
129 format!("\\x{:02X}", b)
130 }
131 })
132 .collect()
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn test_hex_to_bytes() {
141 assert_eq!(
142 hex_to_bytes("48656C6C6F").unwrap(),
143 vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]
144 );
145 assert_eq!(
146 hex_to_bytes("48 65 6C 6C 6F").unwrap(),
147 vec![0x48, 0x65, 0x6C, 0x6C, 0x6F]
148 );
149 assert!(hex_to_bytes("4865F").is_err()); assert!(hex_to_bytes("48XY").is_err()); }
152
153 #[test]
154 fn test_bytes_to_hex() {
155 assert_eq!(
156 bytes_to_hex(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]),
157 "48 65 6C 6C 6F"
158 );
159 assert_eq!(bytes_to_hex(&[]), "");
160 }
161
162 #[test]
163 fn test_bytes_to_string() {
164 assert_eq!(bytes_to_string(&[0x48, 0x65, 0x6C, 0x6C, 0x6F]), "Hello");
165 assert_eq!(
166 bytes_to_string(&[0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x0A]),
167 "Hello\\x0A"
168 );
169 assert_eq!(bytes_to_string(&[0x00, 0x1F, 0x7F]), "\\x00\\x1F\\x7F");
170 }
171}