pub struct ClientServices<TRANSPORT, APP, const N: usize = 1> { /* private fields */ }Expand description
Core client services struct that manages the application logic, transport layer, and expected responses for Modbus communication. This is Main entry point for client operations, providing methods to send requests and process responses.
§Type Parameters
TRANSPORT- The transport layer implementation (e.g., TCP or RTU) that handles the physical transmission of Modbus frames.N- The maximum number of concurrent outstanding requests (capacity of the expected responses queue).- For TCP,
Ncan be > 1 for pipelining. - For Serial,
Nmust be 1 because Modbus serial is half-duplex and supports only one in-flight request.
- For TCP,
APP- The application layer that handles processed Modbus responses.
Implementations§
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_multiple_coils(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
quantity: u16,
) -> Result<(), MbusError>
pub fn read_multiple_coils( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, quantity: u16, ) -> Result<(), MbusError>
Sends a Read Coils request to the specified unit ID and address range, and records the expected response.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -address: The starting address of the coils to read.
quantity: The number of coils to read.
§Returns
Ok(()): If the request was successfully compiled, registered in the expectation queue, and sent.Err(MbusError): If validation fails (e.g., broadcast read), the PDU is invalid, or transport fails.
Sourcepub fn read_single_coil(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
) -> Result<(), MbusError>
pub fn read_single_coil( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, ) -> Result<(), MbusError>
Sends a Read Single Coil request to the specified unit ID and address, and records the expected response.
This method is a convenience wrapper around read_multiple_coils for
reading a single coil, which simplifies the application logic when only one coil needs to be read.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -address: The address of the coil to read.
§Returns
Ok(()): If the request was successfully compiled, registered in the expectation queue, and sent.Err(MbusError): If validation fails (e.g., broadcast read), the PDU is invalid, or transport fails.
Note: This uses FC 0x01 with a quantity of 1.
Sourcepub fn write_single_coil(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
value: bool,
) -> Result<(), MbusError>
pub fn write_single_coil( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, value: bool, ) -> Result<(), MbusError>
Sends a Write Single Coil request to the specified unit ID and address with the given value, and records the expected response.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -address: The address of the coil to write.
value: The boolean value to write to the coil (true for ON, false for OFF).
§Returns
Ok(()): If the request was successfully compiled, registered in the expectation queue, and sent.Err(MbusError): If validation fails (e.g., broadcast read), the PDU is invalid, or transport fails.
Sourcepub fn write_multiple_coils(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
values: &Coils,
) -> Result<(), MbusError>
pub fn write_multiple_coils( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, values: &Coils, ) -> Result<(), MbusError>
Sends a Write Multiple Coils request to the specified unit ID and address with the given values, and records the expected response.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -address: The starting address of the coils to write.
quantity: The number of coils to write.values: A slice of boolean values to write to the coils (true for ON, false for OFF).
§Returns
Ok(()): If the request was successfully compiled, registered in the expectation queue, and sent.Err(MbusError): If validation fails (e.g., broadcast read), the PDU is invalid, or transport fails.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_device_identification(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
read_device_id_code: ReadDeviceIdCode,
object_id: ObjectId,
) -> Result<(), MbusError>
pub fn read_device_identification( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, read_device_id_code: ReadDeviceIdCode, object_id: ObjectId, ) -> Result<(), MbusError>
Sends a Read Device Identification request (FC 43 / 14).
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -read_device_id_code: The type of access (01=Basic, 02=Regular, 03=Extended, 04=Specific).
object_id: The object ID to start reading from.
Sourcepub fn encapsulated_interface_transport(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
mei_type: EncapsulatedInterfaceType,
data: &[u8],
) -> Result<(), MbusError>
pub fn encapsulated_interface_transport( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, mei_type: EncapsulatedInterfaceType, data: &[u8], ) -> Result<(), MbusError>
Sends a generic Encapsulated Interface Transport request (FC 43).
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -mei_type: The MEI type (e.g.,CanopenGeneralReference).
data: The data payload to be sent with the request.
Sourcepub fn read_exception_status(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
) -> Result<(), MbusError>
pub fn read_exception_status( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, ) -> Result<(), MbusError>
Sends a Read Exception Status request (Function Code 07).
This function is specific to Serial Line Modbus. It is used to read the contents of eight Exception Status outputs in a remote device. The meaning of these status bits is device-dependent.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
Sourcepub fn diagnostics(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
sub_function: DiagnosticSubFunction,
data: &[u16],
) -> Result<(), MbusError>
pub fn diagnostics( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, sub_function: DiagnosticSubFunction, data: &[u16], ) -> Result<(), MbusError>
Sends a Diagnostics request (Function Code 08).
This function provides a series of tests for checking the communication system between a client (Master) and a server (Slave), or for checking various internal error conditions within a server.
Note: This function code is supported on Serial Line only (RTU/ASCII).
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -sub_function: The specific diagnostic test to perform (e.g.,ReturnQueryData,RestartCommunicationsOption,ClearCounters).
data: A slice of 16-bit words required by the specific sub-function. Many sub-functions expect a single word (e.g.,0x0000or0xFF00).
§Broadcast Support
Only the following sub-functions are allowed with a broadcast address:
RestartCommunicationsOptionForceListenOnlyModeClearCountersAndDiagnosticRegisterClearOverrunCounterAndFlag
If a broadcast is sent, no response is expected and no expectation is queued.
Sourcepub fn get_comm_event_counter(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
) -> Result<(), MbusError>
pub fn get_comm_event_counter( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, ) -> Result<(), MbusError>
Sends a Get Comm Event Counter request (FC 11). Serial Line only.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
Sourcepub fn get_comm_event_log(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
) -> Result<(), MbusError>
pub fn get_comm_event_log( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, ) -> Result<(), MbusError>
Sends a Get Comm Event Log request (FC 12). Serial Line only.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
Sourcepub fn report_server_id(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
) -> Result<(), MbusError>
pub fn report_server_id( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, ) -> Result<(), MbusError>
Sends a Report Server ID request (FC 17). Serial Line only.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_discrete_inputs(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
quantity: u16,
) -> Result<(), MbusError>
pub fn read_discrete_inputs( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, quantity: u16, ) -> Result<(), MbusError>
Sends a Read Discrete Inputs request to the specified unit ID and address range, and records the expected response.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The starting address of the inputs to read.quantity: The number of inputs to read.
§Returns
Ok(()) if the request was successfully enqueued and transmitted. Or else Error
Sourcepub fn read_single_discrete_input(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
) -> Result<(), MbusError>
pub fn read_single_discrete_input( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, ) -> Result<(), MbusError>
Sends a Read Discrete Inputs request for a single input.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The exact address of the single input to read.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to read from address 0 (Broadcast).
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_fifo_queue(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
) -> Result<(), MbusError>
pub fn read_fifo_queue( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, ) -> Result<(), MbusError>
Sends a Read FIFO Queue request (Function Code 0x18).
This function allows reading the contents of a remote FIFO queue of registers. The FIFO structure is address-specific, and the response contains the current count of registers in the queue followed by the register data itself.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The starting address of the FIFO queue.
§Returns
Ok(()): If the request was successfully built, the expectation was queued, and the frame was transmitted via the transport layer.Err(MbusError): If the address is a broadcast address, if the frame construction fails, or if the transport layer fails to send.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_file_record(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
sub_request: &SubRequest,
) -> Result<(), MbusError>
pub fn read_file_record( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, sub_request: &SubRequest, ) -> Result<(), MbusError>
Sends a Read File Record request (Function Code 0x14).
This function performs a data reference operation to allow reading of “File Records”. A Modbus file is a collection of records. Each file can contain up to 10000 records (addressed 0000 to 9999 decimal).
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial/// -sub_request: A structure containing one or more sub-requests. Each sub-request specifies the file number, record number, and the number of registers to read.
§Returns
Ok(()): If the request was successfully built, the expectation was queued, and the frame was transmitted.Err(MbusError): If the address is a broadcast address (not allowed for FC 0x14), if the PDU exceeds the maximum allowed size, or if transport fails.
Sourcepub fn write_file_record(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
sub_request: &SubRequest,
) -> Result<(), MbusError>
pub fn write_file_record( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, sub_request: &SubRequest, ) -> Result<(), MbusError>
Sends a Write File Record request (Function Code 0x15).
This function performs a data reference operation to allow writing of “File Records”. The request can contain multiple sub-requests, each writing a sequence of registers to a specific file and record.
§Parameters
txn_id: Transaction ID for tracking the request/response pair.unit_id_slave_addr: The target Modbus unit ID (TCP) or slave address (Serial).sub_request: A structure containing one or more sub-requests. Each sub-request specifies the file number, record number, record length, and the actual data to write.
§Returns
Ok(()): If the request was successfully built and transmitted.Err(MbusError): If broadcast is attempted, if the PDU is malformed, or transport fails.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn read_holding_registers(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
from_address: u16,
quantity: u16,
) -> Result<(), MbusError>
pub fn read_holding_registers( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, from_address: u16, quantity: u16, ) -> Result<(), MbusError>
Sends a Read Holding Registers request to the specified unit ID and address range, and records the expected response.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
from_address: The starting address of the holding registers to read.quantity: The number of holding registers to read.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to read from address 0 (Broadcast).
Sourcepub fn read_single_holding_register(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
) -> Result<(), MbusError>
pub fn read_single_holding_register( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, ) -> Result<(), MbusError>
Sends a Read Holding Registers request for a single register (Function Code 0x03).
This is a convenience wrapper around read_holding_registers with a quantity of 1.
It allows the application to receive a simplified read_single_holding_register_response
callback instead of handling a register collection.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The starting address of the holding registers to read.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to read from address 0 (Broadcast).
Sourcepub fn read_input_registers(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
quantity: u16,
) -> Result<(), MbusError>
pub fn read_input_registers( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, quantity: u16, ) -> Result<(), MbusError>
Sends a Read Input Registers request (Function Code 0x04).
This function is used to read from 1 to 125 contiguous input registers in a remote device. Input registers are typically used for read-only data like sensor readings.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The starting address of the input registers to read (0x0000 to 0xFFFF).quantity: The number of input registers to read (1 to 125).
§Returns
Ok(()): If the request was successfully built, the expectation was queued, and the frame was transmitted.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to read from address 0 (Broadcast).
Sourcepub fn read_single_input_register(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
) -> Result<(), MbusError>
pub fn read_single_input_register( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, ) -> Result<(), MbusError>
Sends a Read Input Registers request for a single register (Function Code 0x04).
This is a convenience wrapper around read_input_registers with a quantity of 1.
It allows the application to receive a simplified read_single_input_register_response
callback instead of handling a register collection.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The exact address of the input register to read.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to read from a broadcast address.
Sourcepub fn write_single_register(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
value: u16,
) -> Result<(), MbusError>
pub fn write_single_register( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, value: u16, ) -> Result<(), MbusError>
Sends a Write Single Register request (Function Code 0x06).
This function is used to write a single holding register in a remote device.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The address of the holding register to be written.value: The 16-bit value to be written to the register.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Broadcast Support
Serial Modbus (RTU/ASCII) allows broadcast writes (Slave Address 0). In this case, the request is sent to all slaves, and no response is expected or queued.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to broadcast over TCP.
Sourcepub fn write_multiple_registers(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
quantity: u16,
values: &[u16],
) -> Result<(), MbusError>
pub fn write_multiple_registers( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, quantity: u16, values: &[u16], ) -> Result<(), MbusError>
Sends a Write Multiple Registers request (Function Code 0x10).
This function is used to write a block of contiguous registers (1 to 123 registers) in a remote device.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
quantity: The number of registers to write (1 to 123).values: A slice ofu16values to be written. The length must matchquantity.
§Returns
Ok(()) if the request was successfully enqueued and transmitted.
§Broadcast Support
Serial Modbus allows broadcast. No response is expected for broadcast requests.
§Errors
Returns Err(MbusError::BroadcastNotAllowed) if attempting to broadcast over TCP.
Sourcepub fn read_write_multiple_registers(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
read_address: u16,
read_quantity: u16,
write_address: u16,
write_values: &[u16],
) -> Result<(), MbusError>
pub fn read_write_multiple_registers( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, read_address: u16, read_quantity: u16, write_address: u16, write_values: &[u16], ) -> Result<(), MbusError>
Sends a Read/Write Multiple Registers request (FC 23).
This function performs a combination of one read operation and one write operation in a single Modbus transaction. The write operation is performed before the read.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
read_address: The starting address of the registers to read.read_quantity: The number of registers to read.write_address: The starting address of the registers to write.write_values: A slice ofu16values to be written to the device.
§Returns
Ok(()) if the request was successfully sent, or an MbusError if there was an error
constructing the request (e.g., invalid quantity) or sending it over the transport.
Sourcepub fn mask_write_register(
&mut self,
txn_id: u16,
unit_id_slave_addr: UnitIdOrSlaveAddr,
address: u16,
and_mask: u16,
or_mask: u16,
) -> Result<(), MbusError>
pub fn mask_write_register( &mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr, address: u16, and_mask: u16, or_mask: u16, ) -> Result<(), MbusError>
Sends a Mask Write Register request.
This function is used to modify the contents of a single holding register using a combination
of an AND mask and an OR mask. The new value of the register is calculated as:
(current_value AND and_mask) OR (or_mask AND (NOT and_mask))
The request is added to the expected_responses queue to await a corresponding reply from the Modbus server.
§Parameters
txn_id: Transaction ID of the original request. While Modbus Serial (RTU/ASCII) does not natively use transaction IDs, the stack preserves the ID provided in the request and returns it here to allow for asynchronous tracking.unit_id_slave_addr: The target Modbus unit ID or slave address.unit_id: if transport is tcpslave_addr: if transport is serial
address: The address of the register to apply the mask to.and_mask: The 16-bit AND mask to apply to the current register value.or_mask: The 16-bit OR mask to apply to the current register value.
§Returns
Ok(()) if the request was successfully sent and queued for a response,
or an MbusError if there was an error during request construction,
sending over the transport, or if the expected_responses queue is full.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn discrete_inputs(&mut self) -> DiscreteInputsApi<'_, TRANSPORT, APP, N>
pub fn discrete_inputs(&mut self) -> DiscreteInputsApi<'_, TRANSPORT, APP, N>
Returns a feature-scoped discrete-inputs facade.
Sourcepub fn with_discrete_inputs<R>(
&mut self,
f: impl FnOnce(&mut DiscreteInputsApi<'_, TRANSPORT, APP, N>) -> R,
) -> R
pub fn with_discrete_inputs<R>( &mut self, f: impl FnOnce(&mut DiscreteInputsApi<'_, TRANSPORT, APP, N>) -> R, ) -> R
Executes multiple discrete-input requests in a single scoped borrow.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn registers(&mut self) -> RegistersApi<'_, TRANSPORT, APP, N>
pub fn registers(&mut self) -> RegistersApi<'_, TRANSPORT, APP, N>
Returns a feature-scoped registers facade.
Sourcepub fn with_registers<R>(
&mut self,
f: impl FnOnce(&mut RegistersApi<'_, TRANSPORT, APP, N>) -> R,
) -> R
pub fn with_registers<R>( &mut self, f: impl FnOnce(&mut RegistersApi<'_, TRANSPORT, APP, N>) -> R, ) -> R
Executes multiple register requests in a single scoped borrow.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn diagnostic(&mut self) -> DiagnosticApi<'_, TRANSPORT, APP, N>
pub fn diagnostic(&mut self) -> DiagnosticApi<'_, TRANSPORT, APP, N>
Returns a feature-scoped diagnostics facade.
Sourcepub fn with_diagnostic<R>(
&mut self,
f: impl FnOnce(&mut DiagnosticApi<'_, TRANSPORT, APP, N>) -> R,
) -> R
pub fn with_diagnostic<R>( &mut self, f: impl FnOnce(&mut DiagnosticApi<'_, TRANSPORT, APP, N>) -> R, ) -> R
Executes multiple diagnostic requests in a single scoped borrow.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
Sourcepub fn file_records(&mut self) -> FileRecordsApi<'_, TRANSPORT, APP, N>
pub fn file_records(&mut self) -> FileRecordsApi<'_, TRANSPORT, APP, N>
Returns a feature-scoped file-record facade.
Sourcepub fn with_file_records<R>(
&mut self,
f: impl FnOnce(&mut FileRecordsApi<'_, TRANSPORT, APP, N>) -> R,
) -> R
pub fn with_file_records<R>( &mut self, f: impl FnOnce(&mut FileRecordsApi<'_, TRANSPORT, APP, N>) -> R, ) -> R
Executes multiple file-record requests in a single scoped borrow.
Source§impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>where
TRANSPORT: Transport,
TRANSPORT::Error: Into<MbusError>,
APP: RequestErrorNotifier + TimeKeeper,
impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>where
TRANSPORT: Transport,
TRANSPORT::Error: Into<MbusError>,
APP: RequestErrorNotifier + TimeKeeper,
Sourcepub fn poll(&mut self)
pub fn poll(&mut self)
The main execution loop for the Modbus Client.
This method orchestrates the entire lifecycle of Modbus transactions by performing three critical tasks in a non-blocking manner:
§1. Data Ingestion & Stream Resynchronization
It pulls raw bytes from the TRANSPORT layer into an internal rxed_frame buffer.
Because Modbus streams (especially Serial) can contain noise or fragmented packets,
the logic handles:
- Fragmentation: If a partial frame is received, it stays in the buffer until more data arrives.
- Pipelining: If multiple ADUs are received in a single TCP packet, it processes them sequentially.
- Noise Recovery: If the buffer contains garbage that doesn’t form a valid Modbus header, it drops bytes one-by-one to “slide” the window and find the next valid start-of-frame.
§2. Response Dispatching
Once a complete ADU is validated (via checksums in RTU or length checks in TCP), it is
decompiled into a ModbusMessage. The client then:
- Matches the response to an
ExpectedResponseusing the Transaction ID (TCP) or Unit ID/Slave Address (Serial, where only one request is active at a time). - Validates the Function Code and handles Modbus Exceptions (0x80 + FC).
- Routes the payload to the specific
handler(e.g.,handle_read_coils_rsp) which ultimately triggers the user-defined callback in theAPPlayer.
§3. Timeout & Retry Management
The client maintains a queue of “Outstanding Requests”. For every poll:
- It checks if the
current_millis(provided byAPP) has exceeded thesent_timestampplus the configuredresponse_timeout_ms. - Scheduled Retries: If a timeout occurs and
retries_left > 0, the next retry is scheduled using the configured backoff strategy (and optional jitter). - Scheduled retries are only sent when the poll loop reaches or passes the scheduled retry timestamp. The client never sleeps or blocks internally.
- Connection Loss Handling: If
recv()reports a connection-level transport error (or transport reports disconnected state), all pending requests are immediately failed withMbusError::ConnectionLostand removed from the queue. - Failure Notification: If all retries are exhausted, the request is dropped from
the queue, and
app.request_failedis called withMbusError::NoRetriesLeft.
§Performance Note
This method uses a next_timeout_check cache. If the earliest possible timeout is in
the future, it skips the O(N) scan of the expected responses queue, making it
highly efficient for high-concurrency TCP scenarios.
§Constraints
- For Serial transports, the queue size
Nmust be 1 (1 is default) to comply with the half-duplex nature of RS-485/RS-232. - For TCP,
Ncan be larger to support request pipelining.
Source§impl<TRANSPORT: Transport, APP: ClientCommon, const N: usize> ClientServices<TRANSPORT, APP, N>
Implementation of core client services, including methods for sending requests and processing responses.
impl<TRANSPORT: Transport, APP: ClientCommon, const N: usize> ClientServices<TRANSPORT, APP, N>
Implementation of core client services, including methods for sending requests and processing responses.
Sourcepub fn new(
transport: TRANSPORT,
app: APP,
config: ModbusConfig,
) -> Result<Self, MbusError>
pub fn new( transport: TRANSPORT, app: APP, config: ModbusConfig, ) -> Result<Self, MbusError>
Creates a new instance of ClientServices, connecting to the transport layer with the provided configuration.
Sourcepub fn app(&self) -> &APP
pub fn app(&self) -> &APP
Returns an immutable reference to the application callback handler.
This allows observers/tests to inspect application-owned state while keeping the handler instance stable for in-flight requests.
Sourcepub fn is_connected(&self) -> bool
pub fn is_connected(&self) -> bool
Returns whether the underlying transport currently considers itself connected.
Sourcepub fn reconnect(&mut self) -> Result<(), MbusError>
pub fn reconnect(&mut self) -> Result<(), MbusError>
Re-establishes the underlying transport connection using the existing configuration.
Behavior:
- Drops all currently pending in-flight requests and reports them as
MbusError::ConnectionLost. - Clears any partially received frame bytes.
- Calls
transport.disconnect()(best-effort) followed bytransport.connect(&self.config).
This method does not automatically re-send dropped requests. The application can requeue requests explicitly after reconnection succeeds.
Sourcepub fn new_serial(
transport: TRANSPORT,
app: APP,
config: ModbusSerialConfig,
) -> Result<Self, MbusError>
pub fn new_serial( transport: TRANSPORT, app: APP, config: ModbusSerialConfig, ) -> Result<Self, MbusError>
Creates a serial client with a compile-time enforced queue size of exactly 1.
This constructor exists to make the serial half-duplex constraint fail at compile time
instead of runtime. Any attempt to call this function with N != 1 fails trait-bound
resolution during compilation.
Use this constructor when building serial RTU/ASCII clients and prefer
SerialClientServices as the type alias for readability.