diff --git a/CHANGELOG.md b/CHANGELOG.md index 1560a8d..6f316e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ - Merged `modbus_core::rtu::FrameLocation` and `modbus_core::tcp::FrameLocation` and moved it to `modbus_core::FrameLocation` - Return a `FrameLocation` in addition to the parsed frame in `rtu::server::decode_response`, `rtu::client::decode_response`, `tcp::server::decode_response` and `tcp::client::decode_request` -- Added `FrameLocation::end` helper. +- Added `FrameLocation::end` helper +- Fix `WriteSingleCoil` responses not including the output value ## v0.2.0 (2025-09-30) diff --git a/src/codec/mod.rs b/src/codec/mod.rs index 53778e9..db5bb9f 100644 --- a/src/codec/mod.rs +++ b/src/codec/mod.rs @@ -189,7 +189,10 @@ impl<'r> TryFrom<&'r [u8]> for Response<'r> { _ => unreachable!(), } } - F::WriteSingleCoil => Self::WriteSingleCoil(BigEndian::read_u16(&bytes[1..])), + F::WriteSingleCoil => Self::WriteSingleCoil( + BigEndian::read_u16(&bytes[1..3]), + u16_coil_to_bool(BigEndian::read_u16(&bytes[3..5]))?, + ), F::WriteMultipleCoils | F::WriteSingleRegister | F::WriteMultipleRegisters => { let addr = BigEndian::read_u16(&bytes[1..]); @@ -304,8 +307,9 @@ impl Encode for Response<'_> { buf[1] = (registers.len() * 2) as u8; registers.copy_to(&mut buf[2..]); } - Self::WriteSingleCoil(address) => { + Self::WriteSingleCoil(address, value) => { BigEndian::write_u16(&mut buf[1..], *address); + BigEndian::write_u16(&mut buf[3..], bool_to_u16_coil(*value)); } Self::WriteMultipleCoils(address, payload) | Self::WriteMultipleRegisters(address, payload) @@ -385,8 +389,10 @@ const fn min_response_pdu_len(fn_code: FunctionCode) -> usize { | F::ReadInputRegisters | F::ReadHoldingRegisters | F::ReadWriteMultipleRegisters => 2, - F::WriteSingleCoil => 3, - F::WriteMultipleCoils | F::WriteSingleRegister | F::WriteMultipleRegisters => 5, + F::WriteSingleCoil + | F::WriteMultipleCoils + | F::WriteSingleRegister + | F::WriteMultipleRegisters => 5, _ => 1, } } @@ -444,7 +450,7 @@ mod tests { assert_eq!(min_response_pdu_len(ReadCoils), 2); assert_eq!(min_response_pdu_len(ReadDiscreteInputs), 2); assert_eq!(min_response_pdu_len(ReadInputRegisters), 2); - assert_eq!(min_response_pdu_len(WriteSingleCoil), 3); + assert_eq!(min_response_pdu_len(WriteSingleCoil), 5); assert_eq!(min_response_pdu_len(ReadHoldingRegisters), 2); assert_eq!(min_response_pdu_len(WriteSingleRegister), 5); assert_eq!(min_response_pdu_len(WriteMultipleCoils), 5); @@ -806,12 +812,23 @@ mod tests { #[test] fn write_single_coil() { - let res = Response::WriteSingleCoil(0x33); - let bytes = &mut [0, 0, 0]; + let res = Response::WriteSingleCoil(0x33, true); + let bytes = &mut [0, 0, 0, 0, 0]; + res.encode(bytes).unwrap(); + assert_eq!(bytes[0], 5); + assert_eq!(bytes[1], 0x00); + assert_eq!(bytes[2], 0x33); + assert_eq!(bytes[3], 0xFF); + assert_eq!(bytes[4], 0x00); + + let res = Response::WriteSingleCoil(0x33, false); + let bytes = &mut [0, 0, 0, 0, 0]; res.encode(bytes).unwrap(); assert_eq!(bytes[0], 5); assert_eq!(bytes[1], 0x00); assert_eq!(bytes[2], 0x33); + assert_eq!(bytes[3], 0x00); + assert_eq!(bytes[4], 0x00); } #[test] @@ -959,9 +976,13 @@ mod tests { #[test] fn write_single_coil() { - let bytes: &[u8] = &[5, 0x00, 0x33]; + let bytes: &[u8] = &[5, 0x00, 0x33, 0xFF, 0x00]; + let rsp = Response::try_from(bytes).unwrap(); + assert_eq!(rsp, Response::WriteSingleCoil(0x33, true)); + + let bytes: &[u8] = &[5, 0x00, 0x33, 0x00, 0x00]; let rsp = Response::try_from(bytes).unwrap(); - assert_eq!(rsp, Response::WriteSingleCoil(0x33)); + assert_eq!(rsp, Response::WriteSingleCoil(0x33, false)); let broken_bytes: &[u8] = &[5, 0x00]; assert!(Response::try_from(broken_bytes).is_err()); diff --git a/src/frame/mod.rs b/src/frame/mod.rs index fd7a9a3..875d811 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -244,7 +244,7 @@ type MessageCount = u16; pub enum Response<'r> { ReadCoils(Coils<'r>), ReadDiscreteInputs(Coils<'r>), - WriteSingleCoil(Address), + WriteSingleCoil(Address, Coil), WriteMultipleCoils(Address, Quantity), ReadInputRegisters(Data<'r>), ReadHoldingRegisters(Data<'r>), @@ -309,7 +309,7 @@ impl<'r> From> for FunctionCode { match r { R::ReadCoils(_) => Self::ReadCoils, R::ReadDiscreteInputs(_) => Self::ReadDiscreteInputs, - R::WriteSingleCoil(_) => Self::WriteSingleCoil, + R::WriteSingleCoil(_, _) => Self::WriteSingleCoil, R::WriteMultipleCoils(_, _) => Self::WriteMultipleCoils, R::ReadInputRegisters(_) => Self::ReadInputRegisters, R::ReadHoldingRegisters(_) => Self::ReadHoldingRegisters, @@ -402,7 +402,7 @@ impl Response<'_> { pub const fn pdu_len(&self) -> usize { match *self { Self::ReadCoils(coils) | Self::ReadDiscreteInputs(coils) => 2 + coils.packed_len(), - Self::WriteSingleCoil(_) => 3, + Self::WriteSingleCoil(_, _) => 5, Self::WriteMultipleCoils(_, _) | Self::WriteMultipleRegisters(_, _) | Self::WriteSingleRegister(_, _) => 5, @@ -505,7 +505,7 @@ mod tests { }), 2, ), - (WriteSingleCoil(0x0), 5), + (WriteSingleCoil(0x0, false), 5), (WriteMultipleCoils(0x0, 0x0), 0x0F), ( ReadInputRegisters(Data {