From f3f49141bb44c91ea1c991b1aa1d2e6501a2c9c4 Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Tue, 19 Dec 2023 13:08:20 +0200 Subject: [PATCH 1/3] Implemented Modbus Function code FC23 client function. --- src/client.rs | 9 +++++++++ src/lib.rs | 2 ++ src/tcp.rs | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/src/client.rs b/src/client.rs index 2e12d64b1..bc31ba448 100644 --- a/src/client.rs +++ b/src/client.rs @@ -17,5 +17,14 @@ pub trait Client { fn write_multiple_registers(&mut self, address: u16, values: &[u16]) -> Result<()>; + fn write_read_multiple_registers( + &mut self, + write_address: u16, + write_quantity: u16, + write_values: &[u16], + read_address: u16, + read_quantity: u16, + ) -> Result>; + fn set_uid(&mut self, uid: u8); } diff --git a/src/lib.rs b/src/lib.rs index 3ab36a39a..61ecf23ca 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -52,6 +52,7 @@ enum Function<'a> { WriteSingleRegister(Address, Value), WriteMultipleCoils(Address, Quantity, &'a [u8]), WriteMultipleRegisters(Address, Quantity, &'a [u8]), + WriteReadMultipleRegisters(Address, Quantity, &'a [u8], Address, Quantity), } impl<'a> Function<'a> { @@ -65,6 +66,7 @@ impl<'a> Function<'a> { Function::WriteSingleRegister(_, _) => 0x06, Function::WriteMultipleCoils(_, _, _) => 0x0f, Function::WriteMultipleRegisters(_, _, _) => 0x10, + Function::WriteReadMultipleRegisters(_, _, _, _, _) => 0x17, } // ReadExceptionStatus = 0x07, // ReportSlaveId = 0x11, diff --git a/src/tcp.rs b/src/tcp.rs index a84eec063..6a7fd5722 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -216,6 +216,43 @@ impl Transport { self.write(&mut buff) } + fn write_read_multiple(&mut self, fun: &Function) -> Result> { + let (write_addr, write_quantity, write_values, read_addr, read_quantity) = match *fun { + Function::WriteReadMultipleRegisters(wa, wq, wv, ra, rq) => (wa, wq, wv, ra, rq), + _ => return Err(Error::InvalidFunction), + }; + let expected_bytes = 2 * read_quantity as usize; + + let header = Header::new(self, MODBUS_HEADER_SIZE as u16 + 10u16 + write_quantity * 2 + 1u16); + let mut buff = header.pack()?; + + buff.write_u8(fun.code())?; + buff.write_u16::(read_addr)?; + buff.write_u16::(read_quantity)?; + buff.write_u16::(write_addr)?; + buff.write_u16::(write_quantity)?; + buff.write_u8((write_values.len()) as u8)?; + for v in write_values { + buff.write_u8(*v)?; + } + + match self.stream.write_all(&buff) { + Ok(_s) => { + let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2]; + match self.stream.read(&mut reply) { + Ok(_s) => { + let resp_hd = Header::unpack(&reply[..MODBUS_HEADER_SIZE])?; + Transport::validate_response_header(&header, &resp_hd)?; + Transport::validate_response_code(&buff, &reply)?; + Transport::get_reply_data(&reply, expected_bytes) + } + Err(e) => Err(Error::Io(e)), + } + } + Err(e) => Err(Error::Io(e)), + } + } + fn write_multiple(&mut self, fun: &Function) -> Result<()> { let (addr, quantity, values) = match *fun { Function::WriteMultipleCoils(a, q, v) | Function::WriteMultipleRegisters(a, q, v) => { @@ -397,6 +434,20 @@ impl Client for Transport { )) } + /// Write a multiple 16bit registers starting at address `write_addr` and read starting at address `read_addr`. + fn write_read_multiple_registers( + &mut self, + write_address: u16, + write_quantity: u16, + write_values: &[u16], + read_address: u16, + read_quantity: u16, + ) -> Result> { + let write_bytes = binary::unpack_bytes(write_values); + let read_bytes = self.write_read_multiple(&Function::WriteReadMultipleRegisters(write_address, write_quantity, &write_bytes, read_address, read_quantity))?; + binary::pack_bytes(&read_bytes[..]) + } + /// Set the unit identifier. fn set_uid(&mut self, uid: u8) { self.uid = uid; From e3a8e6310ab201a8b933dfef832cc64cd91b2b53 Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Tue, 19 Dec 2023 21:22:28 +0200 Subject: [PATCH 2/3] Simplified write_read_multiple code, added a simple test for it. --- src/tcp.rs | 58 ++++++++++++++++++++++++++-------------------------- tests/lib.rs | 10 +++------ 2 files changed, 32 insertions(+), 36 deletions(-) diff --git a/src/tcp.rs b/src/tcp.rs index 6a7fd5722..8acde1821 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -217,39 +217,39 @@ impl Transport { } fn write_read_multiple(&mut self, fun: &Function) -> Result> { - let (write_addr, write_quantity, write_values, read_addr, read_quantity) = match *fun { - Function::WriteReadMultipleRegisters(wa, wq, wv, ra, rq) => (wa, wq, wv, ra, rq), - _ => return Err(Error::InvalidFunction), - }; - let expected_bytes = 2 * read_quantity as usize; - - let header = Header::new(self, MODBUS_HEADER_SIZE as u16 + 10u16 + write_quantity * 2 + 1u16); - let mut buff = header.pack()?; - - buff.write_u8(fun.code())?; - buff.write_u16::(read_addr)?; - buff.write_u16::(read_quantity)?; - buff.write_u16::(write_addr)?; - buff.write_u16::(write_quantity)?; - buff.write_u8((write_values.len()) as u8)?; - for v in write_values { - buff.write_u8(*v)?; - } + if let Function::WriteReadMultipleRegisters(write_addr, write_quantity, write_values, read_addr, read_quantity) = *fun { + let expected_bytes = 2 * read_quantity as usize; + + let header = Header::new(self, MODBUS_HEADER_SIZE as u16 + 10u16 + write_quantity * 2 + 1u16); + let mut buff = header.pack()?; + + buff.write_u8(fun.code())?; + buff.write_u16::(read_addr)?; + buff.write_u16::(read_quantity)?; + buff.write_u16::(write_addr)?; + buff.write_u16::(write_quantity)?; + buff.write_u8((write_values.len()) as u8)?; + for v in write_values { + buff.write_u8(*v)?; + } - match self.stream.write_all(&buff) { - Ok(_s) => { - let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2]; - match self.stream.read(&mut reply) { - Ok(_s) => { - let resp_hd = Header::unpack(&reply[..MODBUS_HEADER_SIZE])?; - Transport::validate_response_header(&header, &resp_hd)?; - Transport::validate_response_code(&buff, &reply)?; - Transport::get_reply_data(&reply, expected_bytes) + match self.stream.write_all(&buff) { + Ok(_s) => { + let mut reply = vec![0; MODBUS_HEADER_SIZE + expected_bytes + 2]; + match self.stream.read(&mut reply) { + Ok(_s) => { + let resp_hd = Header::unpack(&reply[..MODBUS_HEADER_SIZE])?; + Transport::validate_response_header(&header, &resp_hd)?; + Transport::validate_response_code(&buff, &reply)?; + Transport::get_reply_data(&reply, expected_bytes) + } + Err(e) => Err(Error::Io(e)), } - Err(e) => Err(Error::Io(e)), } + Err(e) => Err(Error::Io(e)), } - Err(e) => Err(Error::Io(e)), + } else { + return Err(Error::InvalidFunction); } } diff --git a/tests/lib.rs b/tests/lib.rs index 0540c7290..2124e5d15 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -179,14 +179,10 @@ mod modbus_server_tests { fn test_write_read_multiple_registers() { let (_s, cfg) = start_dummy_server_with_cfg(); let mut trans = Transport::new_with_cfg("127.0.0.1", cfg).unwrap(); - // assert!(write_multiple_registers(&mut trans, 0, &[]).is_err()); - assert!(trans.write_multiple_registers(0, &[23]).is_ok()); - assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[23]); - assert!(trans.write_multiple_registers(0, &[1, 2, 3]).is_ok()); - assert_eq!(trans.read_holding_registers(0, 1).unwrap(), &[1]); - assert_eq!(trans.read_holding_registers(1, 1).unwrap(), &[2]); - assert_eq!(trans.read_holding_registers(2, 1).unwrap(), &[3]); + assert!(trans.write_read_multiple_registers(0, 3, &[1, 2, 3], 0, 3).is_ok()); assert_eq!(trans.read_holding_registers(0, 3).unwrap(), &[1, 2, 3]); + assert_eq!(trans.write_read_multiple_registers(3, 2, &[4, 5], 1, 4).unwrap(), &[2, 3, 4, 5]); + assert_eq!(trans.read_holding_registers(0, 5).unwrap(), &[1, 2, 3, 4, 5]); } #[test] From ca2ca0a7bd21fb75cc293119c9a8a14e11c530aa Mon Sep 17 00:00:00 2001 From: Eli Elgaev Date: Wed, 20 Dec 2023 12:19:45 +0200 Subject: [PATCH 3/3] Removed return statement. --- src/tcp.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tcp.rs b/src/tcp.rs index 8acde1821..6efffad93 100644 --- a/src/tcp.rs +++ b/src/tcp.rs @@ -249,7 +249,7 @@ impl Transport { Err(e) => Err(Error::Io(e)), } } else { - return Err(Error::InvalidFunction); + Err(Error::InvalidFunction) } }