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.buttons; 009 010import edu.wpi.first.wpilibj.SendableBase; 011import edu.wpi.first.wpilibj.command.Command; 012import edu.wpi.first.wpilibj.command.Scheduler; 013import edu.wpi.first.wpilibj.smartdashboard.SendableBuilder; 014 015/** 016 * This class provides an easy way to link commands to inputs. 017 * 018 * <p>It is very easy to link a button to a command. For instance, you could link the trigger 019 * button of a joystick to a "score" command. 020 * 021 * <p>It is encouraged that teams write a subclass of Trigger if they want to have something unusual 022 * (for instance, if they want to react to the user holding a button while the robot is reading a 023 * certain sensor input). For this, they only have to write the {@link Trigger#get()} method to get 024 * the full functionality of the Trigger class. 025 */ 026public abstract class Trigger extends SendableBase { 027 private volatile boolean m_sendablePressed = false; 028 029 /** 030 * Returns whether or not the trigger is active. 031 * 032 * <p>This method will be called repeatedly a command is linked to the Trigger. 033 * 034 * @return whether or not the trigger condition is active. 035 */ 036 public abstract boolean get(); 037 038 /** 039 * Returns whether get() return true or the internal table for SmartDashboard use is pressed. 040 * 041 * @return whether get() return true or the internal table for SmartDashboard use is pressed. 042 */ 043 @SuppressWarnings("PMD.UselessParentheses") 044 private boolean grab() { 045 return get() || m_sendablePressed; 046 } 047 048 /** 049 * Starts the given command whenever the trigger just becomes active. 050 * 051 * @param command the command to start 052 */ 053 public void whenActive(final Command command) { 054 new ButtonScheduler() { 055 056 private boolean m_pressedLast = grab(); 057 058 @Override 059 public void execute() { 060 if (grab()) { 061 if (!m_pressedLast) { 062 m_pressedLast = true; 063 command.start(); 064 } 065 } else { 066 m_pressedLast = false; 067 } 068 } 069 }.start(); 070 } 071 072 /** 073 * Constantly starts the given command while the button is held. 074 * 075 * {@link Command#start()} will be called repeatedly while the trigger is active, and will be 076 * canceled when the trigger becomes inactive. 077 * 078 * @param command the command to start 079 */ 080 public void whileActive(final Command command) { 081 new ButtonScheduler() { 082 083 private boolean m_pressedLast = grab(); 084 085 @Override 086 public void execute() { 087 if (grab()) { 088 m_pressedLast = true; 089 command.start(); 090 } else { 091 if (m_pressedLast) { 092 m_pressedLast = false; 093 command.cancel(); 094 } 095 } 096 } 097 }.start(); 098 } 099 100 /** 101 * Starts the command when the trigger becomes inactive. 102 * 103 * @param command the command to start 104 */ 105 public void whenInactive(final Command command) { 106 new ButtonScheduler() { 107 108 private boolean m_pressedLast = grab(); 109 110 @Override 111 public void execute() { 112 if (grab()) { 113 m_pressedLast = true; 114 } else { 115 if (m_pressedLast) { 116 m_pressedLast = false; 117 command.start(); 118 } 119 } 120 } 121 }.start(); 122 } 123 124 /** 125 * Toggles a command when the trigger becomes active. 126 * 127 * @param command the command to toggle 128 */ 129 public void toggleWhenActive(final Command command) { 130 new ButtonScheduler() { 131 132 private boolean m_pressedLast = grab(); 133 134 @Override 135 public void execute() { 136 if (grab()) { 137 if (!m_pressedLast) { 138 m_pressedLast = true; 139 if (command.isRunning()) { 140 command.cancel(); 141 } else { 142 command.start(); 143 } 144 } 145 } else { 146 m_pressedLast = false; 147 } 148 } 149 }.start(); 150 } 151 152 /** 153 * Cancels a command when the trigger becomes active. 154 * 155 * @param command the command to cancel 156 */ 157 public void cancelWhenActive(final Command command) { 158 new ButtonScheduler() { 159 160 private boolean m_pressedLast = grab(); 161 162 @Override 163 public void execute() { 164 if (grab()) { 165 if (!m_pressedLast) { 166 m_pressedLast = true; 167 command.cancel(); 168 } 169 } else { 170 m_pressedLast = false; 171 } 172 } 173 }.start(); 174 } 175 176 /** 177 * An internal class of {@link Trigger}. The user should ignore this, it is only public to 178 * interface between packages. 179 */ 180 public abstract class ButtonScheduler { 181 public abstract void execute(); 182 183 protected void start() { 184 Scheduler.getInstance().addButton(this); 185 } 186 } 187 188 @Override 189 public void initSendable(SendableBuilder builder) { 190 builder.setSmartDashboardType("Button"); 191 builder.setSafeState(() -> { 192 m_sendablePressed = false; 193 }); 194 builder.addBooleanProperty("pressed", this::grab, (value) -> { 195 m_sendablePressed = value; 196 }); 197 } 198}