autd3_driver/
error.rs

1use std::{convert::Infallible, time::Duration};
2
3use autd3_core::{
4    datagram::CombinedError,
5    firmware::{
6        FOCI_STM_BUF_SIZE_MAX, FOCI_STM_FOCI_NUM_MAX, FOCI_STM_FOCI_NUM_MIN, FOCI_STM_LOWER_X,
7        FOCI_STM_LOWER_Y, FOCI_STM_LOWER_Z, FOCI_STM_UPPER_X, FOCI_STM_UPPER_Y, FOCI_STM_UPPER_Z,
8        GAIN_STM_BUF_SIZE_MAX, MOD_BUF_SIZE_MAX, MOD_BUF_SIZE_MIN, PulseWidthError,
9        STM_BUF_SIZE_MIN, SamplingConfigError,
10    },
11    gain::GainError,
12    link::LinkError,
13    modulation::ModulationError,
14};
15
16/// A interface for error handling in autd3-driver.
17#[derive(Debug, PartialEq, Clone)]
18#[non_exhaustive]
19pub enum AUTDDriverError {
20    /// Invalid silencer completion time.
21    InvalidSilencerCompletionTime(Duration),
22    /// Silencer completion time is out of range.
23    SilencerCompletionTimeOutOfRange(Duration),
24    /// Sampling config error
25    SamplingConfig(SamplingConfigError),
26
27    /// Invalid STM period.
28    STMPeriodInvalid(usize, Duration),
29
30    /// Modulation buffer size is out of range.
31    ModulationSizeOutOfRange(usize),
32
33    /// FociSTM buffer size is out of range.
34    FociSTMTotalSizeOutOfRange(usize),
35    /// Number of foci is out of range.
36    FociSTMNumFociOutOfRange(usize),
37    /// FociSTM point is out of range.
38    FociSTMPointOutOfRange(f32, f32, f32),
39    /// GainSTM buffer size is out of range.
40    GainSTMSizeOutOfRange(usize),
41
42    /// GPIO output type is not supported.
43    UnsupportedGPIOOutputType(String),
44
45    /// Pulse width error.
46    PulseWidth(PulseWidthError),
47
48    /// Error in the modulation.
49    Modulation(ModulationError),
50    /// Error in the gain.
51    Gain(GainError),
52    /// Error in the Link.
53    Link(LinkError),
54
55    /// Unknown group key.
56    UnknownKey(String),
57    /// Unused group key.
58    UnusedKey(String),
59
60    /// Failed to confirm the response from the device.
61    ConfirmResponseFailed,
62
63    /// Failed to read firmware version.
64    ReadFirmwareVersionFailed(Vec<bool>),
65
66    /// Invalid date time.
67    InvalidDateTime,
68
69    /// Firmware version mismatch.
70    FirmwareVersionMismatch,
71
72    /// Unsupported operation.
73    UnsupportedOperation,
74    /// Unsupported firmware.
75    UnsupportedFirmware,
76
77    /// Not supported tag.
78    ///
79    /// Occurs when the software is not compatible with the firmware.
80    NotSupportedTag,
81    #[doc(hidden)]
82    InvalidMessageID,
83    #[doc(hidden)]
84    InvalidInfoType,
85    #[doc(hidden)]
86    InvalidGainSTMMode,
87    #[doc(hidden)]
88    UnknownFirmwareError(u8),
89    /// Invalid segment transition.
90    InvalidSegmentTransition,
91    /// Invalid transition mode.
92    InvalidTransitionMode,
93    /// Miss transition time.
94    MissTransitionTime,
95    /// Silencer cannot complete phase/intensity interpolation in the specified sampling period.
96    InvalidSilencerSettings,
97}
98
99impl From<SamplingConfigError> for AUTDDriverError {
100    fn from(e: SamplingConfigError) -> Self {
101        AUTDDriverError::SamplingConfig(e)
102    }
103}
104
105impl From<PulseWidthError> for AUTDDriverError {
106    fn from(e: PulseWidthError) -> Self {
107        AUTDDriverError::PulseWidth(e)
108    }
109}
110
111impl From<ModulationError> for AUTDDriverError {
112    fn from(e: ModulationError) -> Self {
113        AUTDDriverError::Modulation(e)
114    }
115}
116
117impl From<GainError> for AUTDDriverError {
118    fn from(e: GainError) -> Self {
119        AUTDDriverError::Gain(e)
120    }
121}
122
123impl From<LinkError> for AUTDDriverError {
124    fn from(e: LinkError) -> Self {
125        AUTDDriverError::Link(e)
126    }
127}
128
129impl std::fmt::Display for AUTDDriverError {
130    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131        match self {
132            AUTDDriverError::InvalidSilencerCompletionTime(d) => write!(
133                f,
134                "Silencer completion time ({:?}) must be a multiple of the ultrasound period",
135                d
136            ),
137            AUTDDriverError::SilencerCompletionTimeOutOfRange(d) => {
138                write!(f, "Silencer completion time ({:?}) is out of range", d)
139            }
140            AUTDDriverError::SamplingConfig(e) => write!(f, "{}", e),
141            AUTDDriverError::STMPeriodInvalid(size, period) => write!(
142                f,
143                "STM sampling period ({:?}/{}) must be integer",
144                period, size
145            ),
146            AUTDDriverError::ModulationSizeOutOfRange(size) => write!(
147                f,
148                "Modulation buffer size ({}) is out of range ([{}, {}])",
149                size, MOD_BUF_SIZE_MIN, MOD_BUF_SIZE_MAX
150            ),
151            AUTDDriverError::FociSTMTotalSizeOutOfRange(size) => write!(
152                f,
153                "The number of total foci ({}) is out of range ([{}, {}])",
154                size, STM_BUF_SIZE_MIN, FOCI_STM_BUF_SIZE_MAX
155            ),
156            AUTDDriverError::FociSTMNumFociOutOfRange(size) => write!(
157                f,
158                "Number of foci ({}) is out of range ([{}, {}])",
159                size, FOCI_STM_FOCI_NUM_MIN, FOCI_STM_FOCI_NUM_MAX
160            ),
161            AUTDDriverError::FociSTMPointOutOfRange(x, y, z) => write!(
162                f,
163                "Point coordinate ({}, {}, {}) is out of range ([{}, {}], [{}, {}], [{}, {}])",
164                x,
165                y,
166                z,
167                FOCI_STM_LOWER_X,
168                FOCI_STM_UPPER_X,
169                FOCI_STM_LOWER_Y,
170                FOCI_STM_UPPER_Y,
171                FOCI_STM_LOWER_Z,
172                FOCI_STM_UPPER_Z,
173            ),
174            AUTDDriverError::GainSTMSizeOutOfRange(size) => write!(
175                f,
176                "GainSTM size ({}) is out of range ([{}, {}])",
177                size, STM_BUF_SIZE_MIN, GAIN_STM_BUF_SIZE_MAX
178            ),
179            AUTDDriverError::UnsupportedGPIOOutputType(t) => {
180                write!(f, "GPIO output type ({}) is not supported", t)
181            }
182            AUTDDriverError::PulseWidth(e) => write!(f, "{}", e),
183            AUTDDriverError::Modulation(e) => write!(f, "{}", e),
184            AUTDDriverError::Gain(e) => write!(f, "{}", e),
185            AUTDDriverError::Link(e) => write!(f, "{}", e),
186            AUTDDriverError::UnknownKey(key) => write!(f, "Unknown group key({})", key),
187            AUTDDriverError::UnusedKey(key) => write!(f, "Unused group key({})", key),
188            AUTDDriverError::ConfirmResponseFailed => {
189                write!(f, "Failed to confirm the response from the device")
190            }
191            AUTDDriverError::ReadFirmwareVersionFailed(versions) => write!(
192                f,
193                "Read firmware info failed: {}",
194                versions
195                    .iter()
196                    .enumerate()
197                    .filter(|&(_, &b)| !b)
198                    .map(|(i, _)| i.to_string())
199                    .collect::<Vec<_>>()
200                    .join(", ")
201            ),
202            AUTDDriverError::InvalidDateTime => write!(f, "The input data is invalid."),
203            AUTDDriverError::FirmwareVersionMismatch => write!(f, "Firmware version mismatch"),
204            AUTDDriverError::UnsupportedOperation => write!(f, "Unsupported operation"),
205            AUTDDriverError::UnsupportedFirmware => write!(f, "Unsupported firmware"),
206            AUTDDriverError::NotSupportedTag => write!(f, "Not supported tag"),
207            AUTDDriverError::InvalidMessageID => write!(f, "Invalid message ID"),
208            AUTDDriverError::InvalidInfoType => write!(f, "Invalid info type"),
209            AUTDDriverError::InvalidGainSTMMode => write!(f, "Invalid GainSTM mode"),
210            AUTDDriverError::UnknownFirmwareError(e) => write!(f, "Unknown firmware error: {}", e),
211            AUTDDriverError::InvalidSegmentTransition => write!(f, "Invalid segment transition"),
212            AUTDDriverError::InvalidTransitionMode => write!(f, "Invalid transition mode"),
213            AUTDDriverError::MissTransitionTime => write!(f, "Miss transition time"),
214            AUTDDriverError::InvalidSilencerSettings => write!(
215                f,
216                "Silencer cannot complete phase/intensity interpolation in the specified sampling period. Please lower the sampling frequency or make the completion time of Silencer longer than the sampling period of the AM/STM."
217            ),
218        }
219    }
220}
221
222impl std::error::Error for AUTDDriverError {
223    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
224        match self {
225            AUTDDriverError::SamplingConfig(e) => Some(e),
226            AUTDDriverError::PulseWidth(e) => Some(e),
227            AUTDDriverError::Modulation(e) => Some(e),
228            AUTDDriverError::Gain(e) => Some(e),
229            AUTDDriverError::Link(e) => Some(e),
230            AUTDDriverError::InvalidSilencerCompletionTime(_)
231            | AUTDDriverError::SilencerCompletionTimeOutOfRange(_)
232            | AUTDDriverError::STMPeriodInvalid(_, _)
233            | AUTDDriverError::ModulationSizeOutOfRange(_)
234            | AUTDDriverError::FociSTMTotalSizeOutOfRange(_)
235            | AUTDDriverError::FociSTMNumFociOutOfRange(_)
236            | AUTDDriverError::FociSTMPointOutOfRange(_, _, _)
237            | AUTDDriverError::GainSTMSizeOutOfRange(_)
238            | AUTDDriverError::UnsupportedGPIOOutputType(_)
239            | AUTDDriverError::UnknownKey(_)
240            | AUTDDriverError::UnusedKey(_)
241            | AUTDDriverError::ConfirmResponseFailed
242            | AUTDDriverError::ReadFirmwareVersionFailed(_)
243            | AUTDDriverError::InvalidDateTime
244            | AUTDDriverError::FirmwareVersionMismatch
245            | AUTDDriverError::UnsupportedOperation
246            | AUTDDriverError::UnsupportedFirmware
247            | AUTDDriverError::NotSupportedTag
248            | AUTDDriverError::InvalidMessageID
249            | AUTDDriverError::InvalidInfoType
250            | AUTDDriverError::InvalidGainSTMMode
251            | AUTDDriverError::UnknownFirmwareError(_)
252            | AUTDDriverError::InvalidSegmentTransition
253            | AUTDDriverError::InvalidTransitionMode
254            | AUTDDriverError::MissTransitionTime
255            | AUTDDriverError::InvalidSilencerSettings => None,
256        }
257    }
258}
259
260impl From<Infallible> for AUTDDriverError {
261    fn from(_: Infallible) -> Self {
262        unreachable!()
263    }
264}
265
266impl<E1, E2> From<CombinedError<E1, E2>> for AUTDDriverError
267where
268    E1: std::error::Error,
269    E2: std::error::Error,
270    AUTDDriverError: From<E1> + From<E2>,
271{
272    fn from(err: CombinedError<E1, E2>) -> Self {
273        match err {
274            CombinedError::E1(e) => AUTDDriverError::from(e),
275            CombinedError::E2(e) => AUTDDriverError::from(e),
276        }
277    }
278}
279
280#[cfg(test)]
281mod tests {
282    use std::error::Error;
283
284    use super::*;
285
286    #[rstest::rstest]
287    #[case(SamplingConfigError::FreqInvalid(1 * autd3_core::common::Hz), AUTDDriverError::SamplingConfig(SamplingConfigError::FreqInvalid(1 * autd3_core::common::Hz)))]
288    #[case(
289        PulseWidthError::PulseWidthOutOfRange(1),
290        AUTDDriverError::PulseWidth(PulseWidthError::PulseWidthOutOfRange(1))
291    )]
292    #[case(
293        ModulationError::new("test"),
294        AUTDDriverError::Modulation(ModulationError::new("test"))
295    )]
296    #[case(GainError::new("test"), AUTDDriverError::Gain(GainError::new("test")))]
297    #[case(LinkError::new("test"), AUTDDriverError::Link(LinkError::new("test")))]
298    fn from<E>(#[case] source: E, #[case] expected: AUTDDriverError)
299    where
300        E: std::error::Error + Clone,
301        AUTDDriverError: From<E>,
302    {
303        let err: AUTDDriverError = source.clone().into();
304        assert_eq!(expected, err);
305    }
306
307    #[rstest::rstest]
308    #[case(
309        "Silencer completion time (1ms) must be a multiple of the ultrasound period",
310        AUTDDriverError::InvalidSilencerCompletionTime(Duration::from_millis(1))
311    )]
312    #[case(
313        "Silencer completion time (1ms) is out of range",
314        AUTDDriverError::SilencerCompletionTimeOutOfRange(Duration::from_millis(1))
315    )]
316    #[case(
317        "Sampling frequency (1 Hz) must divide the ultrasound frequency",
318        AUTDDriverError::SamplingConfig(SamplingConfigError::FreqInvalid(1 * autd3_core::common::Hz))
319    )]
320    #[case(
321        "STM sampling period (1ms/10) must be integer",
322        AUTDDriverError::STMPeriodInvalid(10, Duration::from_millis(1))
323    )]
324    #[case(
325        "Modulation buffer size (0) is out of range ([2, 65536])",
326        AUTDDriverError::ModulationSizeOutOfRange(0)
327    )]
328    #[case(
329        "The number of total foci (0) is out of range ([2, 65536])",
330        AUTDDriverError::FociSTMTotalSizeOutOfRange(0)
331    )]
332    #[case(
333        "Number of foci (0) is out of range ([1, 8])",
334        AUTDDriverError::FociSTMNumFociOutOfRange(0)
335    )]
336    #[case(
337        &format!("Point coordinate (1, 2, 3) is out of range ([{}, {}], [{}, {}], [{}, {}])", FOCI_STM_LOWER_X, FOCI_STM_UPPER_X, FOCI_STM_LOWER_Y, FOCI_STM_UPPER_Y, FOCI_STM_LOWER_Z, FOCI_STM_UPPER_Z),
338        AUTDDriverError::FociSTMPointOutOfRange(1.0, 2.0, 3.0)
339    )]
340    #[case(
341        "GainSTM size (0) is out of range ([2, 1024])",
342        AUTDDriverError::GainSTMSizeOutOfRange(0)
343    )]
344    #[case(
345        "GPIO output type (test) is not supported",
346        AUTDDriverError::UnsupportedGPIOOutputType("test".to_string())
347    )]
348    #[case(
349        "Pulse width (1) is out of range [0, 512)",
350        AUTDDriverError::PulseWidth(PulseWidthError::PulseWidthOutOfRange(1))
351    )]
352    #[case("test", AUTDDriverError::Modulation(ModulationError::new("test")))]
353    #[case("test", AUTDDriverError::Gain(GainError::new("test")))]
354    #[case("test", AUTDDriverError::Link(LinkError::new("test")))]
355    #[case(
356        "Unknown group key(test_key)",
357        AUTDDriverError::UnknownKey("test_key".to_string())
358    )]
359    #[case(
360        "Unused group key(test_key)",
361        AUTDDriverError::UnusedKey("test_key".to_string())
362    )]
363    #[case(
364        "Failed to confirm the response from the device",
365        AUTDDriverError::ConfirmResponseFailed
366    )]
367    #[case(
368        "Read firmware info failed: 0, 2",
369        AUTDDriverError::ReadFirmwareVersionFailed(vec![false, true, false])
370    )]
371    #[case("The input data is invalid.", AUTDDriverError::InvalidDateTime)]
372    #[case("Firmware version mismatch", AUTDDriverError::FirmwareVersionMismatch)]
373    #[case("Unsupported operation", AUTDDriverError::UnsupportedOperation)]
374    #[case("Unsupported firmware", AUTDDriverError::UnsupportedFirmware)]
375    #[case("Not supported tag", AUTDDriverError::NotSupportedTag)]
376    #[case("Invalid message ID", AUTDDriverError::InvalidMessageID)]
377    #[case("Invalid info type", AUTDDriverError::InvalidInfoType)]
378    #[case("Invalid GainSTM mode", AUTDDriverError::InvalidGainSTMMode)]
379    #[case(
380        "Unknown firmware error: 42",
381        AUTDDriverError::UnknownFirmwareError(42)
382    )]
383    #[case(
384        "Invalid segment transition",
385        AUTDDriverError::InvalidSegmentTransition
386    )]
387    #[case("Invalid transition mode", AUTDDriverError::InvalidTransitionMode)]
388    #[case("Miss transition time", AUTDDriverError::MissTransitionTime)]
389    #[case(
390        "Silencer cannot complete phase/intensity interpolation in the specified sampling period. Please lower the sampling frequency or make the completion time of Silencer longer than the sampling period of the AM/STM.",
391        AUTDDriverError::InvalidSilencerSettings
392    )]
393    fn display(#[case] msg: &str, #[case] err: AUTDDriverError) {
394        assert_eq!(msg, format!("{}", err))
395    }
396
397    #[rstest::rstest]
398    #[case(
399        false,
400        AUTDDriverError::InvalidSilencerCompletionTime(Duration::from_millis(1))
401    )]
402    #[case(
403        false,
404        AUTDDriverError::SilencerCompletionTimeOutOfRange(Duration::from_millis(1))
405    )]
406    #[case(true, AUTDDriverError::SamplingConfig(SamplingConfigError::FreqInvalid(1 * autd3_core::common::Hz)))]
407    #[case(false, AUTDDriverError::STMPeriodInvalid(10, Duration::from_millis(1)))]
408    #[case(false, AUTDDriverError::ModulationSizeOutOfRange(0))]
409    #[case(false, AUTDDriverError::FociSTMTotalSizeOutOfRange(0))]
410    #[case(false, AUTDDriverError::FociSTMNumFociOutOfRange(0))]
411    #[case(false, AUTDDriverError::FociSTMPointOutOfRange(1.0, 2.0, 3.0))]
412    #[case(false, AUTDDriverError::GainSTMSizeOutOfRange(0))]
413    #[case(false, AUTDDriverError::UnsupportedGPIOOutputType("test".to_string()))]
414    #[case(
415        true,
416        AUTDDriverError::PulseWidth(PulseWidthError::PulseWidthOutOfRange(1))
417    )]
418    #[case(true, AUTDDriverError::Modulation(ModulationError::new("test")))]
419    #[case(true, AUTDDriverError::Gain(GainError::new("test")))]
420    #[case(true, AUTDDriverError::Link(LinkError::new("test")))]
421    #[case(false, AUTDDriverError::UnknownKey("test_key".to_string()))]
422    #[case(false, AUTDDriverError::UnusedKey("test_key".to_string()))]
423    #[case(false, AUTDDriverError::ConfirmResponseFailed)]
424    #[case(false, AUTDDriverError::ReadFirmwareVersionFailed(vec![false, true, false]))]
425    #[case(false, AUTDDriverError::InvalidDateTime)]
426    #[case(false, AUTDDriverError::FirmwareVersionMismatch)]
427    #[case(false, AUTDDriverError::UnsupportedOperation)]
428    #[case(false, AUTDDriverError::UnsupportedFirmware)]
429    #[case(false, AUTDDriverError::NotSupportedTag)]
430    #[case(false, AUTDDriverError::InvalidMessageID)]
431    #[case(false, AUTDDriverError::InvalidInfoType)]
432    #[case(false, AUTDDriverError::InvalidGainSTMMode)]
433    #[case(false, AUTDDriverError::UnknownFirmwareError(42))]
434    #[case(false, AUTDDriverError::InvalidSegmentTransition)]
435    #[case(false, AUTDDriverError::InvalidTransitionMode)]
436    #[case(false, AUTDDriverError::MissTransitionTime)]
437    #[case(false, AUTDDriverError::InvalidSilencerSettings)]
438    fn source(#[case] has_source: bool, #[case] err: AUTDDriverError) {
439        assert_eq!(has_source, err.source().is_some());
440    }
441
442    #[test]
443    fn from_combined_error() {
444        let mod_err = ModulationError::new("modulation error");
445        let gain_err = GainError::new("gain error");
446        let combined_mod: CombinedError<ModulationError, GainError> =
447            CombinedError::E1(mod_err.clone());
448        let combined_gain: CombinedError<ModulationError, GainError> =
449            CombinedError::E2(gain_err.clone());
450
451        assert_eq!(AUTDDriverError::Modulation(mod_err), combined_mod.into());
452        assert_eq!(AUTDDriverError::Gain(gain_err), combined_gain.into());
453    }
454}