001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2008-2018 FIRST. 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
010import java.util.LinkedHashSet;
011import java.util.Set;
012
013/**
014 * The MotorSafetyHelper object is constructed for every object that wants to implement the Motor
015 * Safety protocol. The helper object has the code to actually do the timing and call the motors
016 * Stop() method when the timeout expires. The motor object is expected to call the Feed() method
017 * whenever the motors value is updated.
018 */
019public final class MotorSafetyHelper {
020  private double m_expiration;
021  private boolean m_enabled;
022  private double m_stopTime;
023  private final Object m_thisMutex = new Object();
024  private final MotorSafety m_safeObject;
025  private static final Set<MotorSafetyHelper> m_helperList = new LinkedHashSet<>();
026  private static final Object m_listMutex = new Object();
027
028  /**
029   * The constructor for a MotorSafetyHelper object. The helper object is constructed for every
030   * object that wants to implement the Motor Safety protocol. The helper object has the code to
031   * actually do the timing and call the motors Stop() method when the timeout expires. The motor
032   * object is expected to call the Feed() method whenever the motors value is updated.
033   *
034   * @param safeObject a pointer to the motor object implementing MotorSafety. This is used to call
035   *                   the Stop() method on the motor.
036   */
037  public MotorSafetyHelper(MotorSafety safeObject) {
038    m_safeObject = safeObject;
039    m_enabled = false;
040    m_expiration = MotorSafety.DEFAULT_SAFETY_EXPIRATION;
041    m_stopTime = Timer.getFPGATimestamp();
042
043    synchronized (m_listMutex) {
044      m_helperList.add(this);
045    }
046  }
047
048  /**
049   * Feed the motor safety object. Resets the timer on this object that is used to do the timeouts.
050   */
051  public void feed() {
052    synchronized (m_thisMutex) {
053      m_stopTime = Timer.getFPGATimestamp() + m_expiration;
054    }
055  }
056
057  /**
058   * Set the expiration time for the corresponding motor safety object.
059   *
060   * @param expirationTime The timeout value in seconds.
061   */
062  public void setExpiration(double expirationTime) {
063    synchronized (m_thisMutex) {
064      m_expiration = expirationTime;
065    }
066  }
067
068  /**
069   * Retrieve the timeout value for the corresponding motor safety object.
070   *
071   * @return the timeout value in seconds.
072   */
073  public double getExpiration() {
074    synchronized (m_thisMutex) {
075      return m_expiration;
076    }
077  }
078
079  /**
080   * Determine of the motor is still operating or has timed out.
081   *
082   * @return a true value if the motor is still operating normally and hasn't timed out.
083   */
084  public boolean isAlive() {
085    synchronized (m_thisMutex) {
086      return !m_enabled || m_stopTime > Timer.getFPGATimestamp();
087    }
088  }
089
090  /**
091   * Check if this motor has exceeded its timeout. This method is called periodically to determine
092   * if this motor has exceeded its timeout value. If it has, the stop method is called, and the
093   * motor is shut down until its value is updated again.
094   */
095  public void check() {
096    boolean enabled;
097    double stopTime;
098
099    synchronized (m_thisMutex) {
100      enabled = m_enabled;
101      stopTime = m_stopTime;
102    }
103
104    if (!enabled || RobotState.isDisabled() || RobotState.isTest()) {
105      return;
106    }
107    if (stopTime < Timer.getFPGATimestamp()) {
108      DriverStation.reportError(m_safeObject.getDescription() + "... Output not updated often "
109          + "enough.", false);
110
111      m_safeObject.stopMotor();
112    }
113  }
114
115  /**
116   * Enable/disable motor safety for this device Turn on and off the motor safety option for this
117   * PWM object.
118   *
119   * @param enabled True if motor safety is enforced for this object
120   */
121  public void setSafetyEnabled(boolean enabled) {
122    synchronized (m_thisMutex) {
123      m_enabled = enabled;
124    }
125  }
126
127  /**
128   * Return the state of the motor safety enabled flag Return if the motor safety is currently
129   * enabled for this device.
130   *
131   * @return True if motor safety is enforced for this device
132   */
133  public boolean isSafetyEnabled() {
134    synchronized (m_thisMutex) {
135      return m_enabled;
136    }
137  }
138
139  /**
140   * Check the motors to see if any have timed out. This static method is called periodically to
141   * poll all the motors and stop any that have timed out.
142   */
143  public static void checkMotors() {
144    synchronized (m_listMutex) {
145      for (MotorSafetyHelper elem : m_helperList) {
146        elem.check();
147      }
148    }
149  }
150}