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 final Supplier<Command> m_toRun;
027  private Command m_selectedCommand;
028  private boolean m_runsWhenDisabled = true;
029  private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
030
031  /**
032   * Creates a new SelectCommand.
033   *
034   * @param commands the map of commands to choose from
035   * @param selector the selector to determine which command to run
036   */
037  public SelectCommand(Map<Object, Command> commands, Supplier<Object> selector) {
038    m_commands = requireNonNullParam(commands, "commands", "SelectCommand");
039    m_selector = requireNonNullParam(selector, "selector", "SelectCommand");
040
041    CommandScheduler.getInstance()
042        .registerComposedCommands(commands.values().toArray(new Command[] {}));
043
044    m_toRun = null;
045
046    for (Command command : m_commands.values()) {
047      m_requirements.addAll(command.getRequirements());
048      m_runsWhenDisabled &= command.runsWhenDisabled();
049      if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
050        m_interruptBehavior = InterruptionBehavior.kCancelSelf;
051      }
052    }
053  }
054
055  /**
056   * Creates a new SelectCommand.
057   *
058   * @param toRun a supplier providing the command to run
059   * @deprecated Replace with {@link ProxyCommand}
060   */
061  @Deprecated
062  public SelectCommand(Supplier<Command> toRun) {
063    m_commands = null;
064    m_selector = null;
065    m_toRun = requireNonNullParam(toRun, "toRun", "SelectCommand");
066
067    // we have no way of checking the underlying command, so default.
068    m_runsWhenDisabled = false;
069    m_interruptBehavior = InterruptionBehavior.kCancelSelf;
070  }
071
072  @Override
073  public void initialize() {
074    if (m_selector != null) {
075      if (!m_commands.containsKey(m_selector.get())) {
076        m_selectedCommand =
077            new PrintCommand(
078                "SelectCommand selector value does not correspond to" + " any command!");
079        return;
080      }
081      m_selectedCommand = m_commands.get(m_selector.get());
082    } else {
083      m_selectedCommand = m_toRun.get();
084    }
085    m_selectedCommand.initialize();
086  }
087
088  @Override
089  public void execute() {
090    m_selectedCommand.execute();
091  }
092
093  @Override
094  public void end(boolean interrupted) {
095    m_selectedCommand.end(interrupted);
096  }
097
098  @Override
099  public boolean isFinished() {
100    return m_selectedCommand.isFinished();
101  }
102
103  @Override
104  public boolean runsWhenDisabled() {
105    return m_runsWhenDisabled;
106  }
107
108  @Override
109  public InterruptionBehavior getInterruptionBehavior() {
110    return m_interruptBehavior;
111  }
112
113  @Override
114  public void initSendable(SendableBuilder builder) {
115    super.initSendable(builder);
116
117    builder.addStringProperty(
118        "selected", () -> m_selectedCommand == null ? "null" : m_selectedCommand.getName(), null);
119  }
120}