1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5pub enum DecodeError {
6 Truncated {
8 expected: usize,
10 actual: usize,
12 },
13 LengthMismatch {
15 expected: usize,
17 actual: usize,
19 },
20 PduTooLarge {
22 length: usize,
24 maximum: usize,
26 },
27 UnknownFunctionCode(u8),
29 ByteCountMismatch {
31 declared: usize,
33 actual: usize,
35 },
36 ByteCountOutOfRange {
38 count: usize,
40 minimum: usize,
42 maximum: usize,
44 },
45 QuantityOutOfRange {
47 quantity: u16,
49 },
50 InvalidCoilValue(u16),
52 InvalidReferenceType(u8),
54 InvalidFileRecordLength {
56 length: usize,
58 },
59 FileRecordOutOfRange {
61 file_number: u16,
63 record_number: u16,
65 record_length: u16,
67 },
68 UnknownMeiType(u8),
70 UnknownExceptionCode(u8),
72 UnknownDiagnosticSubFunction(u16),
74 InvalidDiagnosticDataLength {
76 length: usize,
78 },
79 InvalidDeviceIdCode(u8),
81 InvalidDeviceIdConformityLevel(u8),
83 InvalidDeviceIdMoreFollows(u8),
85 InvalidDeviceIdNextObjectId(u8),
87 InvalidDeviceIdObjectCount(u8),
89}
90
91impl DecodeError {
92 pub(crate) fn check_exact_len(data: &[u8], expected: usize) -> Result<(), Self> {
94 match data.len().cmp(&expected) {
95 core::cmp::Ordering::Less => Err(Self::Truncated {
96 expected,
97 actual: data.len(),
98 }),
99 core::cmp::Ordering::Equal => Ok(()),
100 core::cmp::Ordering::Greater => Err(Self::LengthMismatch {
101 expected,
102 actual: data.len(),
103 }),
104 }
105 }
106}
107
108impl core::fmt::Display for DecodeError {
109 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
110 match self {
111 Self::Truncated { expected, actual } => {
112 write!(f, "PDU truncated: expected {expected} bytes, got {actual}")
113 }
114 Self::LengthMismatch { expected, actual } => {
115 write!(
116 f,
117 "PDU length mismatch: expected {expected} bytes, got {actual}"
118 )
119 }
120 Self::PduTooLarge { length, maximum } => {
121 write!(f, "PDU too large: {length} bytes (maximum {maximum})")
122 }
123 Self::UnknownFunctionCode(fc) => write!(f, "unknown function code: {fc:#04X}"),
124 Self::ByteCountMismatch { declared, actual } => {
125 write!(
126 f,
127 "byte count mismatch: declared {declared}, actual {actual}"
128 )
129 }
130 Self::ByteCountOutOfRange {
131 count,
132 minimum,
133 maximum,
134 } => write!(
135 f,
136 "byte count out of range: {count} (expected {minimum}..={maximum})"
137 ),
138 Self::QuantityOutOfRange { quantity } => {
139 write!(f, "quantity out of range: {quantity}")
140 }
141 Self::InvalidCoilValue(v) => write!(f, "invalid coil value: {v:#06X}"),
142 Self::InvalidReferenceType(rt) => write!(f, "invalid reference type: {rt}"),
143 Self::InvalidFileRecordLength { length } => {
144 write!(f, "invalid file-record data length: {length}")
145 }
146 Self::FileRecordOutOfRange {
147 file_number,
148 record_number,
149 record_length,
150 } => write!(
151 f,
152 "file record out of range: file {file_number}, record {record_number}, length {record_length}"
153 ),
154 Self::UnknownMeiType(mt) => write!(f, "unknown MEI type: {mt:#04X}"),
155 Self::UnknownExceptionCode(ec) => write!(f, "unknown exception code: {ec:#04X}"),
156 Self::UnknownDiagnosticSubFunction(sf) => {
157 write!(f, "unknown diagnostic sub-function: {sf:#06X}")
158 }
159 Self::InvalidDiagnosticDataLength { length } => {
160 write!(
161 f,
162 "invalid diagnostics data length: {length} (expected a multiple of 2)"
163 )
164 }
165 Self::InvalidDeviceIdCode(code) => write!(f, "invalid device ID code: {code:#04X}"),
166 Self::InvalidDeviceIdConformityLevel(level) => {
167 write!(f, "invalid device ID conformity level: {level:#04X}")
168 }
169 Self::InvalidDeviceIdMoreFollows(value) => {
170 write!(f, "invalid device ID More Follows value: {value:#04X}")
171 }
172 Self::InvalidDeviceIdNextObjectId(object_id) => write!(
173 f,
174 "invalid device ID Next Object ID with More Follows = 0x00: {object_id:#04X}"
175 ),
176 Self::InvalidDeviceIdObjectCount(count) => {
177 write!(f, "invalid individual device ID object count: {count}")
178 }
179 }
180 }
181}
182
183#[cfg(feature = "std")]
184impl std::error::Error for DecodeError {}
185
186#[derive(Debug, Clone, Copy, PartialEq, Eq)]
188pub enum EncodeError {
189 BufferTooSmall {
191 required: usize,
193 available: usize,
195 },
196 PduTooLarge {
198 length: usize,
200 maximum: usize,
202 },
203 QuantityOutOfRange {
205 quantity: u16,
207 },
208 ByteCountMismatch {
210 declared: usize,
212 actual: usize,
214 },
215 ByteCountOutOfRange {
217 count: usize,
219 minimum: usize,
221 maximum: usize,
223 },
224 InvalidReferenceType(u8),
226 InvalidFileRecordLength {
228 length: usize,
230 },
231 FileRecordOutOfRange {
233 file_number: u16,
235 record_number: u16,
237 record_length: u16,
239 },
240 InvalidDiagnosticDataLength {
242 length: usize,
244 },
245}
246
247impl core::fmt::Display for EncodeError {
248 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
249 match self {
250 Self::BufferTooSmall {
251 required,
252 available,
253 } => {
254 write!(
255 f,
256 "buffer too small: need {required} bytes, have {available}"
257 )
258 }
259 Self::PduTooLarge { length, maximum } => {
260 write!(f, "PDU too large: {length} bytes (maximum {maximum})")
261 }
262 Self::QuantityOutOfRange { quantity } => {
263 write!(f, "quantity out of range: {quantity}")
264 }
265 Self::ByteCountMismatch { declared, actual } => {
266 write!(
267 f,
268 "byte count mismatch: declared {declared}, actual {actual}"
269 )
270 }
271 Self::ByteCountOutOfRange {
272 count,
273 minimum,
274 maximum,
275 } => write!(
276 f,
277 "byte count out of range: {count} (expected {minimum}..={maximum})"
278 ),
279 Self::InvalidReferenceType(rt) => write!(f, "invalid reference type: {rt}"),
280 Self::InvalidFileRecordLength { length } => {
281 write!(f, "invalid file-record data length: {length}")
282 }
283 Self::FileRecordOutOfRange {
284 file_number,
285 record_number,
286 record_length,
287 } => write!(
288 f,
289 "file record out of range: file {file_number}, record {record_number}, length {record_length}"
290 ),
291 Self::InvalidDiagnosticDataLength { length } => {
292 write!(
293 f,
294 "invalid diagnostics data length: {length} (expected a multiple of 2)"
295 )
296 }
297 }
298 }
299}
300
301impl EncodeError {
302 pub(crate) fn check_pdu_len(length: usize) -> Result<(), Self> {
303 let maximum = rusty_modbus_types::MAX_PDU_SIZE;
304 if length > maximum {
305 Err(Self::PduTooLarge { length, maximum })
306 } else {
307 Ok(())
308 }
309 }
310
311 pub(crate) fn check_quantity(quantity: u16, max: u16) -> Result<(), Self> {
312 if quantity == 0 || quantity > max {
313 Err(Self::QuantityOutOfRange { quantity })
314 } else {
315 Ok(())
316 }
317 }
318
319 pub(crate) fn check_byte_count(declared: usize, actual: usize) -> Result<(), Self> {
320 if declared == actual {
321 Ok(())
322 } else {
323 Err(Self::ByteCountMismatch { declared, actual })
324 }
325 }
326
327 pub(crate) fn check_byte_count_range(
328 count: usize,
329 minimum: usize,
330 maximum: usize,
331 ) -> Result<(), Self> {
332 if (minimum..=maximum).contains(&count) {
333 Ok(())
334 } else {
335 Err(Self::ByteCountOutOfRange {
336 count,
337 minimum,
338 maximum,
339 })
340 }
341 }
342}
343
344#[cfg(feature = "std")]
345impl std::error::Error for EncodeError {}