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}