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.wpilibj2.command;
006
007import static edu.wpi.first.util.ErrorMessages.requireNonNullParam;
008
009import edu.wpi.first.util.sendable.SendableBuilder;
010import java.util.Map;
011import java.util.function.Supplier;
012
013/**
014 * A command composition that runs one of a selection of commands, either using a selector and a key
015 * to command mapping, or a supplier that returns the command directly at runtime.
016 *
017 * <p>The rules for command compositions apply: command instances that are passed to it cannot be
018 * added to any other composition or scheduled individually, and the composition requires all
019 * subsystems its components require.
020 *
021 * <p>This class is provided by the NewCommands VendorDep
022 */
023public class SelectCommand extends CommandBase {
024  private final Map<Object, Command> m_commands;
025  private final Supplier<Object> m_selector;
026  private Command m_selectedCommand;
027  private boolean m_runsWhenDisabled = true;
028  private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
029
030  /**
031   * Creates a new SelectCommand.
032   *
033   * @param commands the map of commands to choose from
034   * @param selector the selector to determine which command to run
035   */
036  public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) {
037    m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
038    m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
039
040    CommandScheduler.getInstance()
041        .registerComposedCommands(commands.values().toArray(new Command[] {}));
042
043    for (Command command : m_commands.values()) {
044      m_requirements.addAll(command.getRequirements());
045      m_runsWhenDisabled &= command.runsWhenDisabled();
046      if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
047        m_interruptBehavior = InterruptionBehavior.kCancelSelf;
048      }
049    }
050  }
051
052  @Override
053  public void initialize() {
054    if (!m_commands.containsKey(m_selector.get())) {
055      m_selectedCommand =
056          new PrintCommand("SelectCommand selector value does not correspond to any command!");
057    } else {
058      m_selectedCommand = m_commands.get(m_selector.get());
059    }
060    m_selectedCommand.initialize();
061  }
062
063  @Override
064  public void execute() {
065    m_selectedCommand.execute();
066  }
067
068  @Override
069  public void end(boolean interrupted) {
070    m_selectedCommand.end(interrupted);
071  }
072
073  @Override
074  public boolean isFinished() {
075    return m_selectedCommand.isFinished();
076  }
077
078  @Override
079  public boolean runsWhenDisabled() {
080    return m_runsWhenDisabled;
081  }
082
083  @Override
084  public InterruptionBehavior getInterruptionBehavior() {
085    return m_interruptBehavior;
086  }
087
088  @Override
089  public void initSendable(SendableBuilder builder) {
090    super.initSendable(builder);
091
092    builder.addStringProperty(
093        "selected", () -> m_selectedCommand == null ? "null" : m_selectedCommand.getName(), null);
094  }
095}