001// Copyright (c) FIRST and other WPILib contributors.
002// Open Source Software; you can modify and/or share it under the terms of
003// the WPILib BSD license file in the root directory of this project.
004
005package edu.wpi.first.wpilibj;
006
007import java.util.LinkedHashSet;
008import java.util.Set;
009
010/**
011 * This base class runs a watchdog timer and calls the subclass's StopMotor() function if the
012 * timeout expires.
013 *
014 * <p>The subclass should call feed() whenever the motor value is updated.
015 */
016public abstract class MotorSafety {
017  private static final double kDefaultSafetyExpiration = 0.1;
018
019  private double m_expiration = kDefaultSafetyExpiration;
020  private boolean m_enabled;
021  private double m_stopTime = Timer.getFPGATimestamp();
022  private final Object m_thisMutex = new Object();
023  private static final Set<MotorSafety> m_instanceList = new LinkedHashSet<>();
024  private static final Object m_listMutex = new Object();
025
026  /** MotorSafety constructor. */
027  public MotorSafety() {
028    synchronized (m_listMutex) {
029      m_instanceList.add(this);
030    }
031  }
032
033  /**
034   * Feed the motor safety object.
035   *
036   * <p>Resets the timer on this object that is used to do the timeouts.
037   */
038  public void feed() {
039    synchronized (m_thisMutex) {
040      m_stopTime = Timer.getFPGATimestamp() + m_expiration;
041    }
042  }
043
044  /**
045   * Set the expiration time for the corresponding motor safety object.
046   *
047   * @param expirationTime The timeout value in seconds.
048   */
049  public void setExpiration(double expirationTime) {
050    synchronized (m_thisMutex) {
051      m_expiration = expirationTime;
052    }
053  }
054
055  /**
056   * Retrieve the timeout value for the corresponding motor safety object.
057   *
058   * @return the timeout value in seconds.
059   */
060  public double getExpiration() {
061    synchronized (m_thisMutex) {
062      return m_expiration;
063    }
064  }
065
066  /**
067   * Determine of the motor is still operating or has timed out.
068   *
069   * @return a true value if the motor is still operating normally and hasn't timed out.
070   */
071  public boolean isAlive() {
072    synchronized (m_thisMutex) {
073      return !m_enabled || m_stopTime > Timer.getFPGATimestamp();
074    }
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    boolean enabled;
084    double stopTime;
085
086    synchronized (m_thisMutex) {
087      enabled = m_enabled;
088      stopTime = m_stopTime;
089    }
090
091    if (!enabled || RobotState.isDisabled() || RobotState.isTest()) {
092      return;
093    }
094
095    if (stopTime < Timer.getFPGATimestamp()) {
096      DriverStation.reportError(getDescription() + "... Output not updated often enough.", false);
097
098      stopMotor();
099    }
100  }
101
102  /**
103   * Enable/disable motor safety for this device.
104   *
105   * <p>Turn on and off the motor safety option for this PWM object.
106   *
107   * @param enabled True if motor safety is enforced for this object
108   */
109  public void setSafetyEnabled(boolean enabled) {
110    synchronized (m_thisMutex) {
111      m_enabled = enabled;
112    }
113  }
114
115  /**
116   * Return the state of the motor safety enabled flag.
117   *
118   * <p>Return if the motor safety is currently enabled for this device.
119   *
120   * @return True if motor safety is enforced for this device
121   */
122  public boolean isSafetyEnabled() {
123    synchronized (m_thisMutex) {
124      return m_enabled;
125    }
126  }
127
128  /**
129   * Check the motors to see if any have timed out.
130   *
131   * <p>This static method is called periodically to poll all the motors and stop any that have
132   * timed out.
133   */
134  public static void checkMotors() {
135    synchronized (m_listMutex) {
136      for (MotorSafety elem : m_instanceList) {
137        elem.check();
138      }
139    }
140  }
141
142  public abstract void stopMotor();
143
144  public abstract String getDescription();
145}