From 9d9e2cb4c794d2125e63e5a19d085d617f737223 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 3 Oct 2025 11:09:37 +0200 Subject: [PATCH 1/3] Unify FrameLocations of rtu and tcp --- CHANGELOG.md | 1 + src/codec/rtu/mod.rs | 10 ---------- src/codec/tcp/mod.rs | 10 ---------- src/frame/mod.rs | 10 ++++++++++ 4 files changed, 11 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8dd2ed..43f392c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ ## v0.3.0 (unpublished) - Added `Slave` and `SlaveContext` +- Merged `modbus_core::rtu::FrameLocation` and `modbus_core::tcp::FrameLocation` and moved it to `modbus_core::FrameLocation` ## v0.2.0 (2025-09-30) diff --git a/src/codec/rtu/mod.rs b/src/codec/rtu/mod.rs index 0ba8903..06b350f 100644 --- a/src/codec/rtu/mod.rs +++ b/src/codec/rtu/mod.rs @@ -23,16 +23,6 @@ pub struct DecodedFrame<'a> { pub pdu: &'a [u8], } -/// The location of all bytes that belong to the frame. -#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct FrameLocation { - /// The index where the frame starts - pub start: usize, - /// Number of bytes that belong to the frame - pub size: usize, -} - /// Decode RTU PDU frames from a buffer. pub fn decode( decoder_type: DecoderType, diff --git a/src/codec/tcp/mod.rs b/src/codec/tcp/mod.rs index 2840beb..f53c322 100644 --- a/src/codec/tcp/mod.rs +++ b/src/codec/tcp/mod.rs @@ -23,16 +23,6 @@ pub struct DecodedFrame<'a> { pub pdu: &'a [u8], } -/// The location of all bytes that belong to the frame. -#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))] -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub struct FrameLocation { - /// The index where the frame starts - pub start: usize, - /// Number of bytes that belong to the frame - pub size: usize, -} - /// Decode TCP PDU frames from a buffer. pub fn decode( decoder_type: DecoderType, diff --git a/src/frame/mod.rs b/src/frame/mod.rs index bd56e51..d4966fe 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -11,6 +11,16 @@ pub(crate) mod tcp; pub use self::{coils::*, data::*}; use byteorder::{BigEndian, ByteOrder}; +/// The location of all bytes that belong to the frame. +#[cfg_attr(all(feature = "defmt", target_os = "none"), derive(defmt::Format))] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct FrameLocation { + /// The index where the frame starts + pub start: usize, + /// Number of bytes that belong to the frame + pub size: usize, +} + /// A Modbus function code. /// /// It is represented by an unsigned 8 bit integer. From b5f0cb837837769f8f10864ba499219d615e362e Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 3 Oct 2025 11:12:42 +0200 Subject: [PATCH 2/3] Return a FrameLocation in all decode_* functions --- CHANGELOG.md | 2 ++ src/codec/rtu/client.rs | 17 ++++++++++------- src/codec/rtu/server.rs | 10 ++++++---- src/codec/tcp/client.rs | 21 ++++++++++++--------- src/codec/tcp/server.rs | 10 ++++++---- 5 files changed, 36 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43f392c..fa9bd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ - Added `Slave` and `SlaveContext` - 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` ## v0.2.0 (2025-09-30) diff --git a/src/codec/rtu/client.rs b/src/codec/rtu/client.rs index b0f840b..b0243ca 100644 --- a/src/codec/rtu/client.rs +++ b/src/codec/rtu/client.rs @@ -21,13 +21,13 @@ pub fn encode_request(adu: RequestAdu, buf: &mut [u8]) -> Result { } /// Decode an RTU response. -pub fn decode_response(buf: &[u8]) -> Result>> { +pub fn decode_response(buf: &[u8]) -> Result, FrameLocation)>> { if buf.is_empty() { return Ok(None); } decode(DecoderType::Response, buf) .and_then(|frame| { - let Some((DecodedFrame { slave, pdu }, _frame_pos)) = frame else { + let Some((DecodedFrame { slave, pdu }, frame_location)) = frame else { return Ok(None); }; let hdr = Header { slave }; @@ -38,7 +38,7 @@ pub fn decode_response(buf: &[u8]) -> Result>> { let response = ExceptionResponse::try_from(pdu) .map(|er| ResponsePdu(Err(er))) .or_else(|_| Response::try_from(pdu).map(|r| ResponsePdu(Ok(r)))) - .map(|pdu| Some(ResponseAdu { hdr, pdu })); + .map(|pdu| Some((ResponseAdu { hdr, pdu }, frame_location))); #[cfg(feature = "log")] if let Err(error) = response { // Unrecoverable error @@ -108,10 +108,13 @@ mod tests { assert!(matches!( decode_response(rsp), - Ok(Some(ResponseAdu { - hdr: Header { slave: 0x12 }, - pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))) - })) + Ok(Some(( + ResponseAdu { + hdr: Header { slave: 0x12 }, + pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))) + }, + FrameLocation { start: 0, size: 8 } + ))) )); } diff --git a/src/codec/rtu/server.rs b/src/codec/rtu/server.rs index 825df49..aace5ab 100644 --- a/src/codec/rtu/server.rs +++ b/src/codec/rtu/server.rs @@ -5,13 +5,13 @@ use super::*; /// Decode an RTU request. -pub fn decode_request(buf: &[u8]) -> Result>> { +pub fn decode_request(buf: &[u8]) -> Result, FrameLocation)>> { if buf.is_empty() { return Ok(None); } decode(DecoderType::Request, buf) .and_then(|frame| { - let Some((DecodedFrame { slave, pdu }, _frame_pos)) = frame else { + let Some((DecodedFrame { slave, pdu }, frame_location)) = frame else { return Ok(None); }; let hdr = Header { slave }; @@ -20,7 +20,7 @@ pub fn decode_request(buf: &[u8]) -> Result>> { // have already been verified with the CRC. let request = Request::try_from(pdu) .map(RequestPdu) - .map(|pdu| Some(RequestAdu { hdr, pdu })); + .map(|pdu| Some((RequestAdu { hdr, pdu }, frame_location))); #[cfg(feature = "log")] if let Err(error) = request { @@ -84,7 +84,9 @@ mod tests { 0x9F, // crc 0xBE, // crc ]; - let adu = decode_request(buf).unwrap().unwrap(); + let (adu, frame_location) = decode_request(buf).unwrap().unwrap(); + assert_eq!(frame_location.start, 0); + assert_eq!(frame_location.size, 8); let RequestAdu { hdr, pdu } = adu; let RequestPdu(pdu) = pdu; assert_eq!(hdr.slave, 0x12); diff --git a/src/codec/tcp/client.rs b/src/codec/tcp/client.rs index d8f91f9..06cfc46 100644 --- a/src/codec/tcp/client.rs +++ b/src/codec/tcp/client.rs @@ -22,7 +22,7 @@ pub fn encode_request(adu: RequestAdu, buf: &mut [u8]) -> Result { } /// Decode an TCP response. -pub fn decode_response(buf: &[u8]) -> Result>> { +pub fn decode_response(buf: &[u8]) -> Result, FrameLocation)>> { if buf.is_empty() { return Ok(None); } @@ -34,7 +34,7 @@ pub fn decode_response(buf: &[u8]) -> Result>> { unit_id, pdu, }, - _frame_pos, + frame_location, )) = frame else { return Ok(None); @@ -49,7 +49,7 @@ pub fn decode_response(buf: &[u8]) -> Result>> { let response = ExceptionResponse::try_from(pdu) .map(|er| ResponsePdu(Err(er))) .or_else(|_| Response::try_from(pdu).map(|r| ResponsePdu(Ok(r)))) - .map(|pdu| Some(ResponseAdu { hdr, pdu })); + .map(|pdu| Some((ResponseAdu { hdr, pdu }, frame_location))); #[cfg(feature = "log")] if let Err(error) = response { // Unrecoverable error @@ -128,13 +128,16 @@ mod tests { assert!(matches!( decode_response(rsp), - Ok(Some(ResponseAdu { - hdr: Header { - transaction_id: 0x1234, - unit_id: 0x12 + Ok(Some(( + ResponseAdu { + hdr: Header { + transaction_id: 0x1234, + unit_id: 0x12 + }, + pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))) }, - pdu: ResponsePdu(Ok(Response::WriteSingleRegister(0x2222, 0xABCD))) - })) + FrameLocation { start: 0, size: 12 } + ))) )); } diff --git a/src/codec/tcp/server.rs b/src/codec/tcp/server.rs index 157773c..7d0ae91 100644 --- a/src/codec/tcp/server.rs +++ b/src/codec/tcp/server.rs @@ -5,12 +5,12 @@ use super::*; /// Decode an TCP request.<'_> -pub fn decode_request(buf: &[u8]) -> Result>> { +pub fn decode_request(buf: &[u8]) -> Result, FrameLocation)>> { if buf.is_empty() { return Ok(None); } let frame = decode(DecoderType::Request, buf)?; - let Some((decoded_frame, _frame_pos)) = frame else { + let Some((decoded_frame, frame_location)) = frame else { return Ok(None); }; let DecodedFrame { @@ -27,7 +27,7 @@ pub fn decode_request(buf: &[u8]) -> Result>> { // have already been verified at the TCP level. let request = Request::try_from(pdu) .map(RequestPdu) - .map(|pdu| Some(RequestAdu { hdr, pdu })); + .map(|pdu| Some((RequestAdu { hdr, pdu }, frame_location))); #[cfg(feature = "log")] if let Err(error) = request { // Unrecoverable error @@ -148,7 +148,9 @@ mod tests { 0xAB, // value 0xCD, // value ]; - let adu = decode_request(buf).unwrap().unwrap(); + let (adu, frame_location) = decode_request(buf).unwrap().unwrap(); + assert_eq!(frame_location.start, 0); + assert_eq!(frame_location.size, 12); let RequestAdu { hdr, pdu } = adu; let RequestPdu(pdu) = pdu; assert_eq!(hdr.transaction_id, 42); From 7746d85e32d57bca4173a55bb3c63a451828f612 Mon Sep 17 00:00:00 2001 From: Sebastian Scholz Date: Fri, 3 Oct 2025 11:13:29 +0200 Subject: [PATCH 3/3] Add FrameLocation::end helper --- CHANGELOG.md | 1 + src/frame/mod.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa9bd1f..1560a8d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ - 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. ## v0.2.0 (2025-09-30) diff --git a/src/frame/mod.rs b/src/frame/mod.rs index d4966fe..fd7a9a3 100644 --- a/src/frame/mod.rs +++ b/src/frame/mod.rs @@ -21,6 +21,14 @@ pub struct FrameLocation { pub size: usize, } +impl FrameLocation { + /// One past the last byte of the frame. + #[must_use] + pub const fn end(&self) -> usize { + self.start + self.size + } +} + /// A Modbus function code. /// /// It is represented by an unsigned 8 bit integer. @@ -552,4 +560,11 @@ mod tests { ); // TODO: extend test } + + #[test] + fn frame_location_end() { + assert_eq!(FrameLocation { start: 0, size: 3 }.end(), 3); + assert_eq!(FrameLocation { start: 2, size: 3 }.end(), 5); + assert_eq!(FrameLocation { start: 2, size: 0 }.end(), 2); + } }