stepper_motion/
error.rs

1//! Error types for stepper-motion library.
2//!
3//! Provides unified error handling across configuration, motor control, and motion execution.
4
5use core::fmt;
6
7/// Result type alias using the library's Error type.
8pub type Result<T> = core::result::Result<T, Error>;
9
10/// Unified error type for all stepper-motion operations.
11#[derive(Debug, Clone, PartialEq)]
12pub enum Error {
13    /// Configuration parsing or validation error
14    Config(ConfigError),
15    /// Motor operation error
16    Motor(MotorError),
17    /// Motion profile or execution error
18    Motion(MotionError),
19    /// Trajectory lookup or execution error
20    Trajectory(TrajectoryError),
21}
22
23/// Configuration-related errors.
24#[derive(Debug, Clone, PartialEq)]
25pub enum ConfigError {
26    /// Failed to parse TOML configuration
27    ParseError(heapless::String<128>),
28    /// Invalid microstep value (must be power of 2: 1, 2, 4, 8, 16, 32, 64, 128, 256)
29    InvalidMicrosteps(u16),
30    /// Motor name not found in configuration
31    MotorNotFound(heapless::String<32>),
32    /// Trajectory name not found in configuration
33    TrajectoryNotFound(heapless::String<32>),
34    /// Duplicate motor name in configuration
35    DuplicateMotorName(heapless::String<32>),
36    /// Duplicate trajectory name in configuration
37    DuplicateTrajectoryName(heapless::String<32>),
38    /// Invalid velocity percent (must be 1-200)
39    InvalidVelocityPercent(u8),
40    /// Invalid acceleration percent (must be 1-200)
41    InvalidAccelerationPercent(u8),
42    /// Invalid gear ratio (must be > 0)
43    InvalidGearRatio(f32),
44    /// Invalid max velocity (must be > 0)
45    InvalidMaxVelocity(f32),
46    /// Invalid max acceleration (must be > 0)
47    InvalidMaxAcceleration(f32),
48    /// Invalid soft limits (min must be < max)
49    InvalidSoftLimits {
50        /// Minimum limit value
51        min: f32,
52        /// Maximum limit value
53        max: f32,
54    },
55    /// File I/O error (std only)
56    #[cfg(feature = "std")]
57    IoError(heapless::String<128>),
58}
59
60/// Motor operation errors.
61#[derive(Debug, Clone, PartialEq)]
62pub enum MotorError {
63    /// Pin operation failed
64    PinError,
65    /// Motor is in wrong state for requested operation
66    InvalidState(heapless::String<32>),
67    /// Motor not initialized
68    NotInitialized,
69    /// Position exceeds soft limits
70    LimitExceeded {
71        /// Current or requested position
72        position: i64,
73        /// Limit that was exceeded (min or max)
74        limit: i64,
75    },
76}
77
78/// Motion profile and execution errors.
79#[derive(Debug, Clone, PartialEq)]
80pub enum MotionError {
81    /// Requested velocity exceeds motor's maximum
82    VelocityExceedsLimit {
83        /// Requested velocity
84        requested: f32,
85        /// Maximum allowed velocity
86        max: f32,
87    },
88    /// Requested acceleration exceeds motor's maximum
89    AccelerationExceedsLimit {
90        /// Requested acceleration
91        requested: f32,
92        /// Maximum allowed acceleration
93        max: f32,
94    },
95    /// Move distance too short for requested acceleration profile
96    MoveTooShort {
97        /// Requested move in steps
98        steps: i64,
99        /// Minimum required steps
100        minimum: i64,
101    },
102    /// Motion profile computation overflow
103    Overflow,
104}
105
106/// Trajectory-related errors.
107#[derive(Debug, Clone, PartialEq)]
108pub enum TrajectoryError {
109    /// Trajectory references non-existent motor
110    MotorNotFound {
111        /// Trajectory name
112        trajectory: heapless::String<32>,
113        /// Referenced motor name
114        motor: heapless::String<32>,
115    },
116    /// Trajectory target exceeds motor limits
117    TargetExceedsLimits {
118        /// Target position in degrees
119        target: f32,
120        /// Motor's min limit
121        min: f32,
122        /// Motor's max limit
123        max: f32,
124    },
125    /// Waypoint list is empty
126    EmptyWaypoints,
127    /// Too many waypoints
128    TooManyWaypoints,
129    /// Invalid trajectory name or configuration
130    InvalidName(heapless::String<64>),
131    /// Empty trajectory (no waypoints or target)
132    Empty,
133}
134
135impl fmt::Display for Error {
136    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
137        match self {
138            Error::Config(e) => write!(f, "Configuration error: {}", e),
139            Error::Motor(e) => write!(f, "Motor error: {}", e),
140            Error::Motion(e) => write!(f, "Motion error: {}", e),
141            Error::Trajectory(e) => write!(f, "Trajectory error: {}", e),
142        }
143    }
144}
145
146impl fmt::Display for ConfigError {
147    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148        match self {
149            ConfigError::ParseError(msg) => write!(f, "Parse error: {}", msg),
150            ConfigError::InvalidMicrosteps(v) => {
151                write!(f, "Invalid microsteps: {}. Valid values: 1, 2, 4, 8, 16, 32, 64, 128, 256", v)
152            }
153            ConfigError::MotorNotFound(name) => write!(f, "Motor '{}' not found", name),
154            ConfigError::TrajectoryNotFound(name) => write!(f, "Trajectory '{}' not found", name),
155            ConfigError::DuplicateMotorName(name) => write!(f, "Duplicate motor name: '{}'", name),
156            ConfigError::DuplicateTrajectoryName(name) => write!(f, "Duplicate trajectory name: '{}'", name),
157            ConfigError::InvalidVelocityPercent(v) => write!(f, "Invalid velocity percent: {}. Must be 1-200", v),
158            ConfigError::InvalidAccelerationPercent(v) => write!(f, "Invalid acceleration percent: {}. Must be 1-200", v),
159            ConfigError::InvalidGearRatio(v) => write!(f, "Invalid gear ratio: {}. Must be > 0", v),
160            ConfigError::InvalidMaxVelocity(v) => write!(f, "Invalid max velocity: {}. Must be > 0", v),
161            ConfigError::InvalidMaxAcceleration(v) => write!(f, "Invalid max acceleration: {}. Must be > 0", v),
162            ConfigError::InvalidSoftLimits { min, max } => {
163                write!(f, "Invalid soft limits: min ({}) must be < max ({})", min, max)
164            }
165            #[cfg(feature = "std")]
166            ConfigError::IoError(msg) => write!(f, "I/O error: {}", msg),
167        }
168    }
169}
170
171impl fmt::Display for MotorError {
172    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173        match self {
174            MotorError::PinError => write!(f, "GPIO pin operation failed"),
175            MotorError::InvalidState(state) => write!(f, "Invalid motor state: {}", state),
176            MotorError::NotInitialized => write!(f, "Motor not initialized"),
177            MotorError::LimitExceeded { position, limit } => {
178                write!(f, "Position {} exceeds limit {}", position, limit)
179            }
180        }
181    }
182}
183
184impl fmt::Display for MotionError {
185    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186        match self {
187            MotionError::VelocityExceedsLimit { requested, max } => {
188                write!(f, "Requested velocity {} exceeds maximum {}", requested, max)
189            }
190            MotionError::AccelerationExceedsLimit { requested, max } => {
191                write!(f, "Requested acceleration {} exceeds maximum {}", requested, max)
192            }
193            MotionError::MoveTooShort { steps, minimum } => {
194                write!(f, "Move of {} steps too short, minimum is {}", steps, minimum)
195            }
196            MotionError::Overflow => write!(f, "Motion profile computation overflow"),
197        }
198    }
199}
200
201impl fmt::Display for TrajectoryError {
202    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
203        match self {
204            TrajectoryError::MotorNotFound { trajectory, motor } => {
205                write!(f, "Trajectory '{}' references unknown motor '{}'", trajectory, motor)
206            }
207            TrajectoryError::TargetExceedsLimits { target, min, max } => {
208                write!(f, "Target position {} exceeds limits [{}, {}]", target, min, max)
209            }
210            TrajectoryError::EmptyWaypoints => write!(f, "Waypoint list is empty"),
211            TrajectoryError::TooManyWaypoints => {
212                write!(f, "Too many waypoints (max 32)")
213            }
214            TrajectoryError::InvalidName(name) => {
215                write!(f, "Invalid trajectory name or configuration: {}", name)
216            }
217            TrajectoryError::Empty => write!(f, "Trajectory is empty (no waypoints or target)"),
218        }
219    }
220}
221
222// Conversion impls
223impl From<ConfigError> for Error {
224    fn from(e: ConfigError) -> Self {
225        Error::Config(e)
226    }
227}
228
229impl From<MotorError> for Error {
230    fn from(e: MotorError) -> Self {
231        Error::Motor(e)
232    }
233}
234
235impl From<MotionError> for Error {
236    fn from(e: MotionError) -> Self {
237        Error::Motion(e)
238    }
239}
240
241impl From<TrajectoryError> for Error {
242    fn from(e: TrajectoryError) -> Self {
243        Error::Trajectory(e)
244    }
245}
246
247#[cfg(feature = "std")]
248impl std::error::Error for Error {}
249
250#[cfg(feature = "std")]
251impl std::error::Error for ConfigError {}
252
253#[cfg(feature = "std")]
254impl std::error::Error for MotorError {}
255
256#[cfg(feature = "std")]
257impl std::error::Error for MotionError {}
258
259#[cfg(feature = "std")]
260impl std::error::Error for TrajectoryError {}