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 using a selector and a key to
015 * command mapping.
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 *
023 * @param <K> The type of key used to select the command
024 */
025public class SelectCommand<K> extends Command {
026  private final Map<K, Command> m_commands;
027  private final Supplier<? extends K> m_selector;
028  private Command m_selectedCommand;
029  private boolean m_runsWhenDisabled = true;
030  private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
031
032  private final Command m_defaultCommand =
033      new PrintCommand("SelectCommand selector value does not correspond to any command!");
034
035  /**
036   * Creates a new SelectCommand.
037   *
038   * @param commands the map of commands to choose from
039   * @param selector the selector to determine which command to run
040   */
041  public SelectCommand(Map<K, Command> commands, Supplier<? extends K> selector) {
042    m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
043    m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
044
045    CommandScheduler.getInstance().registerComposedCommands(m_defaultCommand);
046    CommandScheduler.getInstance()
047        .registerComposedCommands(commands.values().toArray(new Command[] {}));
048
049    for (Command command : m_commands.values()) {
050      m_requirements.addAll(command.getRequirements());
051      m_runsWhenDisabled &= command.runsWhenDisabled();
052      if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
053        m_interruptBehavior = InterruptionBehavior.kCancelSelf;
054      }
055    }
056  }
057
058  @Override
059  public void initialize() {
060    m_selectedCommand = m_commands.getOrDefault(m_selector.get(), m_defaultCommand);
061    m_selectedCommand.initialize();
062  }
063
064  @Override
065  public void execute() {
066    m_selectedCommand.execute();
067  }
068
069  @Override
070  public void end(boolean interrupted) {
071    m_selectedCommand.end(interrupted);
072  }
073
074  @Override
075  public boolean isFinished() {
076    return m_selectedCommand.isFinished();
077  }
078
079  @Override
080  public boolean runsWhenDisabled() {
081    return m_runsWhenDisabled;
082  }
083
084  @Override
085  public InterruptionBehavior getInterruptionBehavior() {
086    return m_interruptBehavior;
087  }
088
089  @Override
090  public void initSendable(SendableBuilder builder) {
091    super.initSendable(builder);
092
093    builder.addStringProperty(
094        "selected", () -> m_selectedCommand == null ? "null" : m_selectedCommand.getName(), null);
095  }
096}