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 org.wpilib.command2;
006
007import static org.wpilib.util.ErrorMessages.requireNonNullParam;
008
009import java.util.function.Supplier;
010import org.wpilib.util.sendable.SendableBuilder;
011
012/**
013 * Schedules a given command when this command is initialized and ends when it ends, but does not
014 * directly run it. Use this for including a command in a composition without adding its
015 * requirements, <strong>but only if you know what you are doing. If you are unsure, see <a
016 * href="https://docs.wpilib.org/en/stable/docs/software/commandbased/command-compositions.html#scheduling-other-commands">the
017 * WPILib docs</a> for a complete explanation of proxy semantics.</strong> Do not proxy a command
018 * from a subsystem already required by the composition, or else the composition will cancel itself
019 * when the proxy is reached. If this command is interrupted, it will cancel the command.
020 *
021 * <p>This class is provided by the Commands v2 VendorDep
022 */
023public class ProxyCommand extends Command {
024  private final Supplier<Command> m_supplier;
025  private Command m_command;
026
027  /**
028   * Creates a new ProxyCommand that schedules the given command when initialized, and ends when it
029   * is no longer scheduled.
030   *
031   * @param command the command to run by proxy
032   */
033  @SuppressWarnings("this-escape")
034  public ProxyCommand(Command command) {
035    Command nullCheckedCommand = requireNonNullParam(command, "command", "ProxyCommand");
036    m_supplier = () -> nullCheckedCommand;
037    setName("Proxy(" + nullCheckedCommand.getName() + ")");
038  }
039
040  @Override
041  public void initialize() {
042    m_command = m_supplier.get();
043    CommandScheduler.getInstance().schedule(m_command);
044  }
045
046  @Override
047  public void end(boolean interrupted) {
048    if (interrupted) {
049      m_command.cancel();
050    }
051    m_command = null;
052  }
053
054  @Override
055  public void execute() {}
056
057  @Override
058  public boolean isFinished() {
059    // because we're between `initialize` and `end`, `m_command` is necessarily not null
060    // but if called otherwise and m_command is null,
061    // it's UB, so we can do whatever we want -- like return true.
062    return m_command == null || !m_command.isScheduled();
063  }
064
065  /**
066   * Whether the given command should run when the robot is disabled. Override to return true if the
067   * command should run when disabled.
068   *
069   * @return true. Otherwise, this proxy would cancel commands that do run when disabled.
070   */
071  @Override
072  public boolean runsWhenDisabled() {
073    return true;
074  }
075
076  @Override
077  public void initSendable(SendableBuilder builder) {
078    super.initSendable(builder);
079    builder.addStringProperty(
080        "proxied", () -> m_command == null ? "null" : m_command.getName(), null);
081  }
082}