1use crate::encoding::{Reader, Writer};
2use crate::{DecodeError, EncodeError};
3
4#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6#[cfg_attr(feature = "defmt", derive(defmt::Format))]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8#[non_exhaustive]
9pub enum ExceptionCode {
10 IllegalFunction,
12 IllegalDataAddress,
14 IllegalDataValue,
16 ServerDeviceFailure,
18 Acknowledge,
20 ServerDeviceBusy,
22 MemoryParityError,
24 GatewayPathUnavailable,
26 GatewayTargetFailedToRespond,
28 Unknown(u8),
30}
31
32impl ExceptionCode {
33 pub const fn from_u8(value: u8) -> Self {
35 match value {
36 0x01 => Self::IllegalFunction,
37 0x02 => Self::IllegalDataAddress,
38 0x03 => Self::IllegalDataValue,
39 0x04 => Self::ServerDeviceFailure,
40 0x05 => Self::Acknowledge,
41 0x06 => Self::ServerDeviceBusy,
42 0x08 => Self::MemoryParityError,
43 0x0A => Self::GatewayPathUnavailable,
44 0x0B => Self::GatewayTargetFailedToRespond,
45 other => Self::Unknown(other),
46 }
47 }
48
49}
50
51impl core::fmt::Display for ExceptionCode {
52 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53 match self {
54 Self::IllegalFunction => write!(f, "illegal function (0x01)"),
55 Self::IllegalDataAddress => write!(f, "illegal data address (0x02)"),
56 Self::IllegalDataValue => write!(f, "illegal data value (0x03)"),
57 Self::ServerDeviceFailure => write!(f, "server device failure (0x04)"),
58 Self::Acknowledge => write!(f, "acknowledge (0x05)"),
59 Self::ServerDeviceBusy => write!(f, "server device busy (0x06)"),
60 Self::MemoryParityError => write!(f, "memory parity error (0x08)"),
61 Self::GatewayPathUnavailable => write!(f, "gateway path unavailable (0x0A)"),
62 Self::GatewayTargetFailedToRespond => {
63 write!(f, "gateway target failed to respond (0x0B)")
64 }
65 Self::Unknown(code) => write!(f, "unknown exception (0x{code:02X})"),
66 }
67 }
68}
69
70impl ExceptionCode {
71 pub const fn as_u8(self) -> u8 {
73 match self {
74 Self::IllegalFunction => 0x01,
75 Self::IllegalDataAddress => 0x02,
76 Self::IllegalDataValue => 0x03,
77 Self::ServerDeviceFailure => 0x04,
78 Self::Acknowledge => 0x05,
79 Self::ServerDeviceBusy => 0x06,
80 Self::MemoryParityError => 0x08,
81 Self::GatewayPathUnavailable => 0x0A,
82 Self::GatewayTargetFailedToRespond => 0x0B,
83 Self::Unknown(raw) => raw,
84 }
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq)]
89#[cfg_attr(feature = "defmt", derive(defmt::Format))]
90#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
91pub struct ExceptionResponse {
93 pub function_code: u8,
95 pub exception_code: ExceptionCode,
97}
98
99impl core::fmt::Display for ExceptionResponse {
100 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
101 write!(
102 f,
103 "exception on FC 0x{:02X}: {}",
104 self.function_code, self.exception_code
105 )
106 }
107}
108
109impl ExceptionResponse {
110 pub fn encode(&self, w: &mut Writer<'_>) -> Result<(), EncodeError> {
111 w.write_u8(self.function_code | 0x80)?;
112 w.write_u8(self.exception_code.as_u8())?;
113 Ok(())
114 }
115
116 pub fn decode(function_byte: u8, r: &mut Reader<'_>) -> Result<Self, DecodeError> {
117 if (function_byte & 0x80) == 0 {
118 return Err(DecodeError::InvalidFunctionCode);
119 }
120 let exception = r.read_u8()?;
121 Ok(Self {
122 function_code: function_byte & 0x7F,
123 exception_code: ExceptionCode::from_u8(exception),
124 })
125 }
126}
127
128#[cfg(test)]
129mod tests {
130 use super::{ExceptionCode, ExceptionResponse};
131 use crate::encoding::{Reader, Writer};
132
133 #[test]
134 fn roundtrip_exception_response() {
135 let mut buf = [0u8; 2];
136 let mut w = Writer::new(&mut buf);
137 let resp = ExceptionResponse {
138 function_code: 0x03,
139 exception_code: ExceptionCode::ServerDeviceBusy,
140 };
141 resp.encode(&mut w).unwrap();
142 assert_eq!(w.as_written(), &[0x83, 0x06]);
143
144 let mut r = Reader::new(w.as_written());
145 let fc = r.read_u8().unwrap();
146 let decoded = ExceptionResponse::decode(fc, &mut r).unwrap();
147 assert_eq!(decoded, resp);
148 }
149
150 #[test]
151 fn preserves_unknown_exception_codes() {
152 let mut r = Reader::new(&[0x11]);
153 let decoded = ExceptionResponse::decode(0x83, &mut r).unwrap();
154 assert_eq!(decoded.exception_code, ExceptionCode::Unknown(0x11));
155 }
156}