Skip to main content

rusty_modbus_codec/response/
exception.rs

1//! Exception response type.
2
3use crate::error::{DecodeError, EncodeError};
4use crate::request::Encode;
5use rusty_modbus_types::{ExceptionCode, FunctionCode};
6
7/// Modbus exception response.
8///
9/// Sent by a server when a request cannot be processed. The function code
10/// byte on the wire has bit 7 set (0x80 flag).
11#[derive(Debug, Clone, Copy)]
12pub struct ExceptionResponse {
13    /// The original function code (without the 0x80 flag).
14    pub function_code: FunctionCode,
15    /// The exception code indicating the error condition.
16    pub exception_code: ExceptionCode,
17}
18
19impl ExceptionResponse {
20    /// Decode from the raw function code byte and remaining data.
21    ///
22    /// The `fc_byte` parameter is the raw byte from the wire WITH the 0x80
23    /// exception flag still set. The flag is stripped internally using
24    /// `FunctionCode::from_exception_raw`.
25    ///
26    /// # Errors
27    ///
28    /// Returns `DecodeError::Truncated` if `data` is too short.
29    /// Returns `DecodeError::LengthMismatch` if `data` has extra bytes.
30    /// Returns `DecodeError::UnknownFunctionCode` if the function code is not
31    /// recognized.
32    /// Returns `DecodeError::UnknownExceptionCode` if the exception code is
33    /// not recognized.
34    pub fn decode(fc_byte: u8, data: &[u8]) -> Result<Self, DecodeError> {
35        DecodeError::check_exact_len(data, 1)?;
36        let raw_function_code = fc_byte & 0x7F;
37        if raw_function_code == 0 {
38            return Err(DecodeError::UnknownFunctionCode(raw_function_code));
39        }
40        let function_code = FunctionCode::from_exception_raw(fc_byte);
41        let exception_code = ExceptionCode::from_raw(data[0]);
42        Ok(Self {
43            function_code,
44            exception_code,
45        })
46    }
47}
48
49impl Encode for ExceptionResponse {
50    fn encode_into(&self, buf: &mut [u8]) -> Result<usize, EncodeError> {
51        let len = self.encoded_len();
52        if buf.len() < len {
53            return Err(EncodeError::BufferTooSmall {
54                required: len,
55                available: buf.len(),
56            });
57        }
58        EncodeError::check_pdu_len(len)?;
59        buf[0] = self.function_code.exception_code();
60        buf[1] = self.exception_code.code();
61        Ok(len)
62    }
63
64    fn encoded_len(&self) -> usize {
65        2
66    }
67}