From 6acac133e534936ef22a052d7a7dc6b4815e63cd Mon Sep 17 00:00:00 2001 From: Markus Kohlhase Date: Tue, 19 Mar 2024 03:25:48 +0100 Subject: [PATCH] Use typed address --- src/codec/mod.rs | 114 ++++++++++++++++++++++++---------------- src/codec/rtu/server.rs | 5 +- src/codec/tcp/server.rs | 12 +++-- src/frame/mod.rs | 57 +++++++++++++------- 4 files changed, 119 insertions(+), 69 deletions(-) diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 9580c4f..121700c 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -85,7 +85,7 @@ impl<'r> TryFrom<&'r [u8]> for Request<'r> { | F::ReadInputRegisters | F::ReadHoldingRegisters | F::WriteSingleRegister => { - let addr = BigEndian::read_u16(&bytes[1..3]); + let addr = Address::new(BigEndian::read_u16(&bytes[1..3])); let quantity = BigEndian::read_u16(&bytes[3..5]); match FnCode::from(fn_code) { @@ -98,11 +98,11 @@ impl<'r> TryFrom<&'r [u8]> for Request<'r> { } } F::WriteSingleCoil => Self::WriteSingleCoil( - BigEndian::read_u16(&bytes[1..3]), + Address::new(BigEndian::read_u16(&bytes[1..3])), u16_coil_to_bool(BigEndian::read_u16(&bytes[3..5]))?, ), F::WriteMultipleCoils => { - let address = BigEndian::read_u16(&bytes[1..3]); + let address = Address::new(BigEndian::read_u16(&bytes[1..3])); let quantity = BigEndian::read_u16(&bytes[3..5]) as usize; let byte_count = bytes[5]; if bytes.len() < (6 + byte_count as usize) { @@ -113,7 +113,7 @@ impl<'r> TryFrom<&'r [u8]> for Request<'r> { Self::WriteMultipleCoils(address, coils) } F::WriteMultipleRegisters => { - let address = BigEndian::read_u16(&bytes[1..3]); + let address = Address::new(BigEndian::read_u16(&bytes[1..3])); let quantity = BigEndian::read_u16(&bytes[3..5]) as usize; let byte_count = bytes[5]; if bytes.len() < (6 + byte_count as usize) { @@ -126,9 +126,9 @@ impl<'r> TryFrom<&'r [u8]> for Request<'r> { Self::WriteMultipleRegisters(address, data) } F::ReadWriteMultipleRegisters => { - let read_address = BigEndian::read_u16(&bytes[1..3]); + let read_address = Address::new(BigEndian::read_u16(&bytes[1..3])); let read_quantity = BigEndian::read_u16(&bytes[3..5]); - let write_address = BigEndian::read_u16(&bytes[5..7]); + let write_address = Address::new(BigEndian::read_u16(&bytes[5..7])); let write_quantity = BigEndian::read_u16(&bytes[7..9]) as usize; let write_count = bytes[9]; if bytes.len() < (10 + write_count as usize) { @@ -178,10 +178,12 @@ impl<'r> TryFrom<&'r [u8]> for Response<'r> { _ => unreachable!(), } } - F::WriteSingleCoil => Self::WriteSingleCoil(BigEndian::read_u16(&bytes[1..])), + F::WriteSingleCoil => { + Self::WriteSingleCoil(Address::new(BigEndian::read_u16(&bytes[1..]))) + } F::WriteMultipleCoils | F::WriteSingleRegister | F::WriteMultipleRegisters => { - let addr = BigEndian::read_u16(&bytes[1..]); + let addr = Address::new(BigEndian::read_u16(&bytes[1..])); let payload = BigEndian::read_u16(&bytes[3..]); match FnCode::from(fn_code) { F::WriteMultipleCoils => Self::WriteMultipleCoils(addr, payload), @@ -229,22 +231,22 @@ impl<'r> Encode for Request<'r> { | Self::ReadInputRegisters(address, payload) | Self::ReadHoldingRegisters(address, payload) | Self::WriteSingleRegister(address, payload) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); BigEndian::write_u16(&mut buf[3..], *payload); } Self::WriteSingleCoil(address, state) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); BigEndian::write_u16(&mut buf[3..], bool_to_u16_coil(*state)); } Self::WriteMultipleCoils(address, coils) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); let len = coils.len(); BigEndian::write_u16(&mut buf[3..], len as u16); buf[5] = coils.packed_len() as u8; coils.copy_to(&mut buf[6..]); } Self::WriteMultipleRegisters(address, words) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); let len = words.len(); BigEndian::write_u16(&mut buf[3..], len as u16); buf[5] = len as u8 * 2; @@ -253,9 +255,9 @@ impl<'r> Encode for Request<'r> { } } Self::ReadWriteMultipleRegisters(read_address, quantity, write_address, words) => { - BigEndian::write_u16(&mut buf[1..], *read_address); + BigEndian::write_u16(&mut buf[1..], read_address.to_u16()); BigEndian::write_u16(&mut buf[3..], *quantity); - BigEndian::write_u16(&mut buf[5..], *write_address); + BigEndian::write_u16(&mut buf[5..], write_address.to_u16()); let n = words.len(); BigEndian::write_u16(&mut buf[7..], n as u16); buf[9] = n as u8 * 2; @@ -294,12 +296,12 @@ impl<'r> Encode for Response<'r> { registers.copy_to(&mut buf[2..]); } Self::WriteSingleCoil(address) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); } Self::WriteMultipleCoils(address, payload) | Self::WriteMultipleRegisters(address, payload) | Self::WriteSingleRegister(address, payload) => { - BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[1..], address.to_u16()); BigEndian::write_u16(&mut buf[3..], *payload); } Self::Custom(_, custom_data) => { @@ -437,9 +439,13 @@ mod tests { #[test] fn read_coils() { let bytes = &mut [0; 4]; - assert!(Request::ReadCoils(0x12, 4).encode(bytes).is_err()); + assert!(Request::ReadCoils(Address::new(0x12), 4) + .encode(bytes) + .is_err()); let bytes = &mut [0; 5]; - Request::ReadCoils(0x12, 4).encode(bytes).unwrap(); + Request::ReadCoils(Address::new(0x12), 4) + .encode(bytes) + .unwrap(); assert_eq!(bytes[0], 1); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0x12); @@ -450,7 +456,9 @@ mod tests { #[test] fn read_discrete_inputs() { let bytes = &mut [0; 5]; - Request::ReadDiscreteInputs(0x03, 19).encode(bytes).unwrap(); + Request::ReadDiscreteInputs(Address::new(0x03), 19) + .encode(bytes) + .unwrap(); assert_eq!(bytes[0], 2); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0x03); @@ -461,7 +469,7 @@ mod tests { #[test] fn write_single_coil() { let bytes = &mut [0; 5]; - Request::WriteSingleCoil(0x1234, true) + Request::WriteSingleCoil(Address::new(0x1234), true) .encode(bytes) .unwrap(); assert_eq!(bytes[0], 5); @@ -476,9 +484,12 @@ mod tests { let states = &[true, false, true, true]; let buf = &mut [0]; let bytes = &mut [0; 7]; - Request::WriteMultipleCoils(0x3311, Coils::from_bools(states, buf).unwrap()) - .encode(bytes) - .unwrap(); + Request::WriteMultipleCoils( + Address::new(0x3311), + Coils::from_bools(states, buf).unwrap(), + ) + .encode(bytes) + .unwrap(); assert_eq!(bytes[0], 0x0F); assert_eq!(bytes[1], 0x33); assert_eq!(bytes[2], 0x11); @@ -491,7 +502,9 @@ mod tests { #[test] fn read_input_registers() { let bytes = &mut [0; 5]; - Request::ReadInputRegisters(0x09, 77).encode(bytes).unwrap(); + Request::ReadInputRegisters(Address::new(0x09), 77) + .encode(bytes) + .unwrap(); assert_eq!(bytes[0], 4); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0x09); @@ -502,7 +515,7 @@ mod tests { #[test] fn read_holding_registers() { let bytes = &mut [0; 5]; - Request::ReadHoldingRegisters(0x09, 77) + Request::ReadHoldingRegisters(Address::new(0x09), 77) .encode(bytes) .unwrap(); assert_eq!(bytes[0], 3); @@ -515,7 +528,7 @@ mod tests { #[test] fn write_single_register() { let bytes = &mut [0; 5]; - Request::WriteSingleRegister(0x07, 0xABCD) + Request::WriteSingleRegister(Address::new(0x07), 0xABCD) .encode(bytes) .unwrap(); assert_eq!(bytes[0], 6); @@ -531,7 +544,7 @@ mod tests { let bytes = &mut [0; 10]; Request::WriteMultipleRegisters( - 0x06, + Address::new(0x06), Data::from_words(&[0xABCD, 0xEF12], buf).unwrap(), ) .encode(bytes) @@ -563,7 +576,7 @@ mod tests { let buf = &mut [0; 4]; let bytes = &mut [0; 14]; let data = Data::from_words(&[0xABCD, 0xEF12], buf).unwrap(); - Request::ReadWriteMultipleRegisters(0x05, 51, 0x03, data) + Request::ReadWriteMultipleRegisters(Address::new(0x05), 51, Address::new(0x03), data) .encode(bytes) .unwrap(); @@ -628,21 +641,21 @@ mod tests { let data: &[u8] = &[0x01, 0x00, 0x12, 0x0, 0x4]; let req = Request::try_from(data).unwrap(); - assert_eq!(req, Request::ReadCoils(0x12, 4)); + assert_eq!(req, Request::ReadCoils(Address::new(0x12), 4)); } #[test] fn read_discrete_inputs() { let data: &[u8] = &[2, 0x00, 0x03, 0x00, 19]; let req = Request::try_from(data).unwrap(); - assert_eq!(req, Request::ReadDiscreteInputs(0x03, 19)); + assert_eq!(req, Request::ReadDiscreteInputs(Address::new(0x03), 19)); } #[test] fn write_single_coil() { let bytes: &[u8] = &[5, 0x12, 0x34, 0xFF, 0x00]; let req = Request::try_from(bytes).unwrap(); - assert_eq!(req, Request::WriteSingleCoil(0x1234, true)); + assert_eq!(req, Request::WriteSingleCoil(Address::new(0x1234), true)); } #[test] @@ -660,7 +673,7 @@ mod tests { assert_eq!( req, Request::WriteMultipleCoils( - 0x3311, + Address::new(0x3311), Coils { quantity: 4, data: &[0b1101] @@ -673,21 +686,24 @@ mod tests { fn read_input_registers() { let bytes: &[u8] = &[4, 0x00, 0x09, 0x00, 0x4D]; let req = Request::try_from(bytes).unwrap(); - assert_eq!(req, Request::ReadInputRegisters(0x09, 77)); + assert_eq!(req, Request::ReadInputRegisters(Address::new(0x09), 77)); } #[test] fn read_holding_registers() { let bytes: &[u8] = &[3, 0x00, 0x09, 0x00, 0x4D]; let req = Request::try_from(bytes).unwrap(); - assert_eq!(req, Request::ReadHoldingRegisters(0x09, 77)); + assert_eq!(req, Request::ReadHoldingRegisters(Address::new(0x09), 77)); } #[test] fn write_single_register() { let bytes: &[u8] = &[6, 0x00, 0x07, 0xAB, 0xCD]; let req = Request::try_from(bytes).unwrap(); - assert_eq!(req, Request::WriteSingleRegister(0x07, 0xABCD)); + assert_eq!( + req, + Request::WriteSingleRegister(Address::new(0x07), 0xABCD) + ); } #[test] @@ -700,7 +716,7 @@ mod tests { assert_eq!( req, Request::WriteMultipleRegisters( - 0x06, + Address::new(0x06), Data { quantity: 2, data: &[0xAB, 0xCD, 0xEF, 0x12] @@ -731,7 +747,12 @@ mod tests { }; assert_eq!( req, - Request::ReadWriteMultipleRegisters(0x05, 51, 0x03, data) + Request::ReadWriteMultipleRegisters( + Address::new(0x05), + 51, + Address::new(0x03), + data + ) ); if let Request::ReadWriteMultipleRegisters(_, _, _, data) = req { assert_eq!(data.get(0), Some(0xABCD)); @@ -785,7 +806,7 @@ mod tests { #[test] fn write_single_coil() { - let res = Response::WriteSingleCoil(0x33); + let res = Response::WriteSingleCoil(Address::new(0x33)); let bytes = &mut [0, 0, 0]; res.encode(bytes).unwrap(); assert_eq!(bytes[0], 5); @@ -795,7 +816,7 @@ mod tests { #[test] fn write_multiple_coils() { - let res = Response::WriteMultipleCoils(0x3311, 5); + let res = Response::WriteMultipleCoils(Address::new(0x3311), 5); let bytes = &mut [0; 5]; res.encode(bytes).unwrap(); assert_eq!(bytes[0], 0x0F); @@ -840,7 +861,7 @@ mod tests { #[test] fn write_single_register() { - let res = Response::WriteSingleRegister(0x07, 0xABCD); + let res = Response::WriteSingleRegister(Address::new(0x07), 0xABCD); let bytes = &mut [0; 5]; res.encode(bytes).unwrap(); assert_eq!(bytes[0], 6); @@ -852,7 +873,7 @@ mod tests { #[test] fn write_multiple_registers() { - let res = Response::WriteMultipleRegisters(0x06, 2); + let res = Response::WriteMultipleRegisters(Address::new(0x06), 2); let bytes = &mut [0; 5]; res.encode(bytes).unwrap(); assert_eq!(bytes[0], 0x10); @@ -940,7 +961,7 @@ mod tests { fn write_single_coil() { let bytes: &[u8] = &[5, 0x00, 0x33]; let rsp = Response::try_from(bytes).unwrap(); - assert_eq!(rsp, Response::WriteSingleCoil(0x33)); + assert_eq!(rsp, Response::WriteSingleCoil(Address::new(0x33))); let broken_bytes: &[u8] = &[5, 0x00]; assert!(Response::try_from(broken_bytes).is_err()); @@ -950,7 +971,7 @@ mod tests { fn write_multiple_coils() { let bytes: &[u8] = &[0x0F, 0x33, 0x11, 0x00, 0x05]; let rsp = Response::try_from(bytes).unwrap(); - assert_eq!(rsp, Response::WriteMultipleCoils(0x3311, 5)); + assert_eq!(rsp, Response::WriteMultipleCoils(Address::new(0x3311), 5)); let broken_bytes: &[u8] = &[0x0F, 0x33, 0x11, 0x00]; assert!(Response::try_from(broken_bytes).is_err()); } @@ -985,7 +1006,10 @@ mod tests { fn write_single_register() { let bytes: &[u8] = &[6, 0x00, 0x07, 0xAB, 0xCD]; let rsp = Response::try_from(bytes).unwrap(); - assert_eq!(rsp, Response::WriteSingleRegister(0x07, 0xABCD)); + assert_eq!( + rsp, + Response::WriteSingleRegister(Address::new(0x07), 0xABCD) + ); let broken_bytes: &[u8] = &[6, 0x00, 0x07, 0xAB]; assert!(Response::try_from(broken_bytes).is_err()); } @@ -994,7 +1018,7 @@ mod tests { fn write_multiple_registers() { let bytes: &[u8] = &[0x10, 0x00, 0x06, 0x00, 0x02]; let rsp = Response::try_from(bytes).unwrap(); - assert_eq!(rsp, Response::WriteMultipleRegisters(0x06, 2)); + assert_eq!(rsp, Response::WriteMultipleRegisters(Address::new(0x06), 2)); let broken_bytes: &[u8] = &[0x10, 0x00, 0x06, 0x00]; assert!(Response::try_from(broken_bytes).is_err()); } diff --git a/src/codec/rtu/server.rs b/src/codec/rtu/server.rs index 8621f93..dae915b 100644 --- a/src/codec/rtu/server.rs +++ b/src/codec/rtu/server.rs @@ -87,7 +87,10 @@ mod tests { fn encode_write_single_register_response() { let adu = ResponseAdu { hdr: Header { slave: 0x12 }, - pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))), + pdu: ResponsePdu(Ok(Response::WriteSingleRegister( + Address::new(0x2222), + 0xABCD, + ))), }; let buf = &mut [0; 100]; let len = encode_response(adu, buf).unwrap(); diff --git a/src/codec/tcp/server.rs b/src/codec/tcp/server.rs index dda3959..a47b722 100644 --- a/src/codec/tcp/server.rs +++ b/src/codec/tcp/server.rs @@ -172,7 +172,10 @@ mod tests { transaction_id: 42, unit_id: 0x12, }, - pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))), + pdu: ResponsePdu(Ok(Response::WriteSingleRegister( + Address::new(0x2222), + 0xABCD, + ))), }; let buf = &mut [0; 100]; let len = encode_response(adu, buf).unwrap(); @@ -198,7 +201,10 @@ mod tests { transaction_id: 42, unit_id: 0x12, }, - pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))), + pdu: ResponsePdu(Ok(Response::WriteSingleRegister( + Address::new(0x2222), + 0xABCD, + ))), }; let buf = &mut [0; 11]; let res = encode_response(adu, buf).err().unwrap(); @@ -212,7 +218,7 @@ mod tests { transaction_id: 42, unit_id: 0x12, }, - pdu: RequestPdu(Request::WriteSingleRegister(0x2222, 0xABCD)), + pdu: RequestPdu(Request::WriteSingleRegister(Address::new(0x2222), 0xABCD)), }; let buf = &mut [0; 11]; let res = encode_request(adu, buf).err().unwrap(); diff --git a/src/frame/mod.rs b/src/frame/mod.rs index 0e9f232..ff201b8 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -102,7 +102,19 @@ impl From for u8 { pub(crate) type SubFnCode = u16; /// A Modbus address is represented by 16 bit (from `0` to `65535`). -pub(crate) type Address = u16; +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Address(u16); + +impl Address { + #[must_use] + pub const fn new(addr: u16) -> Self { + Self(addr) + } + #[must_use] + pub const fn to_u16(self) -> u16 { + self.0 + } +} /// A Coil represents a single bit. /// @@ -341,7 +353,6 @@ impl<'r> Response<'r> { #[cfg(test)] mod tests { - use super::*; #[test] @@ -362,12 +373,12 @@ mod tests { fn function_code_from_request() { use Request::*; let requests = &[ - (ReadCoils(0, 0), 1), - (ReadDiscreteInputs(0, 0), 2), - (WriteSingleCoil(0, true), 5), + (ReadCoils(Address::new(0), 0), 1), + (ReadDiscreteInputs(Address::new(0), 0), 2), + (WriteSingleCoil(Address::new(0), true), 5), ( WriteMultipleCoils( - 0, + Address::new(0), Coils { quantity: 0, data: &[], @@ -375,12 +386,12 @@ mod tests { ), 0x0F, ), - (ReadInputRegisters(0, 0), 0x04), - (ReadHoldingRegisters(0, 0), 0x03), - (WriteSingleRegister(0, 0), 0x06), + (ReadInputRegisters(Address::new(0), 0), 0x04), + (ReadHoldingRegisters(Address::new(0), 0), 0x03), + (WriteSingleRegister(Address::new(0), 0), 0x06), ( WriteMultipleRegisters( - 0, + Address::new(0), Data { quantity: 0, data: &[], @@ -390,9 +401,9 @@ mod tests { ), ( ReadWriteMultipleRegisters( + Address::new(0), 0, - 0, - 0, + Address::new(0), Data { quantity: 0, data: &[], @@ -426,8 +437,8 @@ mod tests { }), 2, ), - (WriteSingleCoil(0x0), 5), - (WriteMultipleCoils(0x0, 0x0), 0x0F), + (WriteSingleCoil(Address::new(0x0)), 5), + (WriteMultipleCoils(Address::new(0x0), 0x0), 0x0F), ( ReadInputRegisters(Data { quantity: 0, @@ -442,8 +453,8 @@ mod tests { }), 0x03, ), - (WriteSingleRegister(0, 0), 0x06), - (WriteMultipleRegisters(0, 0), 0x10), + (WriteSingleRegister(Address::new(0), 0), 0x06), + (WriteMultipleRegisters(Address::new(0), 0), 0x10), ( ReadWriteMultipleRegisters(Data { quantity: 0, @@ -461,12 +472,18 @@ mod tests { #[test] fn test_request_pdu_len() { - assert_eq!(Request::ReadCoils(0x12, 5).pdu_len(), 5); - assert_eq!(Request::WriteSingleRegister(0x12, 0x33).pdu_len(), 5); + assert_eq!(Request::ReadCoils(Address::new(0x12), 5).pdu_len(), 5); + assert_eq!( + Request::WriteSingleRegister(Address::new(0x12), 0x33).pdu_len(), + 5 + ); let buf = &mut [0, 0]; assert_eq!( - Request::WriteMultipleCoils(0, Coils::from_bools(&[true, false], buf).unwrap()) - .pdu_len(), + Request::WriteMultipleCoils( + Address::new(0), + Coils::from_bools(&[true, false], buf).unwrap() + ) + .pdu_len(), 7 ); // TODO: extend test