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