Skip to main content

ClientServices

Struct ClientServices 

Source
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, N can be > 1 for pipelining.
    • For Serial, N must be 1 because Modbus serial is half-duplex and supports only one in-flight request.
  • APP - The application layer that handles processed Modbus responses.

Implementations§

Source§

impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
where TRANSPORT: Transport, APP: ClientCommon + CoilResponse,

Source

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 tcp
    • slave_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.
Source

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 tcp
    • slave_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.

Source

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 tcp
    • slave_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.
Source

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 tcp
    • slave_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>
where TRANSPORT: Transport, APP: ClientCommon + DiagnosticsResponse,

Source

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 tcp
    • slave_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.
Source

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 tcp
    • slave_addr: if transport is serial/// - mei_type: The MEI type (e.g., CanopenGeneralReference).
  • data: The data payload to be sent with the request.
Source

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 tcp
    • slave_addr: if transport is serial
Source

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 tcp
    • slave_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., 0x0000 or 0xFF00).
§Broadcast Support

Only the following sub-functions are allowed with a broadcast address:

  • RestartCommunicationsOption
  • ForceListenOnlyMode
  • ClearCountersAndDiagnosticRegister
  • ClearOverrunCounterAndFlag

If a broadcast is sent, no response is expected and no expectation is queued.

Source

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 tcp
    • slave_addr: if transport is serial
Source

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 tcp
    • slave_addr: if transport is serial
Source

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 tcp
    • slave_addr: if transport is serial
Source§

impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
where TRANSPORT: Transport, APP: ClientCommon + DiscreteInputResponse,

Source

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 tcp
    • slave_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

Source

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 tcp
    • slave_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>
where TRANSPORT: Transport, APP: ClientCommon + FifoQueueResponse,

Source

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 tcp
    • slave_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>
where TRANSPORT: Transport, APP: ClientCommon + FileRecordResponse,

Source

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 tcp
    • slave_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.
Source

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>
where TRANSPORT: Transport, APP: RegisterResponse + ClientCommon,

Source

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 tcp
    • slave_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).

Source

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 tcp
    • slave_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).

Source

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 tcp
    • slave_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).

Source

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 tcp
    • slave_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.

Source

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 tcp
    • slave_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.

Source

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 tcp
    • slave_addr: if transport is serial
  • quantity: The number of registers to write (1 to 123).
  • values: A slice of u16 values to be written. The length must match quantity.
§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.

Source

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 tcp
    • slave_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 of u16 values 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.

Source

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 tcp
    • slave_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>
where TRANSPORT: Transport, APP: ClientCommon + CoilResponse,

Source

pub fn coils(&mut self) -> CoilsApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped coils facade.

Source

pub fn with_coils<R>( &mut self, f: impl FnOnce(&mut CoilsApi<'_, TRANSPORT, APP, N>) -> R, ) -> R

Executes multiple coil requests in a single scoped borrow.

Source§

impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
where TRANSPORT: Transport, APP: ClientCommon + DiscreteInputResponse,

Source

pub fn discrete_inputs(&mut self) -> DiscreteInputsApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped discrete-inputs facade.

Source

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>
where TRANSPORT: Transport, APP: ClientCommon + RegisterResponse,

Source

pub fn registers(&mut self) -> RegistersApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped registers facade.

Source

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>
where TRANSPORT: Transport, APP: ClientCommon + DiagnosticsResponse,

Source

pub fn diagnostic(&mut self) -> DiagnosticApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped diagnostics facade.

Source

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>
where TRANSPORT: Transport, APP: ClientCommon + FifoQueueResponse,

Source

pub fn fifo(&mut self) -> FifoApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped FIFO facade.

Source

pub fn with_fifo<R>( &mut self, f: impl FnOnce(&mut FifoApi<'_, TRANSPORT, APP, N>) -> R, ) -> R

Executes multiple FIFO requests in a single scoped borrow.

Source§

impl<TRANSPORT, APP, const N: usize> ClientServices<TRANSPORT, APP, N>
where TRANSPORT: Transport, APP: ClientCommon + FileRecordResponse,

Source

pub fn file_records(&mut self) -> FileRecordsApi<'_, TRANSPORT, APP, N>

Returns a feature-scoped file-record facade.

Source

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,

Source

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 ExpectedResponse using 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 the APP layer.
§3. Timeout & Retry Management

The client maintains a queue of “Outstanding Requests”. For every poll:

  • It checks if the current_millis (provided by APP) has exceeded the sent_timestamp plus the configured response_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 with MbusError::ConnectionLost and removed from the queue.
  • Failure Notification: If all retries are exhausted, the request is dropped from the queue, and app.request_failed is called with MbusError::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 N must be 1 (1 is default) to comply with the half-duplex nature of RS-485/RS-232.
  • For TCP, N can 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.

Source

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.

Source

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.

Source

pub fn is_connected(&self) -> bool

Returns whether the underlying transport currently considers itself connected.

Source

pub fn reconnect(&mut self) -> Result<(), MbusError>
where TRANSPORT::Error: Into<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 by transport.connect(&self.config).

This method does not automatically re-send dropped requests. The application can requeue requests explicitly after reconnection succeeds.

Source

pub fn new_serial( transport: TRANSPORT, app: APP, config: ModbusSerialConfig, ) -> Result<Self, MbusError>
where [(); N]: SerialQueueSizeOne,

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.

Trait Implementations§

Source§

impl<TRANSPORT: Debug, APP: Debug, const N: usize> Debug for ClientServices<TRANSPORT, APP, N>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

§

impl<TRANSPORT, APP, const N: usize> Freeze for ClientServices<TRANSPORT, APP, N>
where APP: Freeze, TRANSPORT: Freeze,

§

impl<TRANSPORT, APP, const N: usize> RefUnwindSafe for ClientServices<TRANSPORT, APP, N>
where APP: RefUnwindSafe, TRANSPORT: RefUnwindSafe,

§

impl<TRANSPORT, APP, const N: usize> Send for ClientServices<TRANSPORT, APP, N>
where APP: Send, TRANSPORT: Send,

§

impl<TRANSPORT, APP, const N: usize> Sync for ClientServices<TRANSPORT, APP, N>
where APP: Sync, TRANSPORT: Sync,

§

impl<TRANSPORT, APP, const N: usize> Unpin for ClientServices<TRANSPORT, APP, N>
where APP: Unpin, TRANSPORT: Unpin,

§

impl<TRANSPORT, APP, const N: usize> UnsafeUnpin for ClientServices<TRANSPORT, APP, N>
where APP: UnsafeUnpin, TRANSPORT: UnsafeUnpin,

§

impl<TRANSPORT, APP, const N: usize> UnwindSafe for ClientServices<TRANSPORT, APP, N>
where APP: UnwindSafe, TRANSPORT: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.