001/*----------------------------------------------------------------------------*/
002/* Copyright (c) FIRST 2017. 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  /**
051   * Creates a new ConditionalCommand with given onTrue and onFalse Commands.
052   *
053   * <p>Users of this constructor should also override condition().
054   *
055   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
056   */
057  public ConditionalCommand(Command onTrue) {
058    this(onTrue, new InstantCommand());
059  }
060
061  /**
062   * Creates a new ConditionalCommand with given onTrue and onFalse Commands.
063   *
064   * <p>Users of this constructor should also override condition().
065   *
066   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
067   * @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
068   */
069  public ConditionalCommand(Command onTrue, Command onFalse) {
070    m_onTrue = onTrue;
071    m_onFalse = onFalse;
072
073    for (Enumeration e = m_onTrue.getRequirements(); e.hasMoreElements(); ) {
074      requires((Subsystem) e.nextElement());
075    }
076
077    for (Enumeration e = m_onFalse.getRequirements(); e.hasMoreElements(); ) {
078      requires((Subsystem) e.nextElement());
079    }
080  }
081
082  /**
083   * Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
084   *
085   * <p>Users of this constructor should also override condition().
086   *
087   * @param name the name for this command group
088   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
089   */
090  public ConditionalCommand(String name, Command onTrue) {
091    this(name, onTrue, new InstantCommand());
092  }
093
094  /**
095   * Creates a new ConditionalCommand with given name and onTrue and onFalse Commands.
096   *
097   * <p>Users of this constructor should also override condition().
098   *
099   * @param name the name for this command group
100   * @param onTrue The Command to execute if {@link ConditionalCommand#condition()} returns true
101   * @param onFalse The Command to execute if {@link ConditionalCommand#condition()} returns false
102   */
103  public ConditionalCommand(String name, Command onTrue, Command onFalse) {
104    super(name);
105    m_onTrue = onTrue;
106    m_onFalse = onFalse;
107
108    for (Enumeration e = m_onTrue.getRequirements(); e.hasMoreElements(); ) {
109      requires((Subsystem) e.nextElement());
110    }
111
112    for (Enumeration e = m_onFalse.getRequirements(); e.hasMoreElements(); ) {
113      requires((Subsystem) e.nextElement());
114    }
115  }
116
117  /**
118   * The Condition to test to determine which Command to run.
119   *
120   * @return true if m_onTrue should be run or false if m_onFalse should be run.
121   */
122  protected abstract boolean condition();
123
124  /**
125   * Calls {@link ConditionalCommand#condition()} and runs the proper command.
126   */
127  @Override
128  protected void _initialize() {
129    if (condition()) {
130      m_chosenCommand = m_onTrue;
131    } else {
132      m_chosenCommand = m_onFalse;
133    }
134
135    // This is a hack to make cancelling the chosen command inside a CommandGroup work properly
136    m_chosenCommand.clearRequirements();
137
138    m_chosenCommand.start();
139  }
140
141  @Override
142  protected void _cancel() {
143    if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
144      m_chosenCommand.cancel();
145    }
146
147    super._cancel();
148  }
149
150  @Override
151  protected boolean isFinished() {
152    return m_chosenCommand != null && m_chosenCommand.isRunning()
153        && m_chosenCommand.isFinished();
154  }
155
156  @Override
157  protected void interrupted() {
158    if (m_chosenCommand != null && m_chosenCommand.isRunning()) {
159      m_chosenCommand.cancel();
160    }
161
162    super.interrupted();
163  }
164}