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.wpilibj.util.ErrorMessages.requireNonNullParam;
008import static edu.wpi.first.wpilibj2.command.CommandGroupBase.requireUngrouped;
009
010import java.util.Map;
011import java.util.function.Supplier;
012
013/**
014 * Runs one of a selection of commands, either using a selector and a key to command mapping, or a
015 * supplier that returns the command directly at runtime. Does not actually schedule the selected
016 * command - rather, the command is run through this command; this ensures that the command will
017 * behave as expected if used as part of a CommandGroup. Requires the requirements of all included
018 * commands, again to ensure proper functioning when used in a CommandGroup. If this is undesired,
019 * consider using {@link ScheduleCommand}.
020 *
021 * <p>As this command contains multiple component commands within it, it is technically a command
022 * group; the command instances that are passed to it cannot be added to any other groups, or
023 * scheduled individually.
024 *
025 * <p>As a rule, CommandGroups require the union of the requirements of their component commands.
026 */
027public class SelectCommand extends CommandBase {
028  private final Map<Object, Command> m_commands;
029  private final Supplier<Object> m_selector;
030  private final Supplier<Command> m_toRun;
031  private Command m_selectedCommand;
032
033  /**
034   * Creates a new selectcommand.
035   *
036   * @param commands the map of commands to choose from
037   * @param selector the selector to determine which command to run
038   */
039  public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) {
040    requireUngrouped(commands.values());
041
042    CommandGroupBase.registerGroupedCommands(commands.values().toArray(new Command[] {}));
043
044    m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
045    m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
046
047    m_toRun = null;
048
049    for (Command command : m_commands.values()) {
050      m_requirements.addAll(command.getRequirements());
051    }
052  }
053
054  /**
055   * Creates a new selectcommand.
056   *
057   * @param toRun a supplier providing the command to run
058   */
059  public SelectCommand(Supplier<Command> toRun) {
060    m_commands = null;
061    m_selector = null;
062    m_toRun = requireNonNullParam(toRun, "toRun", "SelectCommand");
063  }
064
065  @Override
066  public void initialize() {
067    if (m_selector != null) {
068      if (!m_commands.keySet().contains(m_selector.get())) {
069        m_selectedCommand =
070            new PrintCommand(
071                "SelectCommand selector value does not correspond to" + " any command!");
072        return;
073      }
074      m_selectedCommand = m_commands.get(m_selector.get());
075    } else {
076      m_selectedCommand = m_toRun.get();
077    }
078    m_selectedCommand.initialize();
079  }
080
081  @Override
082  public void execute() {
083    m_selectedCommand.execute();
084  }
085
086  @Override
087  public void end(boolean interrupted) {
088    m_selectedCommand.end(interrupted);
089  }
090
091  @Override
092  public boolean isFinished() {
093    return m_selectedCommand.isFinished();
094  }
095
096  @Override
097  public boolean runsWhenDisabled() {
098    if (m_commands != null) {
099      boolean runsWhenDisabled = true;
100      for (Command command : m_commands.values()) {
101        runsWhenDisabled &= command.runsWhenDisabled();
102      }
103      return runsWhenDisabled;
104    } else {
105      return m_toRun.get().runsWhenDisabled();
106    }
107  }
108}