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#[cfg(feature = "traffic")]
92/// Direction of raw Modbus frame traffic observed by the client stack.
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
94pub enum TrafficDirection {
95 /// Outgoing request ADU sent by the client.
96 Tx,
97 /// Incoming response ADU received by the client.
98 Rx,
99}
100
101#[cfg(feature = "traffic")]
102/// Optional raw-frame traffic notifications emitted by the client stack.
103///
104/// This trait is opt-in and enabled only with the `traffic` feature. A
105/// blanket no-op implementation is provided for all app types so applications
106/// are never forced to implement it.
107pub trait TrafficNotifier {
108 /// Called when a request frame is sent.
109 fn on_tx_frame(&mut self, _txn_id: u16, _unit_id_slave_addr: UnitIdOrSlaveAddr, _frame: &[u8]) {
110 }
111
112 /// Called when a response frame is received.
113 fn on_rx_frame(&mut self, _txn_id: u16, _unit_id_slave_addr: UnitIdOrSlaveAddr, _frame: &[u8]) {
114 }
115
116 /// Called when sending a request frame failed.
117 fn on_tx_error(
118 &mut self,
119 _txn_id: u16,
120 _unit_id_slave_addr: UnitIdOrSlaveAddr,
121 _error: MbusError,
122 _frame: &[u8],
123 ) {
124 }
125
126 /// Called when processing/receiving a response frame failed.
127 fn on_rx_error(
128 &mut self,
129 _txn_id: u16,
130 _unit_id_slave_addr: UnitIdOrSlaveAddr,
131 _error: MbusError,
132 _frame: &[u8],
133 ) {
134 }
135}
136
137/// Trait defining the expected response handling for coil-related Modbus operations.
138///
139/// Implementors of this trait to deliver the responses to the application layer,
140/// allowing application developers to process the coil data and update their application state accordingly.
141///
142/// ## When Each Callback Is Fired
143/// - `read_coils_response`: after a successful FC 0x01 response for a multi-coil read.
144/// - `read_single_coil_response`: convenience callback when quantity was 1.
145/// - `write_single_coil_response`: after a successful FC 0x05 echo/ack response.
146/// - `write_multiple_coils_response`: after a successful FC 0x0F response containing
147/// start address and quantity written by the server.
148///
149/// ## Data Semantics
150/// - Address values are Modbus data-model addresses exactly as acknowledged by the server.
151/// - Boolean coil values follow Modbus conventions: `true` = ON (`0xFF00` in FC 0x05 request),
152/// `false` = OFF (`0x0000`).
153#[cfg(feature = "coils")]
154pub trait CoilResponse {
155 /// Handles a Read Coils response by invoking the appropriate application callback with the coil states.
156 ///
157 /// # Parameters
158 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
159 /// does not natively use transaction IDs, the stack preserves the ID provided in
160 /// the request and returns it here to allow for asynchronous tracking.
161 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
162 /// - `unit_id`: if transport is tcp
163 /// - `slave_addr`: if transport is serial
164 /// - `coils`: A wrapper containing the bit-packed boolean statuses of the requested coils.
165 fn read_coils_response(
166 &mut self,
167 txn_id: u16,
168 unit_id_slave_addr: UnitIdOrSlaveAddr,
169 coils: &Coils,
170 );
171
172 /// Handles a Read Single Coil response.
173 ///
174 /// # Parameters
175 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
176 /// does not natively use transaction IDs, the stack preserves the ID provided in
177 /// the request and returns it here to allow for asynchronous tracking.
178 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
179 /// - `unit_id`: if transport is tcp
180 /// - `slave_addr`: if transport is serial
181 /// - `address`: The exact address of the single coil that was read.
182 /// - `value`: The boolean state of the coil (`true` = ON, `false` = OFF).
183 fn read_single_coil_response(
184 &mut self,
185 txn_id: u16,
186 unit_id_slave_addr: UnitIdOrSlaveAddr,
187 address: u16,
188 value: bool,
189 );
190
191 /// Handles a Write Single Coil response, confirming the state change.
192 ///
193 /// # Parameters
194 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
195 /// does not natively use transaction IDs, the stack preserves the ID provided in
196 /// the request and returns it here to allow for asynchronous tracking.
197 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
198 /// - `unit_id`: if transport is tcp
199 /// - `slave_addr`: if transport is serial
200 /// - `address`: The address of the coil that was successfully written.
201 /// - `value`: The boolean state applied to the coil (`true` = ON, `false` = OFF).
202 fn write_single_coil_response(
203 &mut self,
204 txn_id: u16,
205 unit_id_slave_addr: UnitIdOrSlaveAddr,
206 address: u16,
207 value: bool,
208 );
209
210 /// Handles a Write Multiple Coils response, confirming the bulk state change.
211 ///
212 /// # Parameters
213 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
214 /// does not natively use transaction IDs, the stack preserves the ID provided in
215 /// the request and returns it here to allow for asynchronous tracking.
216 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
217 /// - `unit_id`: if transport is tcp
218 /// - `slave_addr`: if transport is serial
219 /// - `address`: The starting address where the bulk write began.
220 /// - `quantity`: The total number of consecutive coils updated.
221 fn write_multiple_coils_response(
222 &mut self,
223 txn_id: u16,
224 unit_id_slave_addr: UnitIdOrSlaveAddr,
225 address: u16,
226 quantity: u16,
227 );
228}
229
230/// Trait defining the expected response handling for FIFO Queue Modbus operations.
231///
232/// ## When Callback Is Fired
233/// - `read_fifo_queue_response` is invoked after a successful FC 0x18 response.
234///
235/// ## Data Semantics
236/// - `fifo_queue` contains values in server-returned order.
237/// - Quantity in the payload may vary between calls depending on device state.
238///
239/// ## Implementation Guidance
240/// non-blocking because it runs in the `poll()` execution path.
241#[cfg(feature = "fifo")]
242pub trait FifoQueueResponse {
243 /// Handles a Read FIFO Queue response.
244 ///
245 /// # Parameters
246 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
247 /// does not natively use transaction IDs, the stack preserves the ID provided in
248 /// the request and returns it here to allow for asynchronous tracking.
249 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
250 /// - `unit_id`: if transport is tcp
251 /// - `slave_addr`: if transport is serial
252 /// - `fifo_queue`: A `FifoQueue` struct containing the values pulled from the queue.
253 fn read_fifo_queue_response(
254 &mut self,
255 txn_id: u16,
256 unit_id_slave_addr: UnitIdOrSlaveAddr,
257 fifo_queue: &FifoQueue,
258 );
259}
260
261/// Trait defining the expected response handling for File Record Modbus operations.
262///
263/// ## When Each Callback Is Fired
264/// - `read_file_record_response`: after successful FC 0x14 response parsing.
265/// - `write_file_record_response`: after successful FC 0x15 acknowledgement.
266///
267/// ## Data Semantics
268/// - For read responses, each `SubRequestParams` entry reflects one returned record chunk.
269/// - Per Modbus spec, the response does not echo `file_number` or `record_number`; those
270/// fields are therefore reported as `0` in callback data and should not be used as identity.
271#[cfg(feature = "file-record")]
272pub trait FileRecordResponse {
273 /// Handles a Read File Record response.
274 ///
275 /// # Parameters
276 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
277 /// does not natively use transaction IDs, the stack preserves the ID provided in
278 /// the request and returns it here to allow for asynchronous tracking.
279 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
280 /// - `unit_id`: if transport is tcp
281 /// - `slave_addr`: if transport is serial
282 /// - `data`: A slice containing the sub-request responses. Note that `file_number` and `record_number`
283 ///
284 /// are not returned by the server in the response PDU and will be set to 0 in the parameters.
285 fn read_file_record_response(
286 &mut self,
287 txn_id: u16,
288 unit_id_slave_addr: UnitIdOrSlaveAddr,
289 data: &[SubRequestParams],
290 );
291
292 /// Handles a Write File Record response, confirming the write was successful.
293 ///
294 /// # Parameters
295 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
296 /// does not natively use transaction IDs, the stack preserves the ID provided in
297 /// the request and returns it here to allow for asynchronous tracking.
298 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
299 /// - `unit_id`: if transport is tcp
300 /// - `slave_addr`: if transport is serial
301 fn write_file_record_response(&mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr);
302}
303
304/// Defines callbacks for handling responses to Modbus register-related requests.
305///
306/// Implementors of this trait can process the data received from a Modbus server
307/// and update their application state accordingly. Each method corresponds to a
308/// specific Modbus register operation response.
309///
310/// ## Callback Mapping
311/// - FC 0x03: `read_multiple_holding_registers_response`, `read_single_holding_register_response`
312/// - FC 0x04: `read_multiple_input_registers_response`, `read_single_input_register_response`
313/// - FC 0x06: `write_single_register_response`
314/// - FC 0x10: `write_multiple_registers_response`
315/// - FC 0x16: `mask_write_register_response`
316/// - FC 0x17: `read_write_multiple_registers_response`
317///
318/// ## Data Semantics
319/// - Register values are 16-bit words (`u16`) already decoded from Modbus big-endian byte pairs.
320/// - Address and quantity values are echoed/validated values corresponding to the original request.
321#[cfg(feature = "registers")]
322pub trait RegisterResponse {
323 /// Handles a response for a `Read Input Registers` (FC 0x04) request.
324 ///
325 /// # Parameters
326 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
327 /// does not natively use transaction IDs, the stack preserves the ID provided in
328 /// the request and returns it here to allow for asynchronous tracking.
329 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
330 /// - `unit_id`: if transport is tcp
331 /// - `slave_addr`: if transport is serial
332 /// - `registers`: A `Registers` struct containing the values of the read input registers.
333 fn read_multiple_input_registers_response(
334 &mut self,
335 txn_id: u16,
336 unit_id_slave_addr: UnitIdOrSlaveAddr,
337 registers: &Registers,
338 );
339
340 /// Handles a response for a `Read Single Input Register` (FC 0x04) request.
341 ///
342 /// # Parameters
343 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
344 /// does not natively use transaction IDs, the stack preserves the ID provided in
345 /// the request and returns it here to allow for asynchronous tracking.
346 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
347 /// - `unit_id`: if transport is tcp
348 /// - `slave_addr`: if transport is serial
349 /// - `address`: The address of the register that was read.
350 /// - `value`: The value of the read register.
351 fn read_single_input_register_response(
352 &mut self,
353 txn_id: u16,
354 unit_id_slave_addr: UnitIdOrSlaveAddr,
355 address: u16,
356 value: u16,
357 );
358
359 /// Handles a response for a `Read Holding Registers` (FC 0x03) request.
360 ///
361 /// # Parameters
362 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
363 /// does not natively use transaction IDs, the stack preserves the ID provided in
364 /// the request and returns it here to allow for asynchronous tracking.
365 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
366 /// - `unit_id`: if transport is tcp
367 /// - `slave_addr`: if transport is serial
368 /// - `registers`: A `Registers` struct containing the values of the read holding registers.
369 fn read_multiple_holding_registers_response(
370 &mut self,
371 txn_id: u16,
372 unit_id_slave_addr: UnitIdOrSlaveAddr,
373 registers: &Registers,
374 );
375
376 /// Handles a response for a `Write Single Register` (FC 0x06) request, confirming a successful write.
377 ///
378 /// # Parameters
379 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
380 /// does not natively use transaction IDs, the stack preserves the ID provided in
381 /// the request and returns it here to allow for asynchronous tracking.
382 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
383 /// - `unit_id`: if transport is tcp
384 /// - `slave_addr`: if transport is serial
385 /// - `address`: The address of the register that was written.
386 /// - `value`: The value that was written to the register.
387 fn write_single_register_response(
388 &mut self,
389 txn_id: u16,
390 unit_id_slave_addr: UnitIdOrSlaveAddr,
391 address: u16,
392 value: u16,
393 );
394
395 /// Handles a response for a `Write Multiple Registers` (FC 0x10) request, confirming a successful write.
396 ///
397 /// # Parameters
398 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
399 /// does not natively use transaction IDs, the stack preserves the ID provided in
400 /// the request and returns it here to allow for asynchronous tracking.
401 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
402 /// - `unit_id`: if transport is tcp
403 /// - `slave_addr`: if transport is serial
404 /// - `starting_address`: The starting address of the registers that were written.
405 /// - `quantity`: The number of registers that were written.
406 fn write_multiple_registers_response(
407 &mut self,
408 txn_id: u16,
409 unit_id_slave_addr: UnitIdOrSlaveAddr,
410 starting_address: u16,
411 quantity: u16,
412 );
413
414 /// Handles a response for a `Read/Write Multiple Registers` (FC 0x17) request.
415 ///
416 /// # Parameters
417 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
418 /// does not natively use transaction IDs, the stack preserves the ID provided in
419 /// the request and returns it here to allow for asynchronous tracking.
420 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
421 /// - `unit_id`: if transport is tcp
422 /// - `slave_addr`: if transport is serial
423 /// - `registers`: A `Registers` struct containing the values of the registers that were read.
424 fn read_write_multiple_registers_response(
425 &mut self,
426 txn_id: u16,
427 unit_id_slave_addr: UnitIdOrSlaveAddr,
428 registers: &Registers,
429 );
430
431 /// Handles a response for a single register read request.
432 ///
433 /// This is a convenience callback for when only one register is requested.
434 ///
435 /// # Parameters
436 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
437 /// does not natively use transaction IDs, the stack preserves the ID provided in
438 /// the request and returns it here to allow for asynchronous tracking.
439 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
440 /// - `unit_id`: if transport is tcp
441 /// - `slave_addr`: if transport is serial
442 /// - `address`: The address of the register that was read.
443 /// - `value`: The value of the read register.
444 fn read_single_register_response(
445 &mut self,
446 txn_id: u16,
447 unit_id_slave_addr: UnitIdOrSlaveAddr,
448 address: u16,
449 value: u16,
450 );
451
452 /// Handles a response for a single holding register write request.
453 ///
454 /// # Parameters
455 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
456 /// does not natively use transaction IDs, the stack preserves the ID provided in
457 /// the request and returns it here to allow for asynchronous tracking.
458 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
459 /// - `unit_id`: if transport is tcp
460 /// - `slave_addr`: if transport is serial
461 /// - `address`: The address of the register that was written.
462 /// - `value`: The value that was written to the register.
463 fn read_single_holding_register_response(
464 &mut self,
465 txn_id: u16,
466 unit_id_slave_addr: UnitIdOrSlaveAddr,
467 address: u16,
468 value: u16,
469 );
470
471 /// Handles a response for a `Mask Write Register` (FC 0x16) request, confirming a successful operation.
472 ///
473 /// # Parameters
474 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
475 /// does not natively use transaction IDs, the stack preserves the ID provided in
476 /// the request and returns it here to allow for asynchronous tracking.
477 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
478 /// - `unit_id`: if transport is tcp
479 /// - `slave_addr`: if transport is serial
480 fn mask_write_register_response(&mut self, txn_id: u16, unit_id_slave_addr: UnitIdOrSlaveAddr);
481}
482
483/// Defines callbacks for handling responses to Modbus discrete input-related requests.
484///
485/// Implementors of this trait can process the data received from a Modbus server
486/// and update their application state accordingly.
487///
488/// ## When Each Callback Is Fired
489/// - `read_multiple_discrete_inputs_response`: after successful FC 0x02 with quantity > 1.
490/// - `read_single_discrete_input_response`: convenience callback when quantity was 1.
491///
492/// ## Data Semantics
493/// - `DiscreteInputs` stores bit-packed values; use helper methods on the type instead of
494/// manually decoding bit offsets in application code.
495#[cfg(feature = "discrete-inputs")]
496pub trait DiscreteInputResponse {
497 /// Handles a response for a `Read Discrete Inputs` (FC 0x02) request.
498 ///
499 /// # Parameters
500 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
501 /// does not natively use transaction IDs, the stack preserves the ID provided in
502 /// the request and returns it here to allow for asynchronous tracking.
503 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
504 /// - `unit_id`: if transport is tcp
505 /// - `slave_addr`: if transport is serial
506 /// - `discrete_inputs`: A `DiscreteInputs` struct containing the states of the read inputs.
507 fn read_multiple_discrete_inputs_response(
508 &mut self,
509 txn_id: u16,
510 unit_id_slave_addr: UnitIdOrSlaveAddr,
511 discrete_inputs: &DiscreteInputs,
512 );
513
514 /// Handles a response for a single discrete input read request.
515 ///
516 /// # Parameters
517 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
518 /// does not natively use transaction IDs, the stack preserves the ID provided in
519 /// the request and returns it here to allow for asynchronous tracking.
520 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
521 /// - `unit_id`: if transport is tcp
522 /// - `slave_addr`: if transport is serial
523 /// - `address`: The address of the input that was read.
524 /// - `value`: The boolean state of the read input.
525 fn read_single_discrete_input_response(
526 &mut self,
527 txn_id: u16,
528 unit_id_slave_addr: UnitIdOrSlaveAddr,
529 address: u16,
530 value: bool,
531 );
532}
533
534/// Trait for handling Diagnostics-family responses.
535///
536/// ## Callback Mapping
537/// - FC 0x2B / MEI 0x0E: `read_device_identification_response`
538/// - FC 0x2B / other MEI: `encapsulated_interface_transport_response`
539/// - FC 0x07: `read_exception_status_response`
540/// - FC 0x08: `diagnostics_response`
541/// - FC 0x0B: `get_comm_event_counter_response`
542/// - FC 0x0C: `get_comm_event_log_response`
543/// - FC 0x11: `report_server_id_response`
544///
545/// ## Data Semantics
546/// - `mei_type`, `sub_function`, counters, and event buffers are already validated and decoded.
547/// - Large payloads (event logs, generic encapsulated transport data) should typically be copied
548/// or forwarded quickly, then processed outside the callback hot path.
549#[cfg(feature = "diagnostics")]
550pub trait DiagnosticsResponse {
551 /// Called when a Read Device Identification response is received.
552 ///
553 /// Implementors can use this callback to process the device identity info (Vendor, Product Code, etc.).
554 ///
555 /// # Parameters
556 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
557 /// does not natively use transaction IDs, the stack preserves the ID provided in
558 /// the request and returns it here to allow for asynchronous tracking.
559 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
560 /// - `unit_id`: if transport is tcp
561 /// - `slave_addr`: if transport is serial
562 /// - `response`: Extracted device identification strings.
563 fn read_device_identification_response(
564 &mut self,
565 txn_id: u16,
566 unit_id_slave_addr: UnitIdOrSlaveAddr,
567 response: &DeviceIdentificationResponse,
568 );
569
570 /// Called when a generic Encapsulated Interface Transport response (FC 43) is received.
571 ///
572 /// # Parameters
573 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
574 /// does not natively use transaction IDs, the stack preserves the ID provided in
575 /// the request and returns it here to allow for asynchronous tracking.
576 /// - `unit_id_slave_addr`: The unit ID of the device that responded.
577 /// - `unit_id`: if transport is tcp
578 /// - `slave_addr`: if transport is serial
579 /// - `mei_type`: The MEI type returned in the response.
580 /// - `data`: The data payload returned in the response.
581 fn encapsulated_interface_transport_response(
582 &mut self,
583 txn_id: u16,
584 unit_id_slave_addr: UnitIdOrSlaveAddr,
585 mei_type: EncapsulatedInterfaceType,
586 data: &[u8],
587 );
588
589 /// Called when a Read Exception Status response (FC 07) is received.
590 ///
591 /// # Parameters
592 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
593 /// does not natively use transaction IDs, the stack preserves the ID provided in
594 /// the request and returns it here to allow for asynchronous tracking.
595 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
596 /// - `unit_id`: if transport is tcp
597 /// - `slave_addr`: if transport is serial
598 /// - `status`: The 8-bit exception status code returned by the server.
599 fn read_exception_status_response(
600 &mut self,
601 txn_id: u16,
602 unit_id_slave_addr: UnitIdOrSlaveAddr,
603 status: u8,
604 );
605
606 /// Called when a Diagnostics response (FC 08) is received.
607 ///
608 /// # Parameters
609 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
610 /// does not natively use transaction IDs, the stack preserves the ID provided in
611 /// the request and returns it here to allow for asynchronous tracking.
612 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
613 /// - `unit_id`: if transport is tcp
614 /// - `slave_addr`: if transport is serial
615 /// - `sub_function`: The sub-function code confirming the diagnostic test.
616 /// - `data`: Data payload returned by the diagnostic test (e.g., echoed loopback data).
617 fn diagnostics_response(
618 &mut self,
619 txn_id: u16,
620 unit_id_slave_addr: UnitIdOrSlaveAddr,
621 sub_function: DiagnosticSubFunction,
622 data: &[u16],
623 );
624
625 /// Called when a Get Comm Event Counter response (FC 11) is received.
626 ///
627 /// # Parameters
628 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
629 /// does not natively use transaction IDs, the stack preserves the ID provided in
630 /// the request and returns it here to allow for asynchronous tracking.
631 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
632 /// - `unit_id`: if transport is tcp
633 /// - `slave_addr`: if transport is serial
634 /// - `status`: The status word indicating if the device is busy.
635 /// - `event_count`: The number of successful messages processed by the device.
636 fn get_comm_event_counter_response(
637 &mut self,
638 txn_id: u16,
639 unit_id_slave_addr: UnitIdOrSlaveAddr,
640 status: u16,
641 event_count: u16,
642 );
643
644 /// Called when a Get Comm Event Log response (FC 12) is received.
645 ///
646 /// # Parameters
647 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
648 /// does not natively use transaction IDs, the stack preserves the ID provided in
649 /// the request and returns it here to allow for asynchronous tracking.
650 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
651 /// - `unit_id`: if transport is tcp
652 /// - `slave_addr`: if transport is serial
653 /// - `status`: The status word indicating device state.
654 /// - `event_count`: Number of successful messages processed.
655 /// - `message_count`: Quantity of messages processed since the last restart.
656 /// - `events`: Raw byte array containing the device's internal event log.
657 fn get_comm_event_log_response(
658 &mut self,
659 txn_id: u16,
660 unit_id_slave_addr: UnitIdOrSlaveAddr,
661 status: u16,
662 event_count: u16,
663 message_count: u16,
664 events: &[u8],
665 );
666
667 /// Called when a Report Server ID response (FC 17) is received.
668 ///
669 /// # Parameters
670 /// - `txn_id`: Transaction ID of the original request. While Modbus Serial (RTU/ASCII)
671 /// does not natively use transaction IDs, the stack preserves the ID provided in
672 /// the request and returns it here to allow for asynchronous tracking.
673 /// - `unit_id_slave_addr`: The target Modbus unit ID or slave address.
674 /// - `unit_id`: if transport is tcp
675 /// - `slave_addr`: if transport is serial
676 /// - `data`: Raw identity/status data provided by the manufacturer.
677 fn report_server_id_response(
678 &mut self,
679 txn_id: u16,
680 unit_id_slave_addr: UnitIdOrSlaveAddr,
681 data: &[u8],
682 );
683}