stepper_motion/config/
validation.rs1use crate::error::{ConfigError, Error, Result, TrajectoryError};
4
5use super::SystemConfig;
6
7pub fn validate_config(config: &SystemConfig) -> Result<()> {
15 for (name, motor) in config.motors.iter() {
17 validate_motor(name.as_str(), motor)?;
18 }
19
20 for (name, traj) in config.trajectories.iter() {
22 validate_trajectory(name.as_str(), traj, config)?;
23 }
24
25 for (name, seq) in config.sequences.iter() {
27 validate_sequence(name.as_str(), seq, config)?;
28 }
29
30 Ok(())
31}
32
33fn validate_motor(_name: &str, config: &super::MotorConfig) -> Result<()> {
34 if config.gear_ratio <= 0.0 {
36 return Err(Error::Config(ConfigError::InvalidGearRatio(config.gear_ratio)));
37 }
38
39 if config.max_velocity.0 <= 0.0 {
41 return Err(Error::Config(ConfigError::InvalidMaxVelocity(
42 config.max_velocity.0,
43 )));
44 }
45
46 if config.max_acceleration.0 <= 0.0 {
48 return Err(Error::Config(ConfigError::InvalidMaxAcceleration(
49 config.max_acceleration.0,
50 )));
51 }
52
53 if let Some(ref limits) = config.limits {
55 if !limits.is_valid() {
56 return Err(Error::Config(ConfigError::InvalidSoftLimits {
57 min: limits.min.0,
58 max: limits.max.0,
59 }));
60 }
61 }
62
63 Ok(())
64}
65
66fn validate_trajectory(
67 name: &str,
68 traj: &super::TrajectoryConfig,
69 config: &SystemConfig,
70) -> Result<()> {
71 if config.motor(traj.motor.as_str()).is_none() {
73 return Err(Error::Trajectory(TrajectoryError::MotorNotFound {
74 trajectory: heapless::String::try_from(name).unwrap_or_default(),
75 motor: traj.motor.clone(),
76 }));
77 }
78
79 if traj.velocity_percent == 0 || traj.velocity_percent > 200 {
81 return Err(Error::Config(ConfigError::InvalidVelocityPercent(
82 traj.velocity_percent,
83 )));
84 }
85
86 if traj.acceleration_percent == 0 || traj.acceleration_percent > 200 {
88 return Err(Error::Config(ConfigError::InvalidAccelerationPercent(
89 traj.acceleration_percent,
90 )));
91 }
92
93 if let Some(motor) = config.motor(traj.motor.as_str()) {
95 if let Some(ref limits) = motor.limits {
96 if !limits.contains(traj.target_degrees) {
97 if limits.policy == super::LimitPolicy::Reject {
100 return Err(Error::Trajectory(TrajectoryError::TargetExceedsLimits {
101 target: traj.target_degrees.0,
102 min: limits.min.0,
103 max: limits.max.0,
104 }));
105 }
106 }
107 }
108 }
109
110 Ok(())
111}
112
113fn validate_sequence(
114 name: &str,
115 seq: &super::WaypointTrajectory,
116 config: &SystemConfig,
117) -> Result<()> {
118 if config.motor(seq.motor.as_str()).is_none() {
120 return Err(Error::Trajectory(TrajectoryError::MotorNotFound {
121 trajectory: heapless::String::try_from(name).unwrap_or_default(),
122 motor: seq.motor.clone(),
123 }));
124 }
125
126 if seq.waypoints.is_empty() {
128 return Err(Error::Trajectory(TrajectoryError::EmptyWaypoints));
129 }
130
131 if seq.velocity_percent == 0 || seq.velocity_percent > 200 {
133 return Err(Error::Config(ConfigError::InvalidVelocityPercent(
134 seq.velocity_percent,
135 )));
136 }
137
138 Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143 use super::*;
144
145 #[test]
146 fn test_invalid_gear_ratio() {
147 use crate::config::units::{DegreesPerSec, DegreesPerSecSquared, Microsteps};
148 use crate::config::MotorConfig;
149
150 let config = MotorConfig {
151 name: heapless::String::try_from("test").unwrap(),
152 steps_per_revolution: 200,
153 microsteps: Microsteps::SIXTEENTH,
154 gear_ratio: -1.0, max_velocity: DegreesPerSec(360.0),
156 max_acceleration: DegreesPerSecSquared(720.0),
157 invert_direction: false,
158 limits: None,
159 backlash_compensation: None,
160 };
161
162 let result = validate_motor("test", &config);
163 assert!(matches!(
164 result,
165 Err(Error::Config(ConfigError::InvalidGearRatio(_)))
166 ));
167 }
168}