modbus_rtu/
exception.rs

1/// Enumerates the Modbus application exceptions returned by a slave device,
2/// including a catch-all for codes not defined by the specification.
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4#[repr(u8)]
5pub enum Exception {
6    /// Exception code not defined by this crate; preserves the raw value for
7    /// diagnostics.
8    Undefined(u8) = 0x00,
9
10    /// Exception code `0x01`: the function is not supported or the device state
11    /// does not allow the requested operation (for example an unconfigured unit
12    /// asked to return registers).
13    IllegalFunction = 0x01,
14
15    /// Exception code `0x02`: the combination of starting address and length
16    /// extends beyond the valid data range implemented by the device.
17    IllegalDataAddress = 0x02,
18
19    /// Exception code `0x03`: a value in the request payload is not acceptable
20    /// to the device, or the payload structure is inconsistent (for example an
21    /// incorrect implied length).
22    IllegalDataValue = 0x03,
23
24    /// Exception code `0x04`: an unrecoverable fault occurred while the device
25    /// attempted the requested action.
26    DeviceFailure = 0x04,
27
28    /// Exception code `0x05`: the device accepted the request but needs a long
29    /// interval to finish; the client should poll later (e.g., Poll Program
30    /// Complete).
31    Acknowledge = 0x05,
32
33    /// Exception code `0x06`: the device is busy processing a long-duration
34    /// command and cannot handle the new request yet; the client should retry
35    /// later.
36    DeviceBusy = 0x06,
37
38    /// Exception code `0x08`: while accessing extended file records (function
39    /// codes 20/21, reference type 6) the device detected a memory parity error.
40    MemoryParityError = 0x08,
41
42    /// Exception code `0x0A`: a gateway could not allocate an internal path
43    /// between the input and output ports, often due to misconfiguration or
44    /// overload.
45    GatewayPathUnavailable = 0x0A,
46
47    /// Exception code `0x0B`: a gateway forwarded the request but received no
48    /// response from the target device, which may be offline or unreachable.
49    GatewayTargetDeviceFailedToRespond = 0x0B,
50}
51
52impl Exception {
53    /// Returns the Modbus exception code associated with this variant.
54    ///
55    /// # Examples
56    /// ```rust
57    /// use modbus_rtu::Exception;
58    ///
59    /// assert_eq!(Exception::Undefined(0x7F).as_code(), 0x7F);
60    /// assert_eq!(Exception::IllegalDataAddress.as_code(), 0x02);
61    /// assert_eq!(Exception::DeviceBusy.as_code(), 0x06);
62    /// ```
63    ///
64    pub const fn as_code(&self) -> u8 {
65        match self {
66            Exception::Undefined(code) => *code,
67            Exception::IllegalFunction => 0x01,
68            Exception::IllegalDataAddress => 0x02,
69            Exception::IllegalDataValue => 0x03,
70            Exception::DeviceFailure => 0x04,
71            Exception::Acknowledge => 0x05,
72            Exception::DeviceBusy => 0x06,
73            Exception::MemoryParityError => 0x08,
74            Exception::GatewayPathUnavailable => 0x0A,
75            Exception::GatewayTargetDeviceFailedToRespond => 0x0B,
76        }
77    }
78
79    /// Converts a Modbus exception code into its corresponding [`Exception`]
80    /// variant. Undefined codes are wrapped in [`Exception::Undefined`].
81    ///
82    /// # Examples
83    /// ```rust
84    /// use modbus_rtu::Exception;
85    ///
86    /// assert_eq!(Exception::from_code(0x05), Exception::Acknowledge);
87    /// assert_eq!(Exception::from_code(0xFF), Exception::Undefined(0xFF));
88    /// ```
89    ///
90    pub fn from_code(code: u8) -> Self {
91        match code {
92            0x01 => Self::IllegalFunction,
93            0x02 => Self::IllegalDataAddress,
94            0x03 => Self::IllegalDataValue,
95            0x04 => Self::DeviceFailure,
96            0x05 => Self::Acknowledge,
97            0x06 => Self::DeviceBusy,
98            0x08 => Self::MemoryParityError,
99            0x0A => Self::GatewayPathUnavailable,
100            0x0B => Self::GatewayTargetDeviceFailedToRespond,
101            code => Self::Undefined(code),
102        }
103    }
104}
105
106impl std::fmt::Display for Exception {
107    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
108        write!(
109            f,
110            "{}",
111            match self {
112                Exception::Undefined(code) => format!("undefined exception code 0x{:02X}", code),
113                Exception::IllegalFunction => "illegal function".to_string(),
114                Exception::IllegalDataAddress => "illegal data address".to_string(),
115                Exception::IllegalDataValue => "illegal data value".to_string(),
116                Exception::DeviceFailure => "device failure".to_string(),
117                Exception::Acknowledge => "acknowlegde".to_string(),
118                Exception::DeviceBusy => "device busy".to_string(),
119                Exception::MemoryParityError => "memory parity error".to_string(),
120                Exception::GatewayPathUnavailable => "gateway path unavailable".to_string(),
121                Exception::GatewayTargetDeviceFailedToRespond =>
122                    "gateway target device failed to respond".to_string(),
123            }
124        )
125    }
126}