自从 tokio 1.0发布以来,rust的异步开发总算大势已定。尽管没达到标准库的速度,依然挡不住大家的热情。看编程排行榜,增加2倍的开发者。
既生瑜何生亮,感觉go就是小号的rust。
不废话了。背景:之前用go开发一个边缘网关的小东东,业余时间做了一大半。后来学了rust,打算练手,用rust重新写。
在crate中央仓库里找来找去,选择了 tokio-modbus。测试中发现用rtu的方式下,如果slave没有发回数据,程序将陷入无限等待中。也没找到如何设置这个超时。串口配置中倒是有个超时设置,不知道有啥用。
在tokio-modbus的github上,找到有人提到这个问题。那个解决问题的方式实在是理解不了,也很啰嗦。
自己动手, 用golang和tokio中都有的 select 很好的解决问题。
核心代码如下
1 tokio::select! { 2 result = &ctx.read_holding_registers(3,12) => match result { 3 Ok(v) => println!("Reading holding {:?}", v), 4 Err(_) => println!("reading err"), 5 }, 6 _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => println!("reading timeout 100 ms"), 7 }
完整的测试代码如下
use tokio_modbus::prelude::Reader; use tokio_serial::SerialPortBuilderExt; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { let mut port = String::from(""); let ports = tokio_serial::available_ports().unwrap(); for p in ports { port = String::from("com1"); match p.port_type { tokio_serial::SerialPortType::UsbPort(info) => { println!("{}, {:?}", p.port_name, info); port = p.port_name; }, _ => println!("{}, unknown type", p.port_name), } } println!("port is {}", port); let slave = tokio_modbus::slave::Slave(0x3); let com = tokio_serial::new(port,9600) .timeout(std::time::Duration::from_millis(70)) .data_bits(tokio_serial::DataBits::Eight) .stop_bits(tokio_serial::StopBits::One) .parity(tokio_serial::Parity::None) .open_native_async(); // tokio_serial::SerialPortBuilderExt // let com = tokio_serial::SerialStream::open(&builder).unwrap(); // only open local serial_port let ctx = tokio_modbus::prelude::rtu::connect_slave(com.unwrap(), slave).await; let mut ctx = match ctx { Ok(c) => c, Err(e) => { println!("err is {}", e); return Ok(())}, }; println!("rtu ctx ok, {:?}", ctx); let now = std::time::Instant::now(); // let mut timeend = tokio::time::sleep(std::time::Duration::from_millis(100)); tokio::select! { result = &ctx.read_holding_registers(3,12) => match result { Ok(v) => println!("Reading holding {:?}", v), Err(_) => println!("reading err"), }, _ = tokio::time::sleep(std::time::Duration::from_millis(100)) => println!("reading timeout 100 ms"), } println!("elapsed {} millis", now.elapsed().as_millis()); Ok(()) }