1use super::{SerialDeviceInUseOrDisconnectedError, TryIntoSendError};
4use crate::binary::Message;
5
6macro_rules! impl_binary_error {
7 ($name:ident) => {
8 impl $name {
9 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#[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 pub fn name(&self) -> Option<&'static str> {
47 binary_code::name(self.code())
48 }
49
50 pub fn code(&self) -> i32 {
52 self.0.data::<i32>().unwrap()
53 }
54}
55
56#[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#[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#[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#[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 #[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 (
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 #![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 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 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}