2020-12-26 14:12:05 -08:00
|
|
|
// Copyright (c) FIRST and other WPILib contributors.
|
|
|
|
|
// Open Source Software; you can modify and/or share it under the terms of
|
|
|
|
|
// the WPILib BSD license file in the root directory of this project.
|
2019-08-25 17:47:07 -04:00
|
|
|
|
|
|
|
|
package edu.wpi.first.wpilibj2.command;
|
|
|
|
|
|
2022-12-16 04:28:52 +02:00
|
|
|
import edu.wpi.first.util.sendable.SendableBuilder;
|
2019-08-25 17:47:07 -04:00
|
|
|
import java.util.Collections;
|
2024-06-01 12:01:15 -07:00
|
|
|
import java.util.LinkedHashMap;
|
2019-08-25 17:47:07 -04:00
|
|
|
import java.util.Map;
|
|
|
|
|
|
|
|
|
|
/**
|
2022-12-07 07:13:31 +02:00
|
|
|
* A command composition that runs a set of commands in parallel, ending only when a specific
|
|
|
|
|
* command (the "deadline") ends, interrupting all other commands that are still running at that
|
|
|
|
|
* point.
|
2019-08-25 17:47:07 -04:00
|
|
|
*
|
2022-12-07 07:13:31 +02:00
|
|
|
* <p>The rules for command compositions apply: command instances that are passed to it cannot be
|
|
|
|
|
* added to any other composition or scheduled individually, and the composition requires all
|
|
|
|
|
* subsystems its components require.
|
2022-01-08 11:11:34 -08:00
|
|
|
*
|
|
|
|
|
* <p>This class is provided by the NewCommands VendorDep
|
2019-08-25 17:47:07 -04:00
|
|
|
*/
|
2023-07-14 01:12:01 -04:00
|
|
|
public class ParallelDeadlineGroup extends Command {
|
2022-12-07 07:13:31 +02:00
|
|
|
// maps commands in this composition to whether they are still running
|
2024-06-01 12:01:15 -07:00
|
|
|
// LinkedHashMap guarantees we iterate over commands in the order they were added (Note that
|
|
|
|
|
// changing the value associated with a command does NOT change the order)
|
|
|
|
|
private final Map<Command, Boolean> m_commands = new LinkedHashMap<>();
|
2019-08-25 17:47:07 -04:00
|
|
|
private boolean m_runWhenDisabled = true;
|
2019-11-18 18:33:45 -05:00
|
|
|
private boolean m_finished = true;
|
2019-08-25 17:47:07 -04:00
|
|
|
private Command m_deadline;
|
2022-11-28 02:23:56 +02:00
|
|
|
private InterruptionBehavior m_interruptBehavior = InterruptionBehavior.kCancelIncoming;
|
2019-08-25 17:47:07 -04:00
|
|
|
|
|
|
|
|
/**
|
2023-12-26 18:07:16 -08:00
|
|
|
* Creates a new ParallelDeadlineGroup. The given commands, including the deadline, will be
|
2022-12-07 07:13:31 +02:00
|
|
|
* executed simultaneously. The composition will finish when the deadline finishes, interrupting
|
|
|
|
|
* all other still-running commands. If the composition is interrupted, only the commands still
|
2020-12-29 22:45:16 -08:00
|
|
|
* running will be interrupted.
|
2019-08-25 17:47:07 -04:00
|
|
|
*
|
2022-12-07 07:13:31 +02:00
|
|
|
* @param deadline the command that determines when the composition ends
|
2023-12-26 18:07:16 -08:00
|
|
|
* @param otherCommands the other commands to be executed
|
|
|
|
|
* @throws IllegalArgumentException if the deadline command is also in the otherCommands argument
|
2019-08-25 17:47:07 -04:00
|
|
|
*/
|
2024-07-13 06:52:30 -07:00
|
|
|
@SuppressWarnings("this-escape")
|
2023-12-26 18:07:16 -08:00
|
|
|
public ParallelDeadlineGroup(Command deadline, Command... otherCommands) {
|
|
|
|
|
setDeadline(deadline);
|
2024-06-01 12:01:15 -07:00
|
|
|
addCommands(otherCommands);
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2020-12-29 22:45:16 -08:00
|
|
|
* Sets the deadline to the given command. The deadline is added to the group if it is not already
|
|
|
|
|
* contained.
|
2019-08-25 17:47:07 -04:00
|
|
|
*
|
|
|
|
|
* @param deadline the command that determines when the group ends
|
2023-12-26 18:07:16 -08:00
|
|
|
* @throws IllegalArgumentException if the deadline command is already in the composition
|
2019-08-25 17:47:07 -04:00
|
|
|
*/
|
2023-12-27 07:51:16 -08:00
|
|
|
public final void setDeadline(Command deadline) {
|
2023-12-26 18:07:16 -08:00
|
|
|
@SuppressWarnings("PMD.CompareObjectsWithEquals")
|
|
|
|
|
boolean isAlreadyDeadline = deadline == m_deadline;
|
|
|
|
|
if (isAlreadyDeadline) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
if (m_commands.containsKey(deadline)) {
|
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
|
"The deadline command cannot also be in the other commands!");
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
2023-12-26 18:07:16 -08:00
|
|
|
addCommands(deadline);
|
2019-08-25 17:47:07 -04:00
|
|
|
m_deadline = deadline;
|
|
|
|
|
}
|
|
|
|
|
|
2023-07-10 12:56:18 -04:00
|
|
|
/**
|
|
|
|
|
* Adds the given commands to the group.
|
|
|
|
|
*
|
|
|
|
|
* @param commands Commands to add to the group.
|
|
|
|
|
*/
|
2019-08-25 17:47:07 -04:00
|
|
|
public final void addCommands(Command... commands) {
|
2019-11-18 18:33:45 -05:00
|
|
|
if (!m_finished) {
|
2019-08-25 17:47:07 -04:00
|
|
|
throw new IllegalStateException(
|
2022-12-07 07:13:31 +02:00
|
|
|
"Commands cannot be added to a composition while it's running");
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
|
2022-12-07 07:13:31 +02:00
|
|
|
CommandScheduler.getInstance().registerComposedCommands(commands);
|
2019-08-25 17:47:07 -04:00
|
|
|
|
|
|
|
|
for (Command command : commands) {
|
2024-07-12 21:51:21 +08:00
|
|
|
if (!Collections.disjoint(command.getRequirements(), getRequirements())) {
|
2020-12-29 22:45:16 -08:00
|
|
|
throw new IllegalArgumentException(
|
2023-12-26 18:07:16 -08:00
|
|
|
"Multiple commands in a parallel group cannot require the same subsystems");
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
m_commands.put(command, false);
|
2024-07-12 06:01:54 +08:00
|
|
|
addRequirements(command.getRequirements());
|
2019-08-25 17:47:07 -04:00
|
|
|
m_runWhenDisabled &= command.runsWhenDisabled();
|
2022-11-28 02:23:56 +02:00
|
|
|
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
|
|
|
|
|
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
|
|
|
|
|
}
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2022-08-31 19:15:28 +03:00
|
|
|
public final void initialize() {
|
2019-08-25 17:47:07 -04:00
|
|
|
for (Map.Entry<Command, Boolean> commandRunning : m_commands.entrySet()) {
|
|
|
|
|
commandRunning.getKey().initialize();
|
|
|
|
|
commandRunning.setValue(true);
|
|
|
|
|
}
|
2019-11-18 18:33:45 -05:00
|
|
|
m_finished = false;
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2022-08-31 19:15:28 +03:00
|
|
|
public final void execute() {
|
2019-08-25 17:47:07 -04:00
|
|
|
for (Map.Entry<Command, Boolean> commandRunning : m_commands.entrySet()) {
|
|
|
|
|
if (!commandRunning.getValue()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
commandRunning.getKey().execute();
|
|
|
|
|
if (commandRunning.getKey().isFinished()) {
|
|
|
|
|
commandRunning.getKey().end(false);
|
|
|
|
|
commandRunning.setValue(false);
|
2021-12-09 12:20:08 -08:00
|
|
|
if (commandRunning.getKey().equals(m_deadline)) {
|
2019-11-18 18:33:45 -05:00
|
|
|
m_finished = true;
|
|
|
|
|
}
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2022-08-31 19:15:28 +03:00
|
|
|
public final void end(boolean interrupted) {
|
2019-08-25 17:47:07 -04:00
|
|
|
for (Map.Entry<Command, Boolean> commandRunning : m_commands.entrySet()) {
|
|
|
|
|
if (commandRunning.getValue()) {
|
|
|
|
|
commandRunning.getKey().end(true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
2022-08-31 19:15:28 +03:00
|
|
|
public final boolean isFinished() {
|
2019-11-18 18:33:45 -05:00
|
|
|
return m_finished;
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean runsWhenDisabled() {
|
|
|
|
|
return m_runWhenDisabled;
|
|
|
|
|
}
|
2022-11-28 02:23:56 +02:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public InterruptionBehavior getInterruptionBehavior() {
|
|
|
|
|
return m_interruptBehavior;
|
|
|
|
|
}
|
2022-12-16 04:28:52 +02:00
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void initSendable(SendableBuilder builder) {
|
|
|
|
|
super.initSendable(builder);
|
|
|
|
|
|
|
|
|
|
builder.addStringProperty("deadline", m_deadline::getName, null);
|
|
|
|
|
}
|
2019-08-25 17:47:07 -04:00
|
|
|
}
|