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  @SuppressWarnings("this-escape")
042  public SelectCommand(Map<K, Command> commands, Supplier<? extends K> selector) {
043    m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
044    m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
045
046    CommandScheduler.getInstance().registerComposedCommands(m_defaultCommand);
047    CommandScheduler.getInstance()
048        .registerComposedCommands(commands.values().toArray(new Command[] {}));
049
050    for (Command command : m_commands.values()) {
051      addRequirements(command.getRequirements());
052      m_runsWhenDisabled &= command.runsWhenDisabled();
053      if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
054        m_interruptBehavior = InterruptionBehavior.kCancelSelf;
055      }
056    }
057  }
058
059  @Override
060  public void initialize() {
061    m_selectedCommand = m_commands.getOrDefault(m_selector.get(), m_defaultCommand);
062    m_selectedCommand.initialize();
063  }
064
065  @Override
066  public void execute() {
067    m_selectedCommand.execute();
068  }
069
070  @Override
071  public void end(boolean interrupted) {
072    m_selectedCommand.end(interrupted);
073  }
074
075  @Override
076  public boolean isFinished() {
077    return m_selectedCommand.isFinished();
078  }
079
080  @Override
081  public boolean runsWhenDisabled() {
082    return m_runsWhenDisabled;
083  }
084
085  @Override
086  public InterruptionBehavior getInterruptionBehavior() {
087    return m_interruptBehavior;
088  }
089
090  @Override
091  public void initSendable(SendableBuilder builder) {
092    super.initSendable(builder);
093
094    builder.addStringProperty(
095        "selected", () -> m_selectedCommand == null ? "null" : m_selectedCommand.getName(), null);
096  }
097}