001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2008-2017. All Rights Reserved.                        */
003/* Open Source Software - may be modified and shared by FRC teams. The code   */
004/* must be accompanied by the FIRST BSD license file in the root directory of */
005/* the project.                                                               */
006/*----------------------------------------------------------------------------*/
007
008package edu.wpi.first.wpilibj;
009
010/**
011 * The MotorSafetyHelper object is constructed for every object that wants to implement the Motor
012 * Safety protocol. The helper object has the code to actually do the timing and call the motors
013 * Stop() method when the timeout expires. The motor object is expected to call the Feed() method
014 * whenever the motors value is updated.
015 */
016public final class MotorSafetyHelper {
017
018  private double m_expiration;
019  private boolean m_enabled;
020  private double m_stopTime;
021  private final MotorSafety m_safeObject;
022  private final MotorSafetyHelper m_nextHelper;
023  private static MotorSafetyHelper headHelper = null;
024
025  /**
026   * The constructor for a MotorSafetyHelper object. The helper object is constructed for every
027   * object that wants to implement the Motor Safety protocol. The helper object has the code to
028   * actually do the timing and call the motors Stop() method when the timeout expires. The motor
029   * object is expected to call the Feed() method whenever the motors value is updated.
030   *
031   * @param safeObject a pointer to the motor object implementing MotorSafety. This is used to call
032   *                   the Stop() method on the motor.
033   */
034  public MotorSafetyHelper(MotorSafety safeObject) {
035    m_safeObject = safeObject;
036    m_enabled = false;
037    m_expiration = MotorSafety.DEFAULT_SAFETY_EXPIRATION;
038    m_stopTime = Timer.getFPGATimestamp();
039    m_nextHelper = headHelper;
040    headHelper = this;
041  }
042
043  /**
044   * Feed the motor safety object. Resets the timer on this object that is used to do the timeouts.
045   */
046  public void feed() {
047    m_stopTime = Timer.getFPGATimestamp() + m_expiration;
048  }
049
050  /**
051   * Set the expiration time for the corresponding motor safety object.
052   *
053   * @param expirationTime The timeout value in seconds.
054   */
055  public void setExpiration(double expirationTime) {
056    m_expiration = expirationTime;
057  }
058
059  /**
060   * Retrieve the timeout value for the corresponding motor safety object.
061   *
062   * @return the timeout value in seconds.
063   */
064  public double getExpiration() {
065    return m_expiration;
066  }
067
068  /**
069   * Determine of the motor is still operating or has timed out.
070   *
071   * @return a true value if the motor is still operating normally and hasn't timed out.
072   */
073  public boolean isAlive() {
074    return !m_enabled || m_stopTime > Timer.getFPGATimestamp();
075  }
076
077  /**
078   * Check if this motor has exceeded its timeout. This method is called periodically to determine
079   * if this motor has exceeded its timeout value. If it has, the stop method is called, and the
080   * motor is shut down until its value is updated again.
081   */
082  public void check() {
083    if (!m_enabled || RobotState.isDisabled() || RobotState.isTest()) {
084      return;
085    }
086    if (m_stopTime < Timer.getFPGATimestamp()) {
087      DriverStation.reportError(m_safeObject.getDescription() + "... Output not updated often "
088          + "enough.", false);
089
090      m_safeObject.stopMotor();
091    }
092  }
093
094  /**
095   * Enable/disable motor safety for this device Turn on and off the motor safety option for this
096   * PWM object.
097   *
098   * @param enabled True if motor safety is enforced for this object
099   */
100  public void setSafetyEnabled(boolean enabled) {
101    m_enabled = enabled;
102  }
103
104  /**
105   * Return the state of the motor safety enabled flag Return if the motor safety is currently
106   * enabled for this devicce.
107   *
108   * @return True if motor safety is enforced for this device
109   */
110  public boolean isSafetyEnabled() {
111    return m_enabled;
112  }
113
114  /**
115   * Check the motors to see if any have timed out. This static method is called periodically to
116   * poll all the motors and stop any that have timed out.
117   */
118  // TODO: these should be synchronized with the setting methods in case it's
119  // called from a different thread
120  public static void checkMotors() {
121    for (MotorSafetyHelper msh = headHelper; msh != null; msh = msh.m_nextHelper) {
122      msh.check();
123    }
124  }
125}