From 2015705052825c763be174edbeb4ae1ed3bf92cd Mon Sep 17 00:00:00 2001 From: jadamcrain Date: Thu, 15 Oct 2026 16:52:03 -0700 Subject: [PATCH] Document ordering and count guarantees of read results (#197) Modbus read responses don't encode per-value addresses, so the index in Indexed (and bit_value/register_value in the bindings) is computed by the library as a convenience. Document that successful reads always return exactly range.count values in ascending, contiguous address order, and fix the guide's incorrect claim that the iterator is empty on error. --- ffi/rodbus-schema/src/client.rs | 8 ++++---- ffi/rodbus-schema/src/common.rs | 8 ++++---- guide/docs/api/client/requests.mdx | 6 +++++- rodbus/src/client/channel.rs | 24 ++++++++++++++++++++++++ rodbus/src/client/ffi_channel.rs | 12 ++++++++++++ rodbus/src/types.rs | 13 +++++++++++++ 6 files changed, 62 insertions(+), 9 deletions(-) diff --git a/ffi/rodbus-schema/src/client.rs b/ffi/rodbus-schema/src/client.rs index a38068f0..f29d8b0f 100644 --- a/ffi/rodbus-schema/src/client.rs +++ b/ffi/rodbus-schema/src/client.rs @@ -420,9 +420,9 @@ fn build_bit_read_callback( ) -> BackTraced { let future = lib.define_future_interface( "bit_read_callback", - "Callbacks received when reading coils or discrete inputs", + doc("Callbacks received when reading coils or discrete inputs").details("On success, the iterator yields exactly the requested number of values in ascending, contiguous address order, beginning at the start of the requested {struct:address_range}. A response containing too few or too many values fails the request."), common.bit_iterator.clone(), - "response", + "Iterator over the values read from the server, in ascending address order", common.error_info.clone(), )?; @@ -435,9 +435,9 @@ fn build_register_read_callback( ) -> BackTraced { let future = lib.define_future_interface( "register_read_callback", - "Callbacks received when reading reading holding or input registers", + doc("Callbacks received when reading holding or input registers").details("On success, the iterator yields exactly the requested number of values in ascending, contiguous address order, beginning at the start of the requested {struct:address_range}. A response containing too few or too many values fails the request."), common.register_iterator.clone(), - "response", + "Iterator over the values read from the server, in ascending address order", common.error_info.clone(), )?; diff --git a/ffi/rodbus-schema/src/common.rs b/ffi/rodbus-schema/src/common.rs index 6451789a..a348252d 100644 --- a/ffi/rodbus-schema/src/common.rs +++ b/ffi/rodbus-schema/src/common.rs @@ -149,9 +149,9 @@ fn build_bit_value(lib: &mut LibraryBuilder) -> BackTraced BackTraced(&mut self, range: AddressRange, callback: C) where C: FnOnce(Result) + Send + Sync + 'static, @@ -348,6 +363,9 @@ impl CallbackSession { } /// Read discrete inputs from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub async fn read_discrete_inputs(&mut self, range: AddressRange, callback: C) where C: FnOnce(Result) + Send + Sync + 'static, @@ -357,6 +375,9 @@ impl CallbackSession { } /// Read holding registers from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub async fn read_holding_registers(&mut self, range: AddressRange, callback: C) where C: FnOnce(Result) + Send + Sync + 'static, @@ -366,6 +387,9 @@ impl CallbackSession { } /// Read input registers from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub async fn read_input_registers(&mut self, range: AddressRange, callback: C) where C: FnOnce(Result) + Send + Sync + 'static, diff --git a/rodbus/src/client/ffi_channel.rs b/rodbus/src/client/ffi_channel.rs index bf780f57..bc65a064 100644 --- a/rodbus/src/client/ffi_channel.rs +++ b/rodbus/src/client/ffi_channel.rs @@ -50,6 +50,9 @@ impl FfiChannel { } /// Read coils from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub fn read_coils( &mut self, param: RequestParam, @@ -63,6 +66,9 @@ impl FfiChannel { } /// Read discrete inputs from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub fn read_discrete_inputs( &mut self, param: RequestParam, @@ -76,6 +82,9 @@ impl FfiChannel { } /// Read holding registers from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub fn read_holding_registers( &mut self, param: RequestParam, @@ -89,6 +98,9 @@ impl FfiChannel { } /// Read input registers from the server + /// + /// On success, the result contains exactly `range.count` values in ascending address order + /// starting at `range.start`. See [`Indexed`] for details. pub fn read_input_registers( &mut self, param: RequestParam, diff --git a/rodbus/src/types.rs b/rodbus/src/types.rs index b863abf5..2c44ca6f 100644 --- a/rodbus/src/types.rs +++ b/rodbus/src/types.rs @@ -61,6 +61,15 @@ impl ReadRegistersRange { } /// Value and its address +/// +/// Modbus messages that carry a block of values (read responses and write multiple requests) +/// encode a starting address and the values in address order, but not a per-value address. +/// The library computes `index` from the starting address and the value's position +/// as a convenience. +/// +/// The values of a successful read are always in ascending, contiguous address order, +/// and contain exactly `range.count` values starting at `range.start`. A response +/// containing too few or too many values fails the request. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub struct Indexed { /// Address of the value @@ -70,6 +79,8 @@ pub struct Indexed { } /// Zero-copy type used to iterate over a collection of bits +/// +/// Values are yielded in ascending address order. See [`Indexed`] for details. #[derive(Debug, Copy, Clone)] pub struct BitIterator<'a> { bytes: &'a [u8], @@ -83,6 +94,8 @@ pub(crate) struct BitIteratorDisplay<'a> { } /// Zero-copy type used to iterate over a collection of registers +/// +/// Values are yielded in ascending address order. See [`Indexed`] for details. #[derive(Debug, Copy, Clone)] pub struct RegisterIterator<'a> { bytes: &'a [u8],