use std::net::SocketAddr; use std::str::FromStr; use std::time::Duration; use rodbus::client::*; use rodbus::server::*; use rodbus::*; use tokio::runtime::Runtime; struct Handler { coils: [bool; 10], discrete_inputs: [bool; 10], holding_registers: [u16; 10], input_registers: [u16; 10], } impl Handler { fn new() -> Self { Self { coils: [false; 10], discrete_inputs: [false; 10], holding_registers: [0; 10], input_registers: [0; 10], } } } struct ClientStateListener { tx: tokio::sync::mpsc::Sender, } impl Listener for ClientStateListener { fn update(&mut self, value: ClientState) -> MaybeAsync<()> { let update = { let tx = self.tx.clone(); async move { let _ = tx.send(value).await; } }; MaybeAsync::asynchronous(update) } } impl RequestHandler for Handler { fn read_coil(&self, address: u16) -> Result { match self.coils.get(address as usize) { Some(x) => Ok(*x), None => Err(ExceptionCode::IllegalDataAddress), } } fn read_discrete_input(&self, address: u16) -> Result { match self.discrete_inputs.get(address as usize) { Some(x) => Ok(*x), None => Err(ExceptionCode::IllegalDataAddress), } } fn read_holding_register(&self, address: u16) -> Result { match self.holding_registers.get(address as usize) { Some(x) => Ok(*x), None => Err(ExceptionCode::IllegalDataAddress), } } fn read_input_register(&self, address: u16) -> Result { match self.input_registers.get(address as usize) { Some(x) => Ok(*x), None => Err(ExceptionCode::IllegalDataAddress), } } fn write_single_coil(&mut self, value: Indexed) -> Result<(), ExceptionCode> { match self.coils.get_mut(value.index as usize) { Some(x) => { *x = value.value; Ok(()) } None => Err(ExceptionCode::IllegalDataAddress), } } fn write_single_register(&mut self, value: Indexed) -> Result<(), ExceptionCode> { match self.holding_registers.get_mut(value.index as usize) { Some(x) => { *x = value.value; Ok(()) } None => Err(ExceptionCode::IllegalDataAddress), } } fn write_multiple_coils(&mut self, values: WriteCoils) -> Result<(), ExceptionCode> { for x in values.iterator { match self.coils.get_mut(x.index as usize) { Some(c) => *c = x.value, None => return Err(ExceptionCode::IllegalDataAddress), } } Ok(()) } fn write_multiple_registers(&mut self, values: WriteRegisters) -> Result<(), ExceptionCode> { for x in values.iterator { match self.holding_registers.get_mut(x.index as usize) { Some(c) => *c = x.value, None => return Err(ExceptionCode::IllegalDataAddress), } } Ok(()) } } async fn test_requests_and_responses() { let handler = Handler::new().wrap(); let addr = SocketAddr::from_str("127.0.0.1:40000").unwrap(); let _server = spawn_tcp_server_task( 1, addr, ServerHandlerMap::single(UnitId::new(1), handler.clone()), AddressFilter::Any, DecodeLevel::default(), ) .await .unwrap(); let (tx, mut rx) = tokio::sync::mpsc::channel(8); let listener = ClientStateListener { tx }; let mut channel = spawn_tcp_client_task( HostAddr::ip(addr.ip(), addr.port()), 10, default_retry_strategy(), DecodeLevel::default(), Some(Box::new(listener)), ); channel.enable().await.unwrap(); // wait until we're connected loop { let state = rx.recv().await.unwrap(); if state == ClientState::Connected { break; } } let params = RequestParam::new(UnitId::new(0x01), Duration::from_secs(1)); { let mut guard = handler.lock().unwrap(); guard.discrete_inputs[0] = true; guard.input_registers[0] = 0xCAFE; } assert_eq!( channel .read_discrete_inputs(params, AddressRange::try_from(0, 2).unwrap()) .await .unwrap(), vec![Indexed::new(0, true), Indexed::new(1, false)] ); assert_eq!( channel .read_input_registers(params, AddressRange::try_from(0, 2).unwrap()) .await .unwrap(), vec![Indexed::new(0, 0xCAFE), Indexed::new(1, 0x0000)] ); // do a single coil write and verify that it was written by reading it assert_eq!( channel .write_single_coil(params, Indexed::new(1, true)) .await .unwrap(), Indexed::new(1, true) ); assert_eq!( channel .read_coils(params, AddressRange::try_from(0, 2).unwrap()) .await .unwrap(), vec![Indexed::new(0, false), Indexed::new(1, true)] ); // do a single register write and verify that it was written by reading it assert_eq!( channel .write_single_register(params, Indexed::new(1, 0xABCD)) .await .unwrap(), Indexed::new(1, 0xABCD) ); assert_eq!( channel .read_holding_registers(params, AddressRange::try_from(0, 2).unwrap()) .await .unwrap(), vec![Indexed::new(0, 0x0000), Indexed::new(1, 0xABCD)] ); // write multiple coils and verify that they were written assert_eq!( channel .write_multiple_coils( params, WriteMultiple::from(0, vec![true, true, true]).unwrap() ) .await .unwrap(), AddressRange::try_from(0, 3).unwrap() ); assert_eq!( channel .read_coils(params, AddressRange::try_from(0, 3).unwrap()) .await .unwrap(), vec![ Indexed::new(0, true), Indexed::new(1, true), Indexed::new(2, true) ] ); // write registers and verify that they were written assert_eq!( channel .write_multiple_registers( params, WriteMultiple::from(0, vec![0x0102, 0x0304, 0x0506]).unwrap() ) .await .unwrap(), AddressRange::try_from(0, 3).unwrap() ); assert_eq!( channel .read_holding_registers(params, AddressRange::try_from(0, 3).unwrap()) .await .unwrap(), vec![ Indexed::new(0, 0x0102), Indexed::new(1, 0x0304), Indexed::new(2, 0x0506) ] ); } #[test] fn can_read_and_write_values() { let rt = Runtime::new().unwrap(); rt.block_on(test_requests_and_responses()) }