Skip to main content

ev3dev_lang_rust/motors/
tacho_motor.rs

1//! The TachoMotor provides a uniform interface for using motors with positional
2//! and directional feedback such as the EV3 and NXT motors.
3//! This feedback allows for precise control of the motors.
4
5use std::time::Duration;
6
7use crate::{Ev3Error, Ev3Result};
8
9use super::{LargeMotor, MediumMotor, MotorPort};
10
11#[derive(Debug, Clone)]
12enum TachoMotorInner {
13    LargeMotor { motor: LargeMotor },
14    MediumMotor { motor: MediumMotor },
15}
16
17/// The TachoMotor provides a uniform interface for using motors with positional
18/// and directional feedback such as the EV3 and NXT motors.
19/// This feedback allows for precise control of the motors.
20/// EV3/NXT large servo motor
21#[derive(Debug, Clone)]
22pub struct TachoMotor {
23    inner: TachoMotorInner,
24}
25
26impl TachoMotor {
27    /// Try to get a `Self` on the given port. Returns `None` if port is not used or another device is connected.
28    pub fn get(port: MotorPort) -> Ev3Result<Self> {
29        let large_motor = LargeMotor::get(port);
30        if let Ok(motor) = large_motor {
31            return Ok(TachoMotor {
32                inner: TachoMotorInner::LargeMotor { motor },
33            });
34        }
35
36        let medium_motor = MediumMotor::get(port);
37        if let Ok(motor) = medium_motor {
38            return Ok(TachoMotor {
39                inner: TachoMotorInner::MediumMotor { motor },
40            });
41        }
42
43        Err(Ev3Error::InternalError {
44            msg: "Could not find a tacho motor at requested port!".to_owned(),
45        })
46    }
47
48    /// Try to find a `Self`. Only returns a motor if their is exactly one connected, `Error::NotFound` otherwise.
49    pub fn find() -> Ev3Result<Self> {
50        let large_motor = LargeMotor::find();
51        if let Ok(motor) = large_motor {
52            return Ok(TachoMotor {
53                inner: TachoMotorInner::LargeMotor { motor },
54            });
55        }
56
57        let medium_motor = MediumMotor::find();
58        if let Ok(motor) = medium_motor {
59            return Ok(TachoMotor {
60                inner: TachoMotorInner::MediumMotor { motor },
61            });
62        }
63
64        Err(Ev3Error::InternalError {
65            msg: "Could not find a tacho motor at requested port!".to_owned(),
66        })
67    }
68
69    /// Extract list of connected 'Self'
70    pub fn list() -> Ev3Result<Vec<Self>> {
71        let large_motor = LargeMotor::list()?;
72        let medium_motor = MediumMotor::list()?;
73
74        let mut vec = Vec::with_capacity(large_motor.len() + medium_motor.len());
75
76        vec.extend(large_motor.into_iter().map(|motor| TachoMotor {
77            inner: TachoMotorInner::LargeMotor { motor },
78        }));
79
80        vec.extend(medium_motor.into_iter().map(|motor| TachoMotor {
81            inner: TachoMotorInner::MediumMotor { motor },
82        }));
83
84        Ok(vec)
85    }
86
87    /// Try to convert this tacho motor to an `LargeMotor`, return `Self` if this fails.
88    pub fn into_large_motor(self) -> Result<LargeMotor, TachoMotor> {
89        match self.inner {
90            TachoMotorInner::LargeMotor { motor } => Ok(motor),
91            inner @ TachoMotorInner::MediumMotor { motor: _ } => Err(TachoMotor { inner }),
92        }
93    }
94
95    /// Try to convert this tacho motor to an `LargeMotor`, return `Self` if this fails.
96    pub fn into_medium_motor(self) -> Result<MediumMotor, TachoMotor> {
97        match self.inner {
98            inner @ TachoMotorInner::LargeMotor { motor: _ } => Err(TachoMotor { inner }),
99            TachoMotorInner::MediumMotor { motor } => Ok(motor),
100        }
101    }
102
103    /// Causes the motor to run until another command is sent.
104    pub const COMMAND_RUN_FOREVER: &'static str = "run-forever";
105
106    /// Runs the motor to an absolute position specified by `position_sp`
107    /// and then stops the motor using the command specified in `stop_action`.
108    pub const COMMAND_RUN_TO_ABS_POS: &'static str = "run-to-abs-pos";
109
110    /// Runs the motor to a position relative to the current position value.
111    /// The new position will be current `position` + `position_sp`.
112    /// When the new position is reached, the motor will stop using the command specified by `stop_action`.
113    pub const COMMAND_RUN_TO_REL_POS: &'static str = "run-to-rel-pos";
114
115    /// Run the motor for the amount of time specified in `time_sp`
116    /// and then stops the motor using the command specified by `stop_action`.
117    pub const COMMAND_RUN_TIMED: &'static str = "run-timed";
118
119    /// Runs the motor using the duty cycle specified by `duty_cycle_sp`.
120    /// Unlike other run commands, changing `duty_cycle_sp` while running will take effect immediately.
121    pub const COMMAND_RUN_DIRECT: &'static str = "run-direct";
122
123    /// Stop any of the run commands before they are complete using the command specified by `stop_action`.
124    pub const COMMAND_STOP: &'static str = "stop";
125
126    /// Resets all of the motor parameter attributes to their default values.
127    /// This will also have the effect of stopping the motor.
128    pub const COMMAND_RESET: &'static str = "reset";
129
130    /// A positive duty cycle will cause the motor to rotate clockwise.
131    pub const POLARITY_NORMAL: &'static str = "normal";
132
133    /// A positive duty cycle will cause the motor to rotate counter-clockwise.
134    pub const POLARITY_INVERSED: &'static str = "inversed";
135
136    /// Power is being sent to the motor.
137    pub const STATE_RUNNING: &'static str = "running";
138
139    /// The motor is ramping up or down and has not yet reached a pub constant output level.
140    pub const STATE_RAMPING: &'static str = "ramping";
141
142    /// The motor is not turning, but rather attempting to hold a fixed position.
143    pub const STATE_HOLDING: &'static str = "holding";
144
145    /// The motor is turning as fast as possible, but cannot reach its `speed_sp`.
146    pub const STATE_OVERLOADED: &'static str = "overloaded";
147
148    /// The motor is trying to run but is not turning at all.
149    pub const STATE_STALLED: &'static str = "stalled";
150
151    /// Removes power from the motor. The motor will freely coast to a stop.
152    pub const STOP_ACTION_COAST: &'static str = "coast";
153
154    /// Removes power from the motor and creates a passive electrical load.
155    /// This is usually done by shorting the motor terminals together.
156    /// This load will absorb the energy from the rotation of the motors
157    /// and cause the motor to stop more quickly than coasting.
158    pub const STOP_ACTION_BRAKE: &'static str = "brake";
159
160    /// Causes the motor to actively try to hold the current position.
161    /// If an external force tries to turn the motor, the motor will “push back” to maintain its position.
162    pub const STOP_ACTION_HOLD: &'static str = "hold";
163
164    /// Returns the number of tacho counts in one rotation of the motor.
165    ///
166    /// Tacho counts are used by the position and speed attributes,
167    /// so you can use this value to convert from rotations or degrees to tacho counts.
168    /// (rotation motors only)
169    ///
170    /// # Examples
171    ///
172    /// ```no_run
173    /// use ev3dev_lang_rust::motors::LargeMotor;
174    ///
175    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
176    /// // Init a tacho motor.
177    /// let motor = LargeMotor::find()?;
178    ///
179    /// // Get position and count_per_rot as f32.
180    /// let position = motor.get_position()? as f32;
181    /// let count_per_rot = motor.get_count_per_rot()? as f32;
182    ///
183    /// // Calculate the rotation count.
184    /// let rotations = position / count_per_rot;
185    ///
186    /// println!("The motor did {:.2} rotations", rotations);
187    /// # Ok(())
188    /// # }
189    /// ```
190    pub fn get_count_per_rot(&self) -> Ev3Result<i32> {
191        match self.inner {
192            TachoMotorInner::LargeMotor { ref motor } => motor.get_count_per_rot(),
193            TachoMotorInner::MediumMotor { ref motor } => motor.get_count_per_rot(),
194        }
195    }
196
197    /// Returns the number of tacho counts in one meter of travel of the motor.
198    ///
199    /// Tacho counts are used by the position and speed attributes,
200    /// so you can use this value to convert from distance to tacho counts.
201    /// (linear motors only)
202    pub fn get_count_per_m(&self) -> Ev3Result<i32> {
203        match self.inner {
204            TachoMotorInner::LargeMotor { ref motor } => motor.get_count_per_m(),
205            TachoMotorInner::MediumMotor { ref motor } => motor.get_count_per_m(),
206        }
207    }
208
209    /// Returns the number of tacho counts in the full travel of the motor.
210    ///
211    /// When combined with the count_per_m attribute,
212    /// you can use this value to calculate the maximum travel distance of the motor.
213    /// (linear motors only)
214    pub fn get_full_travel_count(&self) -> Ev3Result<i32> {
215        match self.inner {
216            TachoMotorInner::LargeMotor { ref motor } => motor.get_full_travel_count(),
217            TachoMotorInner::MediumMotor { ref motor } => motor.get_full_travel_count(),
218        }
219    }
220
221    /// Returns the current duty cycle of the motor. Units are percent.
222    ///
223    /// Values are -100 to 100.
224    ///
225    /// # Examples
226    ///
227    /// ```no_run
228    /// use ev3dev_lang_rust::motors::LargeMotor;
229    /// use std::thread;
230    /// use std::time::Duration;
231    ///
232    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
233    /// // Init a tacho motor.
234    /// let motor = LargeMotor::find()?;
235    ///
236    /// // Set the motor command `run-direct` to start rotation.
237    /// motor.run_direct()?;
238    ///
239    /// // Rotate motor forward and wait 5 seconds.
240    /// motor.set_duty_cycle_sp(50)?;
241    /// thread::sleep(Duration::from_secs(5));
242    ///
243    /// assert_eq!(motor.get_duty_cycle()?, 50);
244    /// # Ok(())
245    /// # }
246    pub fn get_duty_cycle(&self) -> Ev3Result<i32> {
247        match self.inner {
248            TachoMotorInner::LargeMotor { ref motor } => motor.get_duty_cycle(),
249            TachoMotorInner::MediumMotor { ref motor } => motor.get_duty_cycle(),
250        }
251    }
252
253    /// Returns the current duty cycle setpoint of the motor.
254    ///
255    /// Units are in percent.
256    /// Valid values are -100 to 100. A negative value causes the motor to rotate in reverse.
257    ///
258    /// # Examples
259    ///
260    /// ```no_run
261    /// use ev3dev_lang_rust::motors::LargeMotor;
262    /// use std::thread;
263    /// use std::time::Duration;
264    ///
265    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
266    /// // Init a tacho motor.
267    /// let motor = LargeMotor::find()?;
268    ///
269    /// // Rotate motor forward and wait 5 seconds.
270    /// motor.set_duty_cycle_sp(50)?;
271    /// thread::sleep(Duration::from_secs(5));
272    ///
273    /// assert_eq!(motor.get_duty_cycle()?, 50);
274    /// # Ok(())
275    /// # }
276    pub fn get_duty_cycle_sp(&self) -> Ev3Result<i32> {
277        match self.inner {
278            TachoMotorInner::LargeMotor { ref motor } => motor.get_duty_cycle_sp(),
279            TachoMotorInner::MediumMotor { ref motor } => motor.get_duty_cycle_sp(),
280        }
281    }
282
283    /// Sets the duty cycle setpoint of the motor.
284    ///
285    /// Units are in percent.
286    /// Valid values are -100 to 100. A negative value causes the motor to rotate in reverse.
287    ///
288    /// # Examples
289    ///
290    /// ```no_run
291    /// use ev3dev_lang_rust::motors::LargeMotor;
292    /// use std::thread;
293    /// use std::time::Duration;
294    ///
295    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
296    /// // Init a tacho motor.
297    /// let motor = LargeMotor::find()?;
298    ///
299    /// // Set the motor command `run-direct` to start rotation.
300    /// motor.run_direct()?;
301    ///
302    /// // Rotate motor forward and wait 5 seconds.
303    /// motor.set_duty_cycle_sp(50)?;
304    /// thread::sleep(Duration::from_secs(5));
305    ///
306    /// // Rotate motor backward and wait 5 seconds.
307    /// motor.set_duty_cycle_sp(-50)?;
308    /// thread::sleep(Duration::from_secs(5));
309    /// # Ok(())
310    /// # }
311    pub fn set_duty_cycle_sp(&self, duty_cycle: i32) -> Ev3Result<()> {
312        match self.inner {
313            TachoMotorInner::LargeMotor { ref motor } => motor.set_duty_cycle_sp(duty_cycle),
314            TachoMotorInner::MediumMotor { ref motor } => motor.set_duty_cycle_sp(duty_cycle),
315        }
316    }
317
318    /// Returns the current polarity of the motor.
319    pub fn get_polarity(&self) -> Ev3Result<String> {
320        match self.inner {
321            TachoMotorInner::LargeMotor { ref motor } => motor.get_polarity(),
322            TachoMotorInner::MediumMotor { ref motor } => motor.get_polarity(),
323        }
324    }
325
326    /// Sets the polarity of the motor.
327    pub fn set_polarity(&self, polarity: &str) -> Ev3Result<()> {
328        match self.inner {
329            TachoMotorInner::LargeMotor { ref motor } => motor.set_polarity(polarity),
330            TachoMotorInner::MediumMotor { ref motor } => motor.set_polarity(polarity),
331        }
332    }
333
334    /// Returns the current position of the motor in pulses of the rotary encoder.
335    ///
336    /// When the motor rotates clockwise, the position will increase.
337    /// Likewise, rotating counter-clockwise causes the position to decrease.
338    /// The range is -2,147,483,648 and +2,147,483,647 tachometer counts (32-bit signed integer)
339    ///
340    /// # Examples
341    ///
342    /// ```no_run
343    /// use ev3dev_lang_rust::motors::LargeMotor;
344    ///
345    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
346    /// // Init a tacho motor.
347    /// let motor = LargeMotor::find()?;
348    ///
349    /// // Get position and count_per_rot as f32.
350    /// let position = motor.get_position()? as f32;
351    /// let count_per_rot = motor.get_count_per_rot()? as f32;
352    ///
353    /// // Calculate the rotation count.
354    /// let rotations: f32 = position / count_per_rot;
355    ///
356    /// println!("The motor did {:.2} rotations", rotations);
357    /// # Ok(())
358    /// # }
359    /// ```
360    pub fn get_position(&self) -> Ev3Result<i32> {
361        match self.inner {
362            TachoMotorInner::LargeMotor { ref motor } => motor.get_position(),
363            TachoMotorInner::MediumMotor { ref motor } => motor.get_position(),
364        }
365    }
366
367    /// Sets the current position of the motor in pulses of the rotary encoder.
368    ///
369    /// When the motor rotates clockwise, the position will increase.
370    /// Likewise, rotating counter-clockwise causes the position to decrease.
371    /// The range is -2,147,483,648 and +2,147,483,647 tachometer counts (32-bit signed integer)
372    ///
373    /// # Examples
374    ///
375    /// ```no_run
376    /// use ev3dev_lang_rust::motors::LargeMotor;
377    ///
378    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
379    /// // Init a tacho motor.
380    /// let motor = LargeMotor::find()?;
381    ///
382    /// motor.set_position(0)?;
383    /// let position = motor.get_position()?;
384    ///
385    /// // If the motor is not moving, the position value
386    /// // should not change between set and get operation.
387    /// assert_eq!(position, 0);
388    /// # Ok(())
389    /// # }
390    /// ```
391    pub fn set_position(&self, position: i32) -> Ev3Result<()> {
392        match self.inner {
393            TachoMotorInner::LargeMotor { ref motor } => motor.set_position(position),
394            TachoMotorInner::MediumMotor { ref motor } => motor.set_position(position),
395        }
396    }
397
398    /// Returns the proportional pub constant for the position PID.
399    pub fn get_hold_pid_kp(&self) -> Ev3Result<f32> {
400        match self.inner {
401            TachoMotorInner::LargeMotor { ref motor } => motor.get_hold_pid_kp(),
402            TachoMotorInner::MediumMotor { ref motor } => motor.get_hold_pid_kp(),
403        }
404    }
405
406    /// Sets the proportional pub constant for the position PID.
407    pub fn set_hold_pid_kp(&self, kp: f32) -> Ev3Result<()> {
408        match self.inner {
409            TachoMotorInner::LargeMotor { ref motor } => motor.set_hold_pid_kp(kp),
410            TachoMotorInner::MediumMotor { ref motor } => motor.set_hold_pid_kp(kp),
411        }
412    }
413
414    /// Returns the integral pub constant for the position PID.
415    pub fn get_hold_pid_ki(&self) -> Ev3Result<f32> {
416        match self.inner {
417            TachoMotorInner::LargeMotor { ref motor } => motor.get_hold_pid_ki(),
418            TachoMotorInner::MediumMotor { ref motor } => motor.get_hold_pid_ki(),
419        }
420    }
421
422    /// Sets the integral pub constant for the position PID.
423    pub fn set_hold_pid_ki(&self, ki: f32) -> Ev3Result<()> {
424        match self.inner {
425            TachoMotorInner::LargeMotor { ref motor } => motor.set_hold_pid_ki(ki),
426            TachoMotorInner::MediumMotor { ref motor } => motor.set_hold_pid_ki(ki),
427        }
428    }
429
430    /// Returns the derivative pub constant for the position PID.
431    pub fn get_hold_pid_kd(&self) -> Ev3Result<f32> {
432        match self.inner {
433            TachoMotorInner::LargeMotor { ref motor } => motor.get_hold_pid_kd(),
434            TachoMotorInner::MediumMotor { ref motor } => motor.get_hold_pid_kd(),
435        }
436    }
437
438    /// Sets the derivative pub constant for the position PID.
439    pub fn set_hold_pid_kd(&self, kd: f32) -> Ev3Result<()> {
440        match self.inner {
441            TachoMotorInner::LargeMotor { ref motor } => motor.set_hold_pid_kd(kd),
442            TachoMotorInner::MediumMotor { ref motor } => motor.set_hold_pid_kd(kd),
443        }
444    }
445
446    /// Returns the maximum value that is accepted by the `speed_sp` attribute.
447    ///
448    /// This value is the speed of the motor at 9V with no load.
449    /// Note: The actual maximum obtainable speed will be less than this
450    /// and will depend on battery voltage and mechanical load on the motor.
451    pub fn get_max_speed(&self) -> Ev3Result<i32> {
452        match self.inner {
453            TachoMotorInner::LargeMotor { ref motor } => motor.get_max_speed(),
454            TachoMotorInner::MediumMotor { ref motor } => motor.get_max_speed(),
455        }
456    }
457
458    /// Returns the current target position for the `run-to-abs-pos` and `run-to-rel-pos` commands.
459    ///
460    /// Units are in tacho counts.
461    /// You can use the value returned by `counts_per_rot` to convert tacho counts to/from rotations or degrees.
462    ///
463    /// The range is -2,147,483,648 and +2,147,483,647 tachometer counts (32-bit signed integer).
464    pub fn get_position_sp(&self) -> Ev3Result<i32> {
465        match self.inner {
466            TachoMotorInner::LargeMotor { ref motor } => motor.get_position_sp(),
467            TachoMotorInner::MediumMotor { ref motor } => motor.get_position_sp(),
468        }
469    }
470
471    /// Sets the target position for the `run-to-abs-pos` and `run-to-rel-pos` commands.
472    ///
473    /// Units are in tacho counts.
474    /// You can use the value returned by `counts_per_rot` to convert tacho counts to/from rotations or degrees.
475    ///
476    /// The range is -2,147,483,648 and +2,147,483,647 tachometer counts (32-bit signed integer).
477    ///
478    /// # Examples
479    ///
480    /// ```ignore
481    /// use ev3dev_lang_rust::motors::LargeMotor;
482    /// use std::thread;
483    /// use std::time::Duration;
484    ///
485    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
486    /// // Init a tacho motor.
487    /// let motor = LargeMotor::find()?;
488    ///
489    /// // Save the current position.
490    /// let old_position = motor.get_position()?;
491    ///
492    /// // Rotate by 100 ticks
493    /// let position = motor.set_position_sp(100)?;
494    /// motor.run_to_rel_pos(None)?;
495    ///
496    /// // Wait till rotation is finished.
497    /// motor.wait_until_not_moving(None);
498    ///
499    /// // The new position should be 100 ticks larger.
500    /// let new_position = motor.get_position()?;
501    /// assert_eq!(old_position + 100, new_position);
502    /// # Ok(())
503    /// # }
504    /// ```
505    pub fn set_position_sp(&self, position_sp: i32) -> Ev3Result<()> {
506        match self.inner {
507            TachoMotorInner::LargeMotor { ref motor } => motor.set_position_sp(position_sp),
508            TachoMotorInner::MediumMotor { ref motor } => motor.set_position_sp(position_sp),
509        }
510    }
511
512    /// Returns the current motor speed in tacho counts per second.
513    ///
514    /// Note, this is not necessarily degrees (although it is for LEGO motors).
515    /// Use the `count_per_rot` attribute to convert this value to RPM or deg/sec.
516    pub fn get_speed(&self) -> Ev3Result<i32> {
517        match self.inner {
518            TachoMotorInner::LargeMotor { ref motor } => motor.get_speed(),
519            TachoMotorInner::MediumMotor { ref motor } => motor.get_speed(),
520        }
521    }
522
523    /// Returns the target speed in tacho counts per second used for all run-* commands except run-direct.
524    ///
525    /// A negative value causes the motor to rotate in reverse
526    /// with the exception of run-to-*-pos commands where the sign is ignored.
527    /// Use the `count_per_rot` attribute to convert RPM or deg/sec to tacho counts per second.
528    /// Use the `count_per_m` attribute to convert m/s to tacho counts per second.
529    pub fn get_speed_sp(&self) -> Ev3Result<i32> {
530        match self.inner {
531            TachoMotorInner::LargeMotor { ref motor } => motor.get_speed_sp(),
532            TachoMotorInner::MediumMotor { ref motor } => motor.get_speed_sp(),
533        }
534    }
535
536    /// Sets the target speed in tacho counts per second used for all run-* commands except run-direct.
537    ///
538    /// A negative value causes the motor to rotate in reverse
539    /// with the exception of run-to-*-pos commands where the sign is ignored.
540    /// Use the `count_per_rot` attribute to convert RPM or deg/sec to tacho counts per second.
541    /// Use the `count_per_m` attribute to convert m/s to tacho counts per second.
542    pub fn set_speed_sp(&self, speed_sp: i32) -> Ev3Result<()> {
543        match self.inner {
544            TachoMotorInner::LargeMotor { ref motor } => motor.set_speed_sp(speed_sp),
545            TachoMotorInner::MediumMotor { ref motor } => motor.set_speed_sp(speed_sp),
546        }
547    }
548
549    /// Returns the current ramp up setpoint.
550    ///
551    /// Units are in milliseconds and must be positive. When set to a non-zero value,
552    /// the motor speed will increase from 0 to 100% of `max_speed` over the span of this setpoint.
553    /// The actual ramp time is the ratio of the difference between the speed_sp
554    /// and the current speed and max_speed multiplied by ramp_up_sp. Values must not be negative.
555    pub fn get_ramp_up_sp(&self) -> Ev3Result<i32> {
556        match self.inner {
557            TachoMotorInner::LargeMotor { ref motor } => motor.get_ramp_up_sp(),
558            TachoMotorInner::MediumMotor { ref motor } => motor.get_ramp_up_sp(),
559        }
560    }
561
562    /// Sets the ramp up setpoint.
563    ///
564    /// Units are in milliseconds and must be positive. When set to a non-zero value,
565    /// the motor speed will increase from 0 to 100% of `max_speed` over the span of this setpoint.
566    /// The actual ramp time is the ratio of the difference between the speed_sp
567    /// and the current speed and max_speed multiplied by ramp_up_sp. Values must not be negative.
568    pub fn set_ramp_up_sp(&self, ramp_up_sp: i32) -> Ev3Result<()> {
569        match self.inner {
570            TachoMotorInner::LargeMotor { ref motor } => motor.set_ramp_up_sp(ramp_up_sp),
571            TachoMotorInner::MediumMotor { ref motor } => motor.set_ramp_up_sp(ramp_up_sp),
572        }
573    }
574
575    /// Returns the current ramp down setpoint.
576    ///
577    /// Units are in milliseconds and must be positive. When set to a non-zero value,
578    /// the motor speed will decrease from 100% down to 0 of `max_speed` over the span of this setpoint.
579    /// The actual ramp time is the ratio of the difference between the speed_sp
580    /// and the current speed and 0 multiplied by ramp_down_sp. Values must not be negative.
581    pub fn get_ramp_down_sp(&self) -> Ev3Result<i32> {
582        match self.inner {
583            TachoMotorInner::LargeMotor { ref motor } => motor.get_ramp_down_sp(),
584            TachoMotorInner::MediumMotor { ref motor } => motor.get_ramp_down_sp(),
585        }
586    }
587
588    /// Sets the ramp down setpoint.
589    ///
590    /// Units are in milliseconds and must be positive. When set to a non-zero value,
591    /// the motor speed will decrease from 100% down to 0 of `max_speed` over the span of this setpoint.
592    /// The actual ramp time is the ratio of the difference between the speed_sp
593    /// and the current speed and 0 multiplied by ramp_down_sp. Values must not be negative.
594    pub fn set_ramp_down_sp(&self, ramp_down_sp: i32) -> Ev3Result<()> {
595        match self.inner {
596            TachoMotorInner::LargeMotor { ref motor } => motor.set_ramp_down_sp(ramp_down_sp),
597            TachoMotorInner::MediumMotor { ref motor } => motor.set_ramp_down_sp(ramp_down_sp),
598        }
599    }
600
601    /// Returns the proportional pub constant for the speed regulation PID.
602    pub fn get_speed_pid_kp(&self) -> Ev3Result<f32> {
603        match self.inner {
604            TachoMotorInner::LargeMotor { ref motor } => motor.get_speed_pid_kp(),
605            TachoMotorInner::MediumMotor { ref motor } => motor.get_speed_pid_kp(),
606        }
607    }
608
609    /// Sets the proportional pub constant for the speed regulation PID.
610    pub fn set_speed_pid_kp(&self, kp: f32) -> Ev3Result<()> {
611        match self.inner {
612            TachoMotorInner::LargeMotor { ref motor } => motor.set_speed_pid_kp(kp),
613            TachoMotorInner::MediumMotor { ref motor } => motor.set_speed_pid_kp(kp),
614        }
615    }
616
617    /// Returns the integral pub constant for the speed regulation PID.
618    pub fn get_speed_pid_ki(&self) -> Ev3Result<f32> {
619        match self.inner {
620            TachoMotorInner::LargeMotor { ref motor } => motor.get_speed_pid_ki(),
621            TachoMotorInner::MediumMotor { ref motor } => motor.get_speed_pid_ki(),
622        }
623    }
624
625    /// Sets the integral pub constant for the speed regulation PID.
626    pub fn set_speed_pid_ki(&self, ki: f32) -> Ev3Result<()> {
627        match self.inner {
628            TachoMotorInner::LargeMotor { ref motor } => motor.set_speed_pid_ki(ki),
629            TachoMotorInner::MediumMotor { ref motor } => motor.set_speed_pid_ki(ki),
630        }
631    }
632
633    /// Returns the derivative pub constant for the speed regulation PID.
634    pub fn get_speed_pid_kd(&self) -> Ev3Result<f32> {
635        match self.inner {
636            TachoMotorInner::LargeMotor { ref motor } => motor.get_speed_pid_kd(),
637            TachoMotorInner::MediumMotor { ref motor } => motor.get_speed_pid_kd(),
638        }
639    }
640
641    /// Sets the derivative pub constant for the speed regulation PID.
642    pub fn set_speed_pid_kd(&self, kd: f32) -> Ev3Result<()> {
643        match self.inner {
644            TachoMotorInner::LargeMotor { ref motor } => motor.set_speed_pid_kd(kd),
645            TachoMotorInner::MediumMotor { ref motor } => motor.set_speed_pid_kd(kd),
646        }
647    }
648
649    /// Returns a list of state flags.
650    pub fn get_state(&self) -> Ev3Result<Vec<String>> {
651        match self.inner {
652            TachoMotorInner::LargeMotor { ref motor } => motor.get_state(),
653            TachoMotorInner::MediumMotor { ref motor } => motor.get_state(),
654        }
655    }
656
657    /// Returns the current stop action.
658    ///
659    /// The value determines the motors behavior when command is set to stop.
660    pub fn get_stop_action(&self) -> Ev3Result<String> {
661        match self.inner {
662            TachoMotorInner::LargeMotor { ref motor } => motor.get_stop_action(),
663            TachoMotorInner::MediumMotor { ref motor } => motor.get_stop_action(),
664        }
665    }
666
667    /// Sets the stop action.
668    ///
669    /// The value determines the motors behavior when command is set to stop.
670    pub fn set_stop_action(&self, stop_action: &str) -> Ev3Result<()> {
671        match self.inner {
672            TachoMotorInner::LargeMotor { ref motor } => motor.set_stop_action(stop_action),
673            TachoMotorInner::MediumMotor { ref motor } => motor.set_stop_action(stop_action),
674        }
675    }
676
677    /// Returns a list of stop actions supported by the motor controller.
678    pub fn get_stop_actions(&self) -> Ev3Result<Vec<String>> {
679        match self.inner {
680            TachoMotorInner::LargeMotor { ref motor } => motor.get_stop_actions(),
681            TachoMotorInner::MediumMotor { ref motor } => motor.get_stop_actions(),
682        }
683    }
684
685    /// Returns the current amount of time the motor will run when using the run-timed command.
686    ///
687    /// Units are in milliseconds. Values must not be negative.
688    pub fn get_time_sp(&self) -> Ev3Result<i32> {
689        match self.inner {
690            TachoMotorInner::LargeMotor { ref motor } => motor.get_time_sp(),
691            TachoMotorInner::MediumMotor { ref motor } => motor.get_time_sp(),
692        }
693    }
694
695    /// Sets the amount of time the motor will run when using the run-timed command.
696    ///
697    /// Units are in milliseconds. Values must not be negative.
698    pub fn set_time_sp(&self, time_sp: i32) -> Ev3Result<()> {
699        match self.inner {
700            TachoMotorInner::LargeMotor { ref motor } => motor.set_time_sp(time_sp),
701            TachoMotorInner::MediumMotor { ref motor } => motor.set_time_sp(time_sp),
702        }
703    }
704
705    /// Runs the motor using the duty cycle specified by `duty_cycle_sp`.
706    ///
707    /// Unlike other run commands, changing `duty_cycle_sp` while running will take effect immediately.
708    pub fn run_direct(&self) -> Ev3Result<()> {
709        match self.inner {
710            TachoMotorInner::LargeMotor { ref motor } => motor.run_direct(),
711            TachoMotorInner::MediumMotor { ref motor } => motor.run_direct(),
712        }
713    }
714
715    /// Causes the motor to run until another command is sent.
716    pub fn run_forever(&self) -> Ev3Result<()> {
717        match self.inner {
718            TachoMotorInner::LargeMotor { ref motor } => motor.run_forever(),
719            TachoMotorInner::MediumMotor { ref motor } => motor.run_forever(),
720        }
721    }
722
723    /// Runs the motor to an absolute position specified by `position_sp`
724    ///
725    /// and then stops the motor using the command specified in `stop_action`.
726    pub fn run_to_abs_pos(&self, position_sp: Option<i32>) -> Ev3Result<()> {
727        match self.inner {
728            TachoMotorInner::LargeMotor { ref motor } => motor.run_to_abs_pos(position_sp),
729            TachoMotorInner::MediumMotor { ref motor } => motor.run_to_abs_pos(position_sp),
730        }
731    }
732
733    /// Runs the motor to a position relative to the current position value.
734    ///
735    /// The new position will be current `position` + `position_sp`.
736    /// When the new position is reached, the motor will stop using the command specified by `stop_action`.
737    pub fn run_to_rel_pos(&self, position_sp: Option<i32>) -> Ev3Result<()> {
738        match self.inner {
739            TachoMotorInner::LargeMotor { ref motor } => motor.run_to_rel_pos(position_sp),
740            TachoMotorInner::MediumMotor { ref motor } => motor.run_to_rel_pos(position_sp),
741        }
742    }
743
744    /// Run the motor for the amount of time specified in `time_sp`
745    ///
746    /// and then stops the motor using the command specified by `stop_action`.
747    pub fn run_timed(&self, time_sp: Option<Duration>) -> Ev3Result<()> {
748        match self.inner {
749            TachoMotorInner::LargeMotor { ref motor } => motor.run_timed(time_sp),
750            TachoMotorInner::MediumMotor { ref motor } => motor.run_timed(time_sp),
751        }
752    }
753
754    /// Stop any of the run commands before they are complete using the command specified by `stop_action`.
755    pub fn stop(&self) -> Ev3Result<()> {
756        match self.inner {
757            TachoMotorInner::LargeMotor { ref motor } => motor.stop(),
758            TachoMotorInner::MediumMotor { ref motor } => motor.stop(),
759        }
760    }
761
762    /// Resets all of the motor parameter attributes to their default values.
763    /// This will also have the effect of stopping the motor.
764    pub fn reset(&self) -> Ev3Result<()> {
765        match self.inner {
766            TachoMotorInner::LargeMotor { ref motor } => motor.reset(),
767            TachoMotorInner::MediumMotor { ref motor } => motor.reset(),
768        }
769    }
770
771    /// Power is being sent to the motor.
772    pub fn is_running(&self) -> Ev3Result<bool> {
773        match self.inner {
774            TachoMotorInner::LargeMotor { ref motor } => motor.is_running(),
775            TachoMotorInner::MediumMotor { ref motor } => motor.is_running(),
776        }
777    }
778
779    /// The motor is ramping up or down and has not yet reached a pub constant output level.
780    pub fn is_ramping(&self) -> Ev3Result<bool> {
781        match self.inner {
782            TachoMotorInner::LargeMotor { ref motor } => motor.is_ramping(),
783            TachoMotorInner::MediumMotor { ref motor } => motor.is_ramping(),
784        }
785    }
786
787    /// The motor is not turning, but rather attempting to hold a fixed position.
788    pub fn is_holding(&self) -> Ev3Result<bool> {
789        match self.inner {
790            TachoMotorInner::LargeMotor { ref motor } => motor.is_holding(),
791            TachoMotorInner::MediumMotor { ref motor } => motor.is_holding(),
792        }
793    }
794
795    /// The motor is turning as fast as possible, but cannot reach its `speed_sp`.
796    pub fn is_overloaded(&self) -> Ev3Result<bool> {
797        match self.inner {
798            TachoMotorInner::LargeMotor { ref motor } => motor.is_overloaded(),
799            TachoMotorInner::MediumMotor { ref motor } => motor.is_overloaded(),
800        }
801    }
802
803    /// The motor is trying to run but is not turning at all.
804    pub fn is_stalled(&self) -> Ev3Result<bool> {
805        match self.inner {
806            TachoMotorInner::LargeMotor { ref motor } => motor.is_stalled(),
807            TachoMotorInner::MediumMotor { ref motor } => motor.is_stalled(),
808        }
809    }
810
811    /// Wait until condition `cond` returns true or the `timeout` is reached.
812    ///
813    /// The condition is checked when to the `state` attribute has changed.
814    /// If the `timeout` is `None` it will wait an infinite time.
815    ///
816    /// # Examples
817    ///
818    /// ```no_run
819    /// use ev3dev_lang_rust::motors::LargeMotor;
820    /// use std::time::Duration;
821    ///
822    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
823    /// // Init a tacho motor.
824    /// let motor = LargeMotor::find()?;
825    ///
826    /// motor.run_timed(Some(Duration::from_secs(5)))?;
827    ///
828    /// let cond = || {
829    ///     motor.get_state()
830    ///         .unwrap_or_else(|_| vec![])
831    ///         .iter()
832    ///         .all(|s| s != LargeMotor::STATE_RUNNING)
833    /// };
834    /// motor.wait(cond, None);
835    ///
836    /// println!("Motor has stopped!");
837    /// # Ok(())
838    /// # }
839    /// ```
840    #[cfg(target_os = "linux")]
841    pub fn wait<F>(&self, cond: F, timeout: Option<Duration>) -> bool
842    where
843        F: Fn() -> bool,
844    {
845        match self.inner {
846            TachoMotorInner::LargeMotor { ref motor } => motor.wait(cond, timeout),
847            TachoMotorInner::MediumMotor { ref motor } => motor.wait(cond, timeout),
848        }
849    }
850
851    /// Wait while the `state` is in the vector `self.get_state()` or the `timeout` is reached.
852    ///
853    /// If the `timeout` is `None` it will wait an infinite time.
854    ///
855    /// # Example
856    ///
857    /// ```no_run
858    /// use ev3dev_lang_rust::motors::LargeMotor;
859    /// use std::time::Duration;
860    ///
861    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
862    /// // Init a tacho motor.
863    /// let motor = LargeMotor::find()?;
864    ///
865    /// motor.run_timed(Some(Duration::from_secs(5)))?;
866    ///
867    /// motor.wait_while(LargeMotor::STATE_RUNNING, None);
868    ///
869    /// println!("Motor has stopped!");
870    /// # Ok(())
871    /// # }
872    /// ```
873    #[cfg(target_os = "linux")]
874    pub fn wait_while(&self, state: &str, timeout: Option<Duration>) -> bool {
875        match self.inner {
876            TachoMotorInner::LargeMotor { ref motor } => motor.wait_while(state, timeout),
877            TachoMotorInner::MediumMotor { ref motor } => motor.wait_while(state, timeout),
878        }
879    }
880
881    /// Wait until the `state` is in the vector `self.get_state()` or the `timeout` is reached.
882    ///
883    /// If the `timeout` is `None` it will wait an infinite time.
884    ///
885    /// # Example
886    ///
887    /// ```no_run
888    /// use ev3dev_lang_rust::motors::LargeMotor;
889    /// use std::time::Duration;
890    ///
891    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
892    /// // Init a tacho motor.
893    /// let motor = LargeMotor::find()?;
894    ///
895    /// motor.run_timed(Some(Duration::from_secs(5)))?;
896    ///
897    /// motor.wait_until(LargeMotor::STATE_RUNNING, None);
898    ///
899    /// println!("Motor has started!");
900    /// # Ok(())
901    /// # }
902    /// ```
903    #[cfg(target_os = "linux")]
904    pub fn wait_until(&self, state: &str, timeout: Option<Duration>) -> bool {
905        match self.inner {
906            TachoMotorInner::LargeMotor { ref motor } => motor.wait_until(state, timeout),
907            TachoMotorInner::MediumMotor { ref motor } => motor.wait_until(state, timeout),
908        }
909    }
910
911    /// Wait until the motor is not moving or the timeout is reached.
912    ///
913    /// This is equal to `wait_while(STATE_RUNNING, timeout)`.
914    /// If the `timeout` is `None` it will wait an infinite time.
915    ///
916    /// # Example
917    ///
918    /// ```no_run
919    /// use ev3dev_lang_rust::motors::LargeMotor;
920    /// use std::time::Duration;
921    ///
922    /// # fn main() -> ev3dev_lang_rust::Ev3Result<()> {
923    /// // Init a tacho motor.
924    /// let motor = LargeMotor::find()?;
925    ///
926    /// motor.run_timed(Some(Duration::from_secs(5)))?;
927    ///
928    /// motor.wait_until_not_moving(None);
929    ///
930    /// println!("Motor has stopped!");
931    /// # Ok(())
932    /// # }
933    /// ```
934    #[cfg(target_os = "linux")]
935    pub fn wait_until_not_moving(&self, timeout: Option<Duration>) -> bool {
936        match self.inner {
937            TachoMotorInner::LargeMotor { ref motor } => motor.wait_until_not_moving(timeout),
938            TachoMotorInner::MediumMotor { ref motor } => motor.wait_until_not_moving(timeout),
939        }
940    }
941}
942
943impl From<LargeMotor> for TachoMotor {
944    fn from(motor: LargeMotor) -> Self {
945        Self {
946            inner: TachoMotorInner::LargeMotor { motor },
947        }
948    }
949}
950
951impl From<MediumMotor> for TachoMotor {
952    fn from(motor: MediumMotor) -> Self {
953        Self {
954            inner: TachoMotorInner::MediumMotor { motor },
955        }
956    }
957}