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