modbus_agent/src/modbus_client.rs
2025-06-21 00:40:29 +08:00

125 lines
3.9 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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]
}
}
}