Skip to main content

mbus_core/errors/
mod.rs

1//! Modbus Error Module
2//!
3//! This module defines the centralized error handling for the Modbus stack.
4//! It provides the [`MbusError`] enum, which covers a wide range of error conditions
5//! including protocol-specific exceptions, parsing failures, transport-layer issues,
6//! and buffer management errors.
7//!
8//! The error types are designed to be compatible with `no_std` environments while
9//! providing descriptive error messages through the `Display` trait implementation.
10//!
11//! Modbus Specification Reference: V1.1b3, Section 7 (MODBUS Exception Responses).
12
13use core::fmt;
14
15/// Modbus exception codes as defined in the Modbus Application Protocol Specification V1.1b3.
16///
17/// These codes are used in exception responses (function code | 0x80) to indicate
18/// the type of error that occurred when processing a request.
19#[derive(Debug, Clone, Copy, PartialEq, Eq)]
20#[repr(u8)]
21pub enum ExceptionCode {
22    /// 0x01: Illegal Function - The function code is not supported by the server.
23    IllegalFunction = 0x01,
24    /// 0x02: Illegal Data Address - The addressed register does not exist.
25    IllegalDataAddress = 0x02,
26    /// 0x03: Illegal Data Value - The quantity of items to read/write is invalid.
27    IllegalDataValue = 0x03,
28    /// 0x04: Server Device Failure - Unrecoverable device failure.
29    ServerDeviceFailure = 0x04,
30}
31
32impl From<ExceptionCode> for u8 {
33    fn from(code: ExceptionCode) -> Self {
34        code as u8
35    }
36}
37
38/// Represents a Modbus error.
39#[derive(Debug, PartialEq, Eq, Clone, Copy)]
40pub enum MbusError {
41    /// An error occurred while parsing the Modbus ADU.
42    ParseError,
43    /// This is used for receieved frame is fundamentally malformed
44    BasicParseError,
45    /// The transaction timed out waiting for a response.
46    Timeout,
47    /// The server responded with a Modbus exception code.
48    ModbusException(u8),
49    /// An I/O error occurred during TCP communication.
50    IoError,
51    /// An unexpected error occurred.
52    Unexpected,
53    /// The connection was lost during an active transaction.
54    ConnectionLost,
55    /// The function code is not supported
56    UnsupportedFunction(u8),
57    /// The sub-function code is not available
58    ReservedSubFunction(u16),
59    /// The PDU length is invalid
60    InvalidPduLength,
61    /// The ADU length is invalid
62    InvalidAduLength,
63    /// Connection failed
64    ConnectionFailed,
65    /// Connection closed
66    ConnectionClosed,
67    /// The data was too large for the buffer
68    BufferTooSmall,
69    /// Buffer length is not matching
70    BufferLenMissmatch,
71    /// Failed to send data
72    SendFailed,
73    /// Invalid address
74    InvalidAddress,
75    /// Invalid offset
76    InvalidOffset,
77    /// Too many requests in flight, expected responses buffer is full
78    TooManyRequests,
79    /// Invalid function code
80    InvalidFunctionCode,
81    /// No retries left for the transaction
82    NoRetriesLeft,
83    /// Too many sub-requests in a PDU, Max allowed is 35
84    TooManyFileReadSubRequests,
85    /// File read PDU overflow, total length of file read sub-requests exceeds maximum allowed bytes per PDU
86    FileReadPduOverflow,
87    /// An unexpected response was received that does not match the expected response type for the transaction.
88    UnexpectedResponse,
89    /// The transport is invalid for the requested operation
90    InvalidTransport,
91    /// Invalid slave address
92    InvalidSlaveAddress,
93    /// Checksum error
94    ChecksumError,
95    /// Invalid configuration
96    InvalidConfiguration,
97    /// Invalid number of expected responses.
98    ///
99    /// For Modbus Serial transports, only one request may be in flight at a time,
100    /// so the expected-response queue size must be exactly `1`.
101    InvalidNumOfExpectedRsps,
102    /// Invalid data length
103    InvalidDataLen,
104    /// Invalid Quantity
105    InvalidQuantity,
106    /// Invalid Value
107    InvalidValue,
108    /// Invalid Masking value
109    InvalidAndMask,
110    /// Invalid Masking value
111    InvalidOrMask,
112    /// Invalid byte count
113    InvalidByteCount,
114    /// Invalid device identification
115    InvalidDeviceIdentification,
116    /// Invalid device id code
117    InvalidDeviceIdCode,
118    /// Invalid MEI type
119    InvalidMeiType,
120    /// Invalid broadcast address (0): Broadcast must be created explicitly.
121    /// Use `UnitIdOrSlaveAddr::new_broadcast_address()` to signal broadcast intent.
122    InvalidBroadcastAddress,
123    /// Broadcast not allowed.
124    ///
125    /// Note: This variant name contains a historical typo and is kept for
126    /// compatibility with existing code.
127    BroadcastNotAllowed,
128}
129
130impl MbusError {
131    /// Returns the canonical "broadcast not allowed" error.
132    ///
133    /// This helper exists to provide a correctly spelled API path while
134    /// preserving the legacy enum variant name for compatibility.
135    pub const fn broadcast_not_allowed() -> Self {
136        Self::BroadcastNotAllowed
137    }
138}
139
140impl fmt::Display for MbusError {
141    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
142        match self {
143            MbusError::ParseError => write!(
144                f,
145                "Parse error: An error occurred while parsing the Modbus ADU"
146            ),
147            MbusError::BasicParseError => write!(
148                f,
149                "Basic parse error: The received frame is fundamentally malformed"
150            ),
151            MbusError::Timeout => write!(
152                f,
153                "Timeout: The transaction timed out waiting for a response"
154            ),
155            MbusError::ModbusException(code) => write!(
156                f,
157                "Modbus exception: The server responded with exception code 0x{:02X}",
158                code
159            ),
160            MbusError::IoError => write!(
161                f,
162                "I/O error: An I/O error occurred during TCP communication"
163            ),
164            MbusError::Unexpected => write!(f, "Unexpected error: An unexpected error occurred"),
165            MbusError::ConnectionLost => write!(
166                f,
167                "Connection lost: The connection was lost during an active transaction"
168            ),
169            MbusError::UnsupportedFunction(code) => write!(
170                f,
171                "Unsupported function: Function code 0x{:02X} is not supported",
172                code
173            ),
174            MbusError::ReservedSubFunction(code) => write!(
175                f,
176                "Reserved sub-function: Sub-function code 0x{:04X} is not available",
177                code
178            ),
179            MbusError::InvalidPduLength => {
180                write!(f, "Invalid PDU length: The PDU length is invalid")
181            }
182            MbusError::InvalidAduLength => {
183                write!(f, "Invalid ADU length: The ADU length is invalid")
184            }
185            MbusError::ConnectionFailed => write!(f, "Connection failed"),
186            MbusError::ConnectionClosed => write!(f, "Connection closed"),
187            MbusError::BufferTooSmall => {
188                write!(f, "Buffer too small: The data was too large for the buffer")
189            }
190            MbusError::BufferLenMissmatch => {
191                write!(f, "Buffer length mismatch: Buffer length is not matching")
192            }
193            MbusError::SendFailed => write!(f, "Send failed: Failed to send data"),
194            MbusError::InvalidAddress => write!(f, "Invalid address"),
195            MbusError::TooManyRequests => {
196                write!(f, "Too many requests: Expected responses buffer is full")
197            }
198            MbusError::InvalidFunctionCode => write!(f, "Invalid function code"),
199            MbusError::NoRetriesLeft => write!(f, "No retries left for the transaction"),
200            MbusError::TooManyFileReadSubRequests => write!(
201                f,
202                "Too many sub-requests: Maximum of 35 sub-requests per PDU allowed"
203            ),
204            MbusError::FileReadPduOverflow => write!(
205                f,
206                "File read PDU overflow: Total length of file read sub-requests exceeds maximum allowed bytes per PDU"
207            ),
208            MbusError::UnexpectedResponse => write!(
209                f,
210                "Unexpected response: An unexpected response was received"
211            ),
212            MbusError::InvalidTransport => write!(
213                f,
214                "Invalid transport: The transport is invalid for the requested operation"
215            ),
216            MbusError::InvalidSlaveAddress => write!(
217                f,
218                "Invalid slave address: The provided slave address is invalid"
219            ),
220            MbusError::ChecksumError => write!(
221                f,
222                "Checksum error: The received frame has an invalid checksum"
223            ),
224            MbusError::InvalidConfiguration => write!(
225                f,
226                "Invalid configuration: The provided configuration is invalid"
227            ),
228            MbusError::InvalidNumOfExpectedRsps => write!(
229                f,
230                "Invalid number of expected responses: for serial transports the queue size N must be exactly 1"
231            ),
232            MbusError::InvalidDataLen => write!(
233                f,
234                "Invalid data length: The provided data length is invalid"
235            ),
236            MbusError::InvalidQuantity => {
237                write!(f, "Invalid quantity: The provided quantity is invalid")
238            }
239            MbusError::InvalidValue => write!(f, "Invalid value: The provided value is invalid"),
240            MbusError::InvalidAndMask => {
241                write!(f, "Invalid AND mask: The provided AND mask is invalid")
242            }
243            MbusError::InvalidOrMask => {
244                write!(f, "Invalid OR mask: The provided OR mask is invalid")
245            }
246            MbusError::InvalidByteCount => {
247                write!(f, "Invalid byte count: The provided byte count is invalid")
248            }
249            MbusError::InvalidDeviceIdentification => write!(
250                f,
251                "Invalid device identification: The provided device identification is invalid"
252            ),
253            MbusError::InvalidDeviceIdCode => write!(
254                f,
255                "Invalid device ID code: The provided device ID code is invalid"
256            ),
257            MbusError::InvalidMeiType => {
258                write!(f, "Invalid MEI type: The provided MEI type is invalid")
259            }
260            MbusError::InvalidBroadcastAddress => write!(
261                f,
262                "Invalid broadcast address: The provided broadcast address (0) is invalid. Must use UnitIdOrSlaveAddr::new_broadcast_address() instead."
263            ),
264            MbusError::BroadcastNotAllowed => {
265                write!(f, "Broadcast not allowed: Broadcast not allowed")
266            }
267            MbusError::InvalidOffset => write!(f, "Invalid offset: The provided offset is invalid"),
268        }
269    }
270}
271
272impl core::error::Error for MbusError {}