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}