zproto/error/
binary.rs

1//! Error types for the Zaber's Binary protocol.
2
3use super::{SerialDeviceInUseOrDisconnectedError, TryIntoSendError};
4use crate::binary::Message;
5
6macro_rules! impl_binary_error {
7	($name:ident) => {
8		impl $name {
9			/// Create a new error.
10			pub(crate) const fn new(message: Message) -> Self {
11				$name(message)
12			}
13		}
14
15		impl AsRef<Message> for $name {
16			fn as_ref(&self) -> &Message {
17				&self.0
18			}
19		}
20
21		impl From<$name> for Message {
22			fn from(other: $name) -> Self {
23				other.0
24			}
25		}
26	};
27}
28
29/// A Binary command failed and an Error (`255`) response was received.
30#[derive(Debug, PartialEq, Eq)]
31#[cfg_attr(all(doc, feature = "doc_cfg"), doc(cfg(feature = "binary")))]
32pub struct BinaryCommandFailureError(Message);
33
34impl_error_display! {
35	BinaryCommandFailureError,
36	self => "command failed: [{}] {}",
37	self.code(),
38	self.name().unwrap_or("<Unknown error code>")
39}
40impl_binary_error! { BinaryCommandFailureError }
41
42impl BinaryCommandFailureError {
43	/// Get the name of the error.
44	///
45	/// If the error code is not recognized, `None` is returned.
46	pub fn name(&self) -> Option<&'static str> {
47		binary_code::name(self.code())
48	}
49
50	/// Get the error code.
51	pub fn code(&self) -> i32 {
52		self.0.data::<i32>().unwrap()
53	}
54}
55
56/// A Binary response came from an unexpected target.
57#[derive(Debug, PartialEq, Eq)]
58#[cfg_attr(all(doc, feature = "doc_cfg"), doc(cfg(feature = "binary")))]
59pub struct BinaryUnexpectedTargetError(Message);
60
61impl_error_display! {
62	BinaryUnexpectedTargetError,
63	self => "unexpected response target: {}", self.0
64}
65impl_binary_error! { BinaryUnexpectedTargetError }
66
67/// A Binary response had an unexpected message ID.
68#[derive(Debug, PartialEq, Eq)]
69#[cfg_attr(all(doc, feature = "doc_cfg"), doc(cfg(feature = "binary")))]
70pub struct BinaryUnexpectedIdError(Message);
71
72impl_error_display! {
73	BinaryUnexpectedIdError,
74	self => "unexpected response message ID: {}", self.0
75}
76impl_binary_error! { BinaryUnexpectedIdError }
77
78/// A Binary response had an unexpected Binary command code.
79#[derive(Debug, PartialEq, Eq)]
80#[cfg_attr(all(doc, feature = "doc_cfg"), doc(cfg(feature = "binary")))]
81pub struct BinaryUnexpectedCommandError(Message);
82
83impl_error_display! {
84	BinaryUnexpectedCommandError,
85	self => "unexpected response command code: {}", self.0
86}
87impl_binary_error! { BinaryUnexpectedCommandError }
88
89error_enum! {
90/// Received an unexpected Binary response.
91#[derive(Debug, PartialEq, Eq)]
92#[cfg_attr(
93	all(doc, feature = "doc_cfg"),
94	doc(cfg(feature = "binary"))
95)]
96pub enum BinaryUnexpectedError {
97	Target(BinaryUnexpectedTargetError),
98	Id(BinaryUnexpectedIdError),
99	Command(BinaryUnexpectedCommandError),
100}
101}
102
103error_enum! {
104	/// Any error returned by the [`binary`](crate::binary) module.
105	#[derive(Debug)]
106	#[non_exhaustive]
107	#[cfg_attr(
108	all(doc, feature = "doc_cfg"),
109	doc(cfg(feature = "binary"))
110)]
111	pub enum BinaryError {
112		SerialDeviceInUseOrDisconnected(SerialDeviceInUseOrDisconnectedError),
113		Io(std::io::Error),
114		TryIntoSend(TryIntoSendError),
115		CommandFailure(BinaryCommandFailureError),
116		UnexpectedTarget(BinaryUnexpectedTargetError),
117		UnexpectedId(BinaryUnexpectedIdError),
118		UnexpectedCommand(BinaryUnexpectedCommandError),
119	}
120
121	impl From<BinaryUnexpectedError> {
122		Target => UnexpectedTarget,
123		Id => UnexpectedId,
124		Command => UnexpectedCommand,
125	}
126}
127impl_is_timeout! { BinaryError }
128impl_is_io! { BinaryError }
129impl_from_serialport_error! { BinaryError }
130
131macro_rules! define_error_codes {
132    // Entry point.
133    //
134    // Serves to concatenate the parts of the name before defining the constants.
135    (
136        $(
137            $num:literal: $($name_word:ident)+
138        ),+
139        $(,)?
140    ) => {
141        paste::paste! {
142            define_error_codes!{@with_concatenated_name
143                $(
144                    $num: $($name_word)+, [< $($name_word:camel)+ >]
145                 ),+
146            }
147        }
148    };
149    (@with_concatenated_name
150        $(
151            $num:literal: $($name_word:ident)+, $name:ident
152        ),+
153    ) => {
154        paste::paste! {
155            #[cfg_attr(
156    all(doc, feature = "doc_cfg"),
157    doc(cfg(feature = "binary"))
158)]
159            pub mod binary_code {
160                //! Binary error codes.
161                //!
162                //! The codes in numerical order are:
163                #![doc =
164                $( "* `" $num "`: [`" $name:snake:upper "`]\n\n" )+
165                ]
166
167                $(
168                    #[doc = $(" " $name_word " ")+ "(code `" $num "`)." ]
169                    pub const [< $name:snake:upper >] : i32 = $num;
170                )+
171
172                /// Get the name of an error code.
173                ///
174                /// If the error code is not recognized, `None` is returned.
175                /// The contents of the returned string may change.
176                pub const fn name(code: i32) -> Option<&'static str> {
177                    match code {
178                        $(
179                            $num => Some(stringify!($($name_word)+)),
180                        )+
181                        _ => None,
182                    }
183                }
184            }
185        }
186    };
187}
188
189define_error_codes! {
190	1: Cannot Home,
191	2: Device Number Invalid,
192	14: Voltage Low,
193	15: Voltage High,
194	18: Stored Position Invalid,
195	20: Absolute Position Invalid,
196	21: Relative Position Invalid,
197	22: Velocity Invalid,
198	36: Restore Settings Data Invalid,
199	37: Resolution Invalid,
200	38: Run Current Invalid,
201	39: Hold Current Invalid,
202	41: Home Speed Invalid,
203	42: Speed Invalid,
204	43: Acceleration Invalid,
205	44: Maximum Position Invalid,
206	45: Current Position Invalid,
207	47: Offset Invalid,
208	48: Alias Invalid,
209	53: Setting Invalid,
210	64: Command Invalid,
211	65: Park State Invalid,
212	67: Temperature High,
213	68: Digital Input Pin Invalid,
214	71: Digital Output Pin Invalid,
215	74: Digital Output Mask Invalid,
216	76: Analog Input Pin Invalid,
217	78: Move Index Number Invalid,
218	79: Index Distance Invalid,
219	80: Cycle Distance Invalid,
220	81: Filter Holder Id Invalid,
221	87: Absolute Force Invalid,
222	101: Auto Reply Disabled Mode Invalid,
223	102: Message Id Mode Invalid,
224	103: Home Status Invalid,
225	105: Auto Home Disabled Mode Invalid,
226	106: Minimum Position Invalid,
227	107: Knob Disabled Mode Invalid,
228	108: Knob Direction Invalid,
229	109: Knob Movement Mode Invalid,
230	110: Knob Jog Size Invalid,
231	111: Knob Velocity Scale Invalid,
232	112: Knob Velocity Profile Invalid,
233	113: Acceleration Only Invalid,
234	114: Deceleration Only Invalid,
235	115: Move Tracking Mode Invalid,
236	116: Manual Move Tracking Disabled Mode Invalid,
237	117: Move Tracking Period Invalid,
238	118: Closed Loop Mode Invalid,
239	119: Slip Tracking Period Invalid,
240	120: Stall Timeout Invalid,
241	122: Baud Rate Invalid,
242	123: Protocol Invalid,
243	124: Baud Rate or Protocol Invalid,
244	255: Busy,
245	257: System Error,
246	401: Storage Full,
247	1600: Save Position Invalid,
248	1601: Save Position Not Homed,
249	1700: Return Position Invalid,
250	1800: Move Position Invalid,
251	1801: Move Position Not Homed,
252	6501: Device Parked,
253	9001: Driver Disabled,
254	9301: Peripheral Inactive,
255}
256
257#[cfg(test)]
258mod test {
259	use super::binary_code::*;
260	use super::*;
261	use static_assertions::{assert_impl_all, const_assert, const_assert_eq};
262
263	// Make sure the error enums are at most 3 words large (the same size as a String).
264	// This will minimize the size of Result<R, Error>.
265	const _WORD_SIZE: usize = std::mem::size_of::<&usize>();
266	const_assert_eq!(std::mem::size_of::<Message>(), 8);
267	const_assert!(
268		std::mem::size_of::<BinaryUnexpectedError>() < std::mem::size_of::<Message>() + _WORD_SIZE
269	);
270	const_assert_eq!(std::mem::size_of::<BinaryError>(), 3 * _WORD_SIZE);
271
272	assert_impl_all!(BinaryError: From<BinaryUnexpectedError>);
273	assert_impl_all!(BinaryUnexpectedError: TryFrom<BinaryError>);
274
275	#[test]
276	fn binary_error_code_names() {
277		assert_eq!(name(PERIPHERAL_INACTIVE), Some("Peripheral Inactive"));
278		assert_eq!(
279			name(MANUAL_MOVE_TRACKING_DISABLED_MODE_INVALID),
280			Some("Manual Move Tracking Disabled Mode Invalid")
281		);
282		assert_eq!(name(9999999), None);
283	}
284}