001/*----------------------------------------------------------------------------*/
002/* Copyright (c) 2017-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.command;
009
010import java.util.Enumeration;
011
012/**
013 * A {@link ConditionalCommand} is a {@link Command} that starts one of two commands.
014 *
015 * <p>
016 * A {@link ConditionalCommand} uses m_condition to determine whether it should run m_onTrue or
017 * m_onFalse.
018 * </p>
019 *
020 * <p>
021 * A {@link ConditionalCommand} adds the proper {@link Command} to the {@link Scheduler} during
022 * {@link ConditionalCommand#initialize()} and then {@link ConditionalCommand#isFinished()} will
023 * return true once that {@link Command} has finished executing.
024 * </p>
025 *
026 * <p>
027 * If no {@link Command} is specified for m_onFalse, the occurrence of that condition will be a
028 * no-op.
029 * </p>
030 *
031 * @see Command
032 * @see Scheduler
033 */
034public abstract class ConditionalCommand extends Command {
035  /**
036   * The Command to execute if {@link ConditionalCommand#condition()} returns true.
037   */
038  private Command m_onTrue;
039
040  /**
041   * The Command to execute if {@link ConditionalCommand#condition()} returns false.
042   */
043  private Command m_onFalse;
044
045  /**
046   * Stores command chosen by condition.
047   */
048  private Command m_chosenCommand = null;
049
050  private void requireAll() {
051    if (m_onTrue != null) {
052      for (Enumeration e = m_onTrue.getRequirements(); e.hasMoreElements(); ) {
053        requires((Subsystem) e.nextElement());
054      }
055    }
056
057    if (m_onFalse != null) {
058      for (Enumeration e = m_onFalse.getRequirements(); e.hasMoreElements(); ) {
059        requires((Subsystem) e.nextElement());
060      }
061    }
062  }
063
064  /**
065   * Creates a new ConditionalCommand with given onTrue and onFalse Commands.
066   *
067   * <p>Users of this constructor should also override condition().
068   *
069   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
070   */
071  public ConditionalCommand(Command onTrue) {
072    this(onTrue, null);
073  }
074
075  /**
076   * Creates a new ConditionalCommand with given onTrue and onFalse Commands.
077   *
078   * <p>Users of this constructor should also override condition().
079   *
080   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
081   * @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
082   */
083  public ConditionalCommand(Command onTrue, Command onFalse) {
084    m_onTrue = onTrue;
085    m_onFalse = onFalse;
086
087    requireAll();
088  }
089
090  /**
091   * Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
092   *
093   * <p>Users of this constructor should also override condition().
094   *
095   * @param name the name for this command group
096   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
097   */
098  public ConditionalCommand(String name, Command onTrue) {
099    this(name, onTrue, null);
100  }
101
102  /**
103   * Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
104   *
105   * <p>Users of this constructor should also override condition().
106   *
107   * @param name the name for this command group
108   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
109   * @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
110   */
111  public ConditionalCommand(String name, Command onTrue, Command onFalse) {
112    super(name);
113    m_onTrue = onTrue;
114    m_onFalse = onFalse;
115
116    requireAll();
117  }
118
119  /**
120   * The Condition to test to determine which Command to run.
121   *
122   * @return true if m_onTrue should be run or false if m_onFalse should be run.
123   */
124  protected abstract boolean condition();
125
126  /**
127   * Calls {@link ConditionalCommand#condition()} and runs the proper command.
128   */
129  @Override
130  protected void _initialize() {
131    if (condition()) {
132      m_chosenCommand = m_onTrue;
133    } else {
134      m_chosenCommand = m_onFalse;
135    }
136
137    if (m_chosenCommand != null) {
138      /*
139       * This is a hack to make cancelling the chosen command inside a
140       * CommandGroup work properly
141       */
142      m_chosenCommand.clearRequirements();
143
144      m_chosenCommand.start();
145    }
146    super._initialize();
147  }
148
149  @Override
150  protected void _cancel() {
151    if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
152      m_chosenCommand.cancel();
153    }
154
155    super._cancel();
156  }
157
158  @Override
159  protected boolean isFinished() {
160    if (m_chosenCommand != null) {
161      return m_chosenCommand.isCompleted();
162    } else {
163      return true;
164    }
165  }
166
167  @Override
168  protected void _interrupted() {
169    if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
170      m_chosenCommand.cancel();
171    }
172
173    super._interrupted();
174  }
175}