Skip to main content

mbus_client/app/
app_trait.rs

1//! Application Layer Traits
2//!
3//! This module defines the core traits used to bridge the Modbus protocol stack with
4//! user-defined application logic. It follows a callback-based (observer) pattern
5//! where the stack notifies the application of successful responses or failures.
6//!
7//! Each trait corresponds to a functional group of Modbus services (Coils, Registers, etc.).
8//!
9//! ## Callback Contract (applies to all traits in this file)
10//!
11//! - Callbacks are dispatched from `ClientServices::poll()`. No callback is invoked unless
12//!   the application actively calls `poll()`.
13//! - A successful callback means the response was fully parsed and validated against the
14//!   queued request context (transaction id, unit/slave address, and operation metadata).
15//! - For a single request, either:
16//!   - one success callback is invoked from the corresponding response trait, or
17//!   - one failure callback is invoked via [`RequestErrorNotifier::request_failed`].
18//! - After either callback path runs, the request is removed from the internal queue.
19//! - Callback implementations should remain lightweight and non-blocking. If heavy work is
20//!   needed (database writes, UI updates, IPC), enqueue that work into your own task queue.
21//! - `txn_id` is always the original id supplied by the caller, including Serial modes where
22//!   transaction ids are not transmitted on the wire.
23
24use mbus_core::{
25    errors::MbusError,
26    function_codes::public::{DiagnosticSubFunction, EncapsulatedInterfaceType},
27    transport::UnitIdOrSlaveAddr,
28};
29
30#[cfg(feature = "coils")]
31use crate::services::coil::Coils;
32#[cfg(feature = "diagnostics")]
33use crate::services::diagnostic::DeviceIdentificationResponse;
34#[cfg(feature = "discrete-inputs")]
35use crate::services::discrete_input::DiscreteInputs;
36#[cfg(feature = "fifo")]
37use crate::services::fifo_queue::FifoQueue;
38#[cfg(feature = "file-record")]
39use crate::services::file_record::SubRequestParams;
40#[cfg(feature = "registers")]
41use crate::services::register::Registers;
42
43/// Trait for receiving notifications about failed Modbus requests.
44///
45/// This is used to handle timeouts, connection issues, or Modbus exception responses
46/// at the application level, allowing the implementor to gracefully recover or alert the user.
47pub trait RequestErrorNotifier {
48    /// Called by the client stack whenever a previously queued request cannot be completed.
49    ///
50    /// The `error` parameter identifies the exact failure cause. The following variants are
51    /// delivered by the stack's internal `poll()` and `handle_timeouts()` logic:
52    ///
53    /// - **`MbusError::ModbusException(code)`** — The remote device replied with a Modbus
54    ///   exception frame (`function code 0x80 + FC`). The server understood the request but
55    ///   refused to execute it (e.g. illegal data address, illegal function). Delivered
56    ///   immediately inside the `poll()` call that received the exception response, before
57    ///   any retry logic runs.
58    ///
59    /// - **`MbusError::NoRetriesLeft`** — The response timeout expired and every configured
60    ///   retry attempt was exhausted. `handle_timeouts()` waits `response_timeout_ms`
61    ///   milliseconds after each send, schedules each retry according to the configured
62    ///   `BackoffStrategy` and `JitterStrategy`, and fires this error only after the last
63    ///   retry attempt has itself timed out without a response. The request is permanently
64    ///   removed from the queue.
65    ///
66    /// - **`MbusError::SendFailed`** — A scheduled retry was due (its backoff timestamp was
67    ///   reached inside `handle_timeouts()`), but the call to `transport.send()` returned an
68    ///   error (e.g. the TCP connection or serial port was lost between the original send and
69    ///   the retry). The request is dropped immediately; remaining retries in the budget are
70    ///   not consumed.
71    ///
72    /// # Notes
73    /// - Each call corresponds to exactly one transaction. After this call the request is
74    ///   permanently removed from the internal expected-response queue and will not be retried
75    ///   again. No further callbacks will be issued for the same `txn_id`.
76    /// - The `txn_id` is always the value supplied when the request was originally enqueued,
77    ///   even for Serial transports that do not transmit a transaction ID on the wire.
78    ///
79    /// # Parameters
80    /// - `txn_id`: Transaction ID of the original request.
81    /// - `unit_id_slave_addr`: The target Modbus unit ID (TCP) or slave address (Serial).
82    /// - `error`: The specific [`MbusError`] variant describing the failure (see above).
83    fn request_failed(
84        &mut self,
85        txn_id: u16,
86        unit_id_slave_addr: UnitIdOrSlaveAddr,
87        error: MbusError,
88    );
89}
90
91/// Trait defining the expected response handling for coil-related Modbus operations.
92///
93/// Implementors of this trait to deliver the responses to the application layer,
94/// allowing application developers to process the coil data and update their application state accordingly.
95///
96/// ## When Each Callback Is Fired
97/// - `read_coils_response`: after a successful FC 0x01 response for a multi-coil read.
98/// - `read_single_coil_response`: convenience callback when quantity was 1.
99/// - `write_single_coil_response`: after a successful FC 0x05 echo/ack response.
100/// - `write_multiple_coils_response`: after a successful FC 0x0F response containing
101///   start address and quantity written by the server.
102///
103/// ## Data Semantics
104/// - Address values are Modbus data-model addresses exactly as acknowledged by the server.
105/// - Boolean coil values follow Modbus conventions: `true` = ON (`0xFF00` in FC 0x05 request),
106///   `false` = OFF (`0x0000`).
107#[cfg(feature = "coils")]
108pub trait CoilResponse {
109    /// Handles a Read Coils response by invoking the appropriate application callback with the coil states.
110    ///
111    /// # Parameters
112    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
113    ///   does not natively use transaction IDs, the stack preserves the ID provided in
114    ///   the request and returns it here to allow for asynchronous tracking.
115    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
116    ///   - `unit_id`: if transport is tcp
117    ///   - `slave_addr`: if transport is serial
118    /// - `coils`: A wrapper containing the bit-packed boolean statuses of the requested coils.
119    fn read_coils_response(
120        &mut self,
121        txn_id: u16,
122        unit_id_slave_addr: UnitIdOrSlaveAddr,
123        coils: &Coils,
124    );
125
126    /// Handles a Read Single Coil response.
127    ///
128    /// # Parameters
129    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
130    ///   does not natively use transaction IDs, the stack preserves the ID provided in
131    ///   the request and returns it here to allow for asynchronous tracking.
132    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
133    ///   - `unit_id`: if transport is tcp
134    ///   - `slave_addr`: if transport is serial
135    /// - `address`: The exact address of the single coil that was read.
136    /// - `value`: The boolean state of the coil (`true` = ON, `false` = OFF).
137    fn read_single_coil_response(
138        &mut self,
139        txn_id: u16,
140        unit_id_slave_addr: UnitIdOrSlaveAddr,
141        address: u16,
142        value: bool,
143    );
144
145    /// Handles a Write Single Coil response, confirming the state change.
146    ///
147    /// # Parameters
148    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
149    ///   does not natively use transaction IDs, the stack preserves the ID provided in
150    ///   the request and returns it here to allow for asynchronous tracking.
151    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
152    ///   - `unit_id`: if transport is tcp
153    ///   - `slave_addr`: if transport is serial
154    /// - `address`: The address of the coil that was successfully written.
155    /// - `value`: The boolean state applied to the coil (`true` = ON, `false` = OFF).
156    fn write_single_coil_response(
157        &mut self,
158        txn_id: u16,
159        unit_id_slave_addr: UnitIdOrSlaveAddr,
160        address: u16,
161        value: bool,
162    );
163
164    /// Handles a Write Multiple Coils response, confirming the bulk state change.
165    ///
166    /// # Parameters
167    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
168    ///   does not natively use transaction IDs, the stack preserves the ID provided in
169    ///   the request and returns it here to allow for asynchronous tracking.
170    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
171    ///   - `unit_id`: if transport is tcp
172    ///   - `slave_addr`: if transport is serial
173    /// - `address`: The starting address where the bulk write began.
174    /// - `quantity`: The total number of consecutive coils updated.
175    fn write_multiple_coils_response(
176        &mut self,
177        txn_id: u16,
178        unit_id_slave_addr: UnitIdOrSlaveAddr,
179        address: u16,
180        quantity: u16,
181    );
182}
183
184/// Trait defining the expected response handling for FIFO Queue Modbus operations.
185///
186/// ## When Callback Is Fired
187/// - `read_fifo_queue_response` is invoked after a successful FC 0x18 response.
188///
189/// ## Data Semantics
190/// - `fifo_queue` contains values in server-returned order.
191/// - Quantity in the payload may vary between calls depending on device state.
192///
193/// ## Implementation Guidance
194///   non-blocking because it runs in the `poll()` execution path.
195#[cfg(feature = "fifo")]
196pub trait FifoQueueResponse {
197    /// Handles a Read FIFO Queue response.
198    ///
199    /// # Parameters
200    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
201    ///   does not natively use transaction IDs, the stack preserves the ID provided in
202    ///   the request and returns it here to allow for asynchronous tracking.
203    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
204    ///   - `unit_id`: if transport is tcp
205    ///   - `slave_addr`: if transport is serial
206    /// - `fifo_queue`: A `FifoQueue` struct containing the values pulled from the queue.
207    fn read_fifo_queue_response(
208        &mut self,
209        txn_id: u16,
210        unit_id_slave_addr: UnitIdOrSlaveAddr,
211        fifo_queue: &FifoQueue,
212    );
213}
214
215/// Trait defining the expected response handling for File Record Modbus operations.
216///
217/// ## When Each Callback Is Fired
218/// - `read_file_record_response`: after successful FC 0x14 response parsing.
219/// - `write_file_record_response`: after successful FC 0x15 acknowledgement.
220///
221/// ## Data Semantics
222/// - For read responses, each `SubRequestParams` entry reflects one returned record chunk.
223/// - Per Modbus spec, the response does not echo `file_number` or `record_number`; those
224///   fields are therefore reported as `0` in callback data and should not be used as identity.
225#[cfg(feature = "file-record")]
226pub trait FileRecordResponse {
227    /// Handles a Read File Record response.
228    ///
229    /// # Parameters
230    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
231    ///   does not natively use transaction IDs, the stack preserves the ID provided in
232    ///   the request and returns it here to allow for asynchronous tracking.
233    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
234    ///   - `unit_id`: if transport is tcp
235    ///   - `slave_addr`: if transport is serial
236    /// - `data`: A slice containing the sub-request responses. Note that `file_number` and `record_number`
237    ///
238    /// are not returned by the server in the response PDU and will be set to 0 in the parameters.
239    fn read_file_record_response(
240        &mut self,
241        txn_id: u16,
242        unit_id_slave_addr: UnitIdOrSlaveAddr,
243        data: &[SubRequestParams],
244    );
245
246    /// Handles a Write File Record response, confirming the write was successful.
247    ///
248    /// # Parameters
249    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
250    ///   does not natively use transaction IDs, the stack preserves the ID provided in
251    ///   the request and returns it here to allow for asynchronous tracking.
252    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
253    ///   - `unit_id`: if transport is tcp
254    ///   - `slave_addr`: if transport is serial
255    fn write_file_record_response(&mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr);
256}
257
258/// Defines callbacks for handling responses to Modbus register-related requests.
259///
260/// Implementors of this trait can process the data received from a Modbus server
261/// and update their application state accordingly. Each method corresponds to a
262/// specific Modbus register operation response.
263///
264/// ## Callback Mapping
265/// - FC 0x03: `read_multiple_holding_registers_response`, `read_single_holding_register_response`
266/// - FC 0x04: `read_multiple_input_registers_response`, `read_single_input_register_response`
267/// - FC 0x06: `write_single_register_response`
268/// - FC 0x10: `write_multiple_registers_response`
269/// - FC 0x16: `mask_write_register_response`
270/// - FC 0x17: `read_write_multiple_registers_response`
271///
272/// ## Data Semantics
273/// - Register values are 16-bit words (`u16`) already decoded from Modbus big-endian byte pairs.
274/// - Address and quantity values are echoed/validated values corresponding to the original request.
275#[cfg(feature = "registers")]
276pub trait RegisterResponse {
277    /// Handles a response for a `Read Input Registers` (FC 0x04) request.
278    ///
279    /// # Parameters
280    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
281    ///   does not natively use transaction IDs, the stack preserves the ID provided in
282    ///   the request and returns it here to allow for asynchronous tracking.
283    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
284    ///   - `unit_id`: if transport is tcp
285    ///   - `slave_addr`: if transport is serial
286    /// - `registers`: A `Registers` struct containing the values of the read input registers.
287    fn read_multiple_input_registers_response(
288        &mut self,
289        txn_id: u16,
290        unit_id_slave_addr: UnitIdOrSlaveAddr,
291        registers: &Registers,
292    );
293
294    /// Handles a response for a `Read Single Input Register` (FC 0x04) request.
295    ///
296    /// # Parameters
297    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
298    ///   does not natively use transaction IDs, the stack preserves the ID provided in
299    ///   the request and returns it here to allow for asynchronous tracking.
300    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
301    ///   - `unit_id`: if transport is tcp
302    ///   - `slave_addr`: if transport is serial
303    /// - `address`: The address of the register that was read.
304    /// - `value`: The value of the read register.
305    fn read_single_input_register_response(
306        &mut self,
307        txn_id: u16,
308        unit_id_slave_addr: UnitIdOrSlaveAddr,
309        address: u16,
310        value: u16,
311    );
312
313    /// Handles a response for a `Read Holding Registers` (FC 0x03) request.
314    ///
315    /// # Parameters
316    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
317    ///   does not natively use transaction IDs, the stack preserves the ID provided in
318    ///   the request and returns it here to allow for asynchronous tracking.
319    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
320    ///   - `unit_id`: if transport is tcp
321    ///   - `slave_addr`: if transport is serial
322    /// - `registers`: A `Registers` struct containing the values of the read holding registers.
323    fn read_multiple_holding_registers_response(
324        &mut self,
325        txn_id: u16,
326        unit_id_slave_addr: UnitIdOrSlaveAddr,
327        registers: &Registers,
328    );
329
330    /// Handles a response for a `Write Single Register` (FC 0x06) request, confirming a successful write.
331    ///
332    /// # Parameters
333    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
334    ///   does not natively use transaction IDs, the stack preserves the ID provided in
335    ///   the request and returns it here to allow for asynchronous tracking.
336    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
337    ///   - `unit_id`: if transport is tcp
338    ///   - `slave_addr`: if transport is serial
339    /// - `address`: The address of the register that was written.
340    /// - `value`: The value that was written to the register.
341    fn write_single_register_response(
342        &mut self,
343        txn_id: u16,
344        unit_id_slave_addr: UnitIdOrSlaveAddr,
345        address: u16,
346        value: u16,
347    );
348
349    /// Handles a response for a `Write Multiple Registers` (FC 0x10) request, confirming a successful write.
350    ///
351    /// # Parameters
352    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
353    ///   does not natively use transaction IDs, the stack preserves the ID provided in
354    ///   the request and returns it here to allow for asynchronous tracking.
355    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
356    ///   - `unit_id`: if transport is tcp
357    ///   - `slave_addr`: if transport is serial
358    /// - `starting_address`: The starting address of the registers that were written.
359    /// - `quantity`: The number of registers that were written.
360    fn write_multiple_registers_response(
361        &mut self,
362        txn_id: u16,
363        unit_id_slave_addr: UnitIdOrSlaveAddr,
364        starting_address: u16,
365        quantity: u16,
366    );
367
368    /// Handles a response for a `Read/Write Multiple Registers` (FC 0x17) request.
369    ///
370    /// # Parameters
371    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
372    ///   does not natively use transaction IDs, the stack preserves the ID provided in
373    ///   the request and returns it here to allow for asynchronous tracking.
374    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
375    ///   - `unit_id`: if transport is tcp
376    ///   - `slave_addr`: if transport is serial
377    /// - `registers`: A `Registers` struct containing the values of the registers that were read.
378    fn read_write_multiple_registers_response(
379        &mut self,
380        txn_id: u16,
381        unit_id_slave_addr: UnitIdOrSlaveAddr,
382        registers: &Registers,
383    );
384
385    /// Handles a response for a single register read request.
386    ///
387    /// This is a convenience callback for when only one register is requested.
388    ///
389    /// # Parameters
390    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
391    ///   does not natively use transaction IDs, the stack preserves the ID provided in
392    ///   the request and returns it here to allow for asynchronous tracking.
393    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
394    ///   - `unit_id`: if transport is tcp
395    ///   - `slave_addr`: if transport is serial
396    /// - `address`: The address of the register that was read.
397    /// - `value`: The value of the read register.
398    fn read_single_register_response(
399        &mut self,
400        txn_id: u16,
401        unit_id_slave_addr: UnitIdOrSlaveAddr,
402        address: u16,
403        value: u16,
404    );
405
406    /// Handles a response for a single holding register write request.
407    ///
408    /// # Parameters
409    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
410    ///   does not natively use transaction IDs, the stack preserves the ID provided in
411    ///   the request and returns it here to allow for asynchronous tracking.
412    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
413    ///   - `unit_id`: if transport is tcp
414    ///   - `slave_addr`: if transport is serial
415    /// - `address`: The address of the register that was written.
416    /// - `value`: The value that was written to the register.
417    fn read_single_holding_register_response(
418        &mut self,
419        txn_id: u16,
420        unit_id_slave_addr: UnitIdOrSlaveAddr,
421        address: u16,
422        value: u16,
423    );
424
425    /// Handles a response for a `Mask Write Register` (FC 0x16) request, confirming a successful operation.
426    ///
427    /// # Parameters
428    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
429    ///   does not natively use transaction IDs, the stack preserves the ID provided in
430    ///   the request and returns it here to allow for asynchronous tracking.
431    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
432    ///   - `unit_id`: if transport is tcp
433    ///   - `slave_addr`: if transport is serial
434    fn mask_write_register_response(&mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr);
435}
436
437/// Defines callbacks for handling responses to Modbus discrete input-related requests.
438///
439/// Implementors of this trait can process the data received from a Modbus server
440/// and update their application state accordingly.
441///
442/// ## When Each Callback Is Fired
443/// - `read_multiple_discrete_inputs_response`: after successful FC 0x02 with quantity > 1.
444/// - `read_single_discrete_input_response`: convenience callback when quantity was 1.
445///
446/// ## Data Semantics
447/// - `DiscreteInputs` stores bit-packed values; use helper methods on the type instead of
448///   manually decoding bit offsets in application code.
449#[cfg(feature = "discrete-inputs")]
450pub trait DiscreteInputResponse {
451    /// Handles a response for a `Read Discrete Inputs` (FC 0x02) request.
452    ///
453    /// # Parameters
454    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
455    ///   does not natively use transaction IDs, the stack preserves the ID provided in
456    ///   the request and returns it here to allow for asynchronous tracking.
457    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
458    ///   - `unit_id`: if transport is tcp
459    ///   - `slave_addr`: if transport is serial
460    /// - `discrete_inputs`: A `DiscreteInputs` struct containing the states of the read inputs.
461    fn read_multiple_discrete_inputs_response(
462        &mut self,
463        txn_id: u16,
464        unit_id_slave_addr: UnitIdOrSlaveAddr,
465        discrete_inputs: &DiscreteInputs,
466    );
467
468    /// Handles a response for a single discrete input read request.
469    ///
470    /// # Parameters
471    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
472    ///   does not natively use transaction IDs, the stack preserves the ID provided in
473    ///   the request and returns it here to allow for asynchronous tracking.
474    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
475    ///   - `unit_id`: if transport is tcp
476    ///   - `slave_addr`: if transport is serial
477    /// - `address`: The address of the input that was read.
478    /// - `value`: The boolean state of the read input.
479    fn read_single_discrete_input_response(
480        &mut self,
481        txn_id: u16,
482        unit_id_slave_addr: UnitIdOrSlaveAddr,
483        address: u16,
484        value: bool,
485    );
486}
487
488/// Trait for handling Diagnostics-family responses.
489///
490/// ## Callback Mapping
491/// - FC 0x2B / MEI 0x0E: `read_device_identification_response`
492/// - FC 0x2B / other MEI: `encapsulated_interface_transport_response`
493/// - FC 0x07: `read_exception_status_response`
494/// - FC 0x08: `diagnostics_response`
495/// - FC 0x0B: `get_comm_event_counter_response`
496/// - FC 0x0C: `get_comm_event_log_response`
497/// - FC 0x11: `report_server_id_response`
498///
499/// ## Data Semantics
500/// - `mei_type`, `sub_function`, counters, and event buffers are already validated and decoded.
501/// - Large payloads (event logs, generic encapsulated transport data) should typically be copied
502///   or forwarded quickly, then processed outside the callback hot path.
503#[cfg(feature = "diagnostics")]
504pub trait DiagnosticsResponse {
505    /// Called when a Read Device Identification response is received.
506    ///
507    /// Implementors can use this callback to process the device identity info (Vendor, Product Code, etc.).
508    ///
509    /// # Parameters
510    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
511    ///   does not natively use transaction IDs, the stack preserves the ID provided in
512    ///   the request and returns it here to allow for asynchronous tracking.
513    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
514    ///   - `unit_id`: if transport is tcp
515    ///   - `slave_addr`: if transport is serial
516    /// - `response`: Extracted device identification strings.
517    fn read_device_identification_response(
518        &mut self,
519        txn_id: u16,
520        unit_id_slave_addr: UnitIdOrSlaveAddr,
521        response: &DeviceIdentificationResponse,
522    );
523
524    /// Called when a generic Encapsulated Interface Transport response (FC 43) is received.
525    ///
526    /// # Parameters
527    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
528    ///   does not natively use transaction IDs, the stack preserves the ID provided in
529    ///   the request and returns it here to allow for asynchronous tracking.
530    /// - `unit_id_slave_addr`: The unit ID of the device that responded.
531    ///   - `unit_id`: if transport is tcp
532    ///   - `slave_addr`: if transport is serial
533    /// - `mei_type`: The MEI type returned in the response.
534    /// - `data`: The data payload returned in the response.
535    fn encapsulated_interface_transport_response(
536        &mut self,
537        txn_id: u16,
538        unit_id_slave_addr: UnitIdOrSlaveAddr,
539        mei_type: EncapsulatedInterfaceType,
540        data: &[u8],
541    );
542
543    /// Called when a Read Exception Status response (FC 07) is received.
544    ///
545    /// # Parameters
546    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
547    ///   does not natively use transaction IDs, the stack preserves the ID provided in
548    ///   the request and returns it here to allow for asynchronous tracking.
549    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
550    ///   - `unit_id`: if transport is tcp
551    ///   - `slave_addr`: if transport is serial
552    /// - `status`: The 8-bit exception status code returned by the server.
553    fn read_exception_status_response(
554        &mut self,
555        txn_id: u16,
556        unit_id_slave_addr: UnitIdOrSlaveAddr,
557        status: u8,
558    );
559
560    /// Called when a Diagnostics response (FC 08) is received.
561    ///
562    /// # Parameters
563    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
564    ///   does not natively use transaction IDs, the stack preserves the ID provided in
565    ///   the request and returns it here to allow for asynchronous tracking.
566    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
567    ///   - `unit_id`: if transport is tcp
568    ///   - `slave_addr`: if transport is serial
569    /// - `sub_function`: The sub-function code confirming the diagnostic test.
570    /// - `data`: Data payload returned by the diagnostic test (e.g., echoed loopback data).
571    fn diagnostics_response(
572        &mut self,
573        txn_id: u16,
574        unit_id_slave_addr: UnitIdOrSlaveAddr,
575        sub_function: DiagnosticSubFunction,
576        data: &[u16],
577    );
578
579    /// Called when a Get Comm Event Counter response (FC 11) is received.
580    ///
581    /// # Parameters
582    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
583    ///   does not natively use transaction IDs, the stack preserves the ID provided in
584    ///   the request and returns it here to allow for asynchronous tracking.
585    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
586    ///   - `unit_id`: if transport is tcp
587    ///   - `slave_addr`: if transport is serial
588    /// - `status`: The status word indicating if the device is busy.
589    /// - `event_count`: The number of successful messages processed by the device.
590    fn get_comm_event_counter_response(
591        &mut self,
592        txn_id: u16,
593        unit_id_slave_addr: UnitIdOrSlaveAddr,
594        status: u16,
595        event_count: u16,
596    );
597
598    /// Called when a Get Comm Event Log response (FC 12) is received.
599    ///
600    /// # Parameters
601    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
602    ///   does not natively use transaction IDs, the stack preserves the ID provided in
603    ///   the request and returns it here to allow for asynchronous tracking.
604    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
605    ///   - `unit_id`: if transport is tcp
606    ///   - `slave_addr`: if transport is serial
607    /// - `status`: The status word indicating device state.
608    /// - `event_count`: Number of successful messages processed.
609    /// - `message_count`: Quantity of messages processed since the last restart.
610    /// - `events`: Raw byte array containing the device's internal event log.
611    fn get_comm_event_log_response(
612        &mut self,
613        txn_id: u16,
614        unit_id_slave_addr: UnitIdOrSlaveAddr,
615        status: u16,
616        event_count: u16,
617        message_count: u16,
618        events: &[u8],
619    );
620
621    /// Called when a Report Server ID response (FC 17) is received.
622    ///
623    /// # Parameters
624    /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
625    ///   does not natively use transaction IDs, the stack preserves the ID provided in
626    ///   the request and returns it here to allow for asynchronous tracking.
627    /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
628    ///   - `unit_id`: if transport is tcp
629    ///   - `slave_addr`: if transport is serial
630    /// - `data`: Raw identity/status data provided by the manufacturer.
631    fn report_server_id_response(
632        &mut self,
633        txn_id: u16,
634        unit_id_slave_addr: UnitIdOrSlaveAddr,
635        data: &[u8],
636    );
637}