use std::sync::Arc;
use std::time::Duration;
use oms_modbus::monitor::*;
use oms_modbus::*;
#[tokio::test]
async fn quick_start() {
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
let addr = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 0);
let server = tcp::TcpServer::bind(addr).await.unwrap();
let bind_addr = server.local_addr().unwrap();
let store = Arc::new(SlaveStore::with_holding_registers(&[(0, 1234), (1, 5678)]));
tokio::spawn(async move {
server.serve_forever(store).await.ok();
});
tokio::time::sleep(Duration::from_millis(50)).await;
let client = tcp::TcpClient::connect_with_timeout(bind_addr, Duration::from_secs(3))
.await
.unwrap();
let regs = client.read_holding_registers(1, 0, 2).await.unwrap();
assert_eq!(regs, vec![1234, 5678]);
client.write_single_register(1, 0, 9999).await.unwrap();
}
#[tokio::test]
#[ignore = "requires a Modbus TCP server at 192.168.1.10:502"]
async fn client_master() {
let addr = "192.168.1.10:502".parse().unwrap();
let client = tcp::TcpClient::connect_with_timeout(addr, Duration::from_secs(3))
.await
.unwrap();
client.read_coils(1, 0, 10).await.unwrap();
client.read_discrete_inputs(1, 0, 4).await.unwrap();
client.read_holding_registers(1, 100, 5).await.unwrap();
client.read_input_registers(1, 0, 2).await.unwrap();
client.write_single_coil(1, 10, true).await.unwrap();
client.write_single_register(1, 100, 42).await.unwrap();
client
.write_multiple_coils(1, 20, &[true, false, true])
.await
.unwrap();
client
.write_multiple_registers(1, 200, &[1, 2, 3])
.await
.unwrap();
client
.mask_write_register(1, 10, 0x00FF, 0xFF00)
.await
.unwrap();
let _old = client
.read_write_multiple_registers(1, 0, 4, 10, &[100, 200])
.await
.unwrap();
let (_sf, _data) = client.diagnostic(1, 0x0000, 0xABCD).await.unwrap();
let _shared: Arc<dyn ModbusClient> = Arc::new(client);
}
#[tokio::test]
async fn server_slave() {
let store = Arc::new(SlaveStore::with_holding_registers(&[(0, 100), (1, 200)]));
store.write_coil(0, true);
let addr = "127.0.0.1:0".parse().unwrap();
let server = tcp::TcpServer::bind(addr).await.unwrap();
let _bind_addr = server.local_addr().unwrap();
let (serial_port, _server_end) = tokio::io::duplex(64);
let rtu_server =
rtu::RtuServer::new(serial_port).with_bus_timing(Arc::new(BusTiming::rtu_35t(9600)));
tokio::spawn(async move {
rtu_server.serve_forever(store).await.ok();
});
}
#[tokio::test]
async fn bus_monitoring() {
let (port, _server) = tokio::io::duplex(64);
let cap = Arc::new(BusCapture::unbounded()); let _ring = BusCapture::bounded(10_000);
let opts = ClientOptions::default()
.with_timeout(Duration::from_secs(3))
.with_tap(cap.clone());
let _client = rtu::with_options(port, opts);
let recorder = FileRecorder::new("traffic.log").unwrap();
let (tap, _handle) = ChannelRecorder::spawn(recorder);
let _opts = ClientOptions::default().with_tap(Arc::new(tap));
struct MyRecorder;
impl RecordSink for MyRecorder {
fn on_packet(&mut self, _record: PacketRecord) {
}
}
let (_tap, _handle) = ChannelRecorder::spawn(MyRecorder);
}
#[tokio::test]
#[ignore = "requires a Modbus TCP server at 127.0.0.1:502"]
async fn auto_reconnect() {
let addr = "127.0.0.1:502".parse().unwrap();
let _client = tcp::TcpClient::connect(addr)
.await
.unwrap()
.with_reconnect(0, Duration::from_millis(100));
let (port, _server) = tokio::io::duplex(64);
let opts = ClientOptions::default()
.with_timeout(Duration::from_secs(3))
.with_tap(Arc::new(BusCapture::unbounded()));
let _client =
rtu::with_options(port, opts).with_reconnect_on(0, Duration::from_millis(100), || {
let (c, _s) = tokio::io::duplex(64);
Ok(c)
});
}
#[test]
fn bus_timing() {
let (port, _server) = tokio::io::duplex(64);
let _timing = BusTiming::rtu_35t(9600);
let _timing = BusTiming::rtu_35t(115200);
let _timing = BusTiming::rtu_35t_raw(115200);
let _timing = BusTiming::custom(Duration::from_millis(50));
let opts = ClientOptions::default()
.with_timeout(Duration::from_secs(3))
.with_bus_timing(BusTiming::rtu_35t(9600));
let _client = rtu::with_options(port, opts);
}
#[test]
fn error_handling() {
let result: Result<Vec<u16>, ModbusError> = Err(ModbusError::timeout("RTU recv timed out"));
match result {
Ok(regs) => println!("{regs:?}"),
Err(e) => {
eprintln!("{}", e); log::error!("{}", e.detail()); println!("{}", e.label());
match &e {
ModbusError::Exception {
function: _,
code: _,
} => { }
ModbusError::Timeout(_) => { }
ModbusError::Serial(_) => { }
_ => {}
}
}
}
}
#[tokio::test]
#[ignore = "requires a Modbus TCP server at 127.0.0.1:502"]
async fn advanced_tcp() {
use oms_modbus::tcp::{LengthMode, TcpConfig, TidMode};
let addr = "127.0.0.1:502".parse().unwrap();
let _client = tcp::TcpClient::connect(addr)
.await
.unwrap()
.with_gateway_mode();
let _client = tcp::TcpClient::connect(addr)
.await
.unwrap()
.with_config(TcpConfig {
tid: TidMode::Auto, unit_id_in_body: true, length_mode: LengthMode::Standard, });
}
#[tokio::test]
#[ignore = "requires a TCP server at 127.0.0.1:1234"]
async fn transport_flexibility() {
use tokio::net::TcpStream;
let tcp = TcpStream::connect("127.0.0.1:1234").await.unwrap();
let _rtu = rtu::RtuClient::with_timeout(tcp, Duration::from_secs(3));
let tcp = TcpStream::connect("127.0.0.1:1234").await.unwrap();
let _ascii = ascii::AsciiClient::with_timeout(tcp, Duration::from_secs(3));
let (client, _server) = tokio::io::duplex(1024);
let _rtu = rtu::RtuClient::with_timeout(client, Duration::from_secs(3));
let (_any, _server) = tokio::io::duplex(64);
let opts = ClientOptions::default()
.with_timeout(Duration::from_secs(3))
.with_tap(Arc::new(BusCapture::unbounded()));
let _rtu = rtu::with_options(_any, opts); }