125 lines
3.9 KiB
Rust
125 lines
3.9 KiB
Rust
use crate::codec::SerialConfig;
|
||
|
||
use tokio_serial::{SerialPortBuilderExt, SerialStream};
|
||
use tokio_modbus::{
|
||
client::rtu,
|
||
client::Context,
|
||
slave::Slave,
|
||
prelude::*,
|
||
};
|
||
|
||
pub (crate) struct ModbusClient {
|
||
stream: SerialStream
|
||
}
|
||
|
||
impl ModbusClient {
|
||
|
||
pub fn new(serial_config: SerialConfig) -> Self {
|
||
let port_name = String::from_utf8(serial_config.port).unwrap();
|
||
let parity = match serial_config.parity {
|
||
0x00 => tokio_serial::Parity::None,
|
||
0x01 => tokio_serial::Parity::Odd,
|
||
0x02 => tokio_serial::Parity::Even,
|
||
_ => tokio_serial::Parity::None
|
||
};
|
||
|
||
let stopbits = match serial_config.stopbits {
|
||
0x01 => tokio_serial::StopBits::One,
|
||
0x02 => tokio_serial::StopBits::Two,
|
||
_ => tokio_serial::StopBits::One
|
||
};
|
||
|
||
let builder = tokio_serial::new(port_name, serial_config.baudrate)
|
||
.data_bits(tokio_serial::DataBits::Eight)
|
||
.stop_bits(stopbits)
|
||
.parity(parity)
|
||
.timeout(std::time::Duration::from_millis(serial_config.timeout as u64));
|
||
|
||
// 2. 建立串口连接
|
||
let stream = builder.open_native_async().unwrap();
|
||
|
||
Self {
|
||
stream
|
||
}
|
||
}
|
||
|
||
pub async fn request(&mut self, slave_id: u8, address: u16, cnt: u16) -> Result<Vec<u16>, Box<dyn std::error::Error>> {
|
||
let slave = Slave(slave_id);
|
||
let mut ctx = rtu::attach_slave(&mut self.stream, slave);
|
||
|
||
// 根据 address 范围执行不同操作
|
||
match address {
|
||
// 保持寄存器(Holding Registers): 40000-49999
|
||
40000..=49999 => {
|
||
let modbus_addr = address - 40000; // 转换为 Modbus 协议地址(0-9999)
|
||
let response = ctx.read_holding_registers(modbus_addr, cnt).await?;
|
||
println!("Holding Register {}: {:?}", modbus_addr, response);
|
||
match response {
|
||
Ok(bytes) => {
|
||
Ok(bytes)
|
||
}
|
||
Err(e) => {
|
||
Err(e.into())
|
||
}
|
||
}
|
||
}
|
||
|
||
// 输入寄存器(Input Registers): 30000-39999
|
||
30000..=39999 => {
|
||
let modbus_addr = address - 30000;
|
||
let response = ctx.read_input_registers(modbus_addr, cnt).await?;
|
||
println!("Input Register {}: {:?}", modbus_addr, response);
|
||
match response {
|
||
Ok(bytes) => {
|
||
Ok(bytes)
|
||
}
|
||
Err(e) => {
|
||
Err(e.into())
|
||
}
|
||
}
|
||
}
|
||
|
||
// 离散输入(Discrete Inputs): 10000-19999
|
||
10000..=19999 => {
|
||
let modbus_addr = address - 10000;
|
||
let response = ctx.read_discrete_inputs(modbus_addr, 1).await?;
|
||
println!("Discrete Input {}: {:?}", modbus_addr, response);
|
||
match response {
|
||
Ok(bytes) => {
|
||
Ok(Self::bool_to_u16(bytes[0]))
|
||
}
|
||
Err(e) => {
|
||
Err(e.into())
|
||
}
|
||
}
|
||
}
|
||
|
||
// 线圈(Coils): 0-9999
|
||
0..=9999 => {
|
||
let response = ctx.read_coils(address, 1).await?;
|
||
println!("Coil {}: {:?}", address, response);
|
||
match response {
|
||
Ok(bytes) => {
|
||
Ok(Self::bool_to_u16(bytes[0]))
|
||
}
|
||
Err(e) => {
|
||
Err(e.into())
|
||
}
|
||
}
|
||
}
|
||
|
||
_ => {
|
||
return Err("Invalid Modbus address".into());
|
||
}
|
||
}
|
||
}
|
||
|
||
pub fn bool_to_u16(val: bool) -> Vec<u16> {
|
||
if val {
|
||
vec![1]
|
||
} else {
|
||
vec![0]
|
||
}
|
||
}
|
||
|
||
} |