-
Notifications
You must be signed in to change notification settings - Fork 610
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Update Commands.java Add looseSequence() method #6639
Changes from 7 commits
7a8cd5b
8b36b83
d5d39d3
0f28942
5c858d3
59e77bb
c1576a2
1fb1c9a
0082985
5963974
4272774
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -223,6 +223,22 @@ public static Command sequence(Command... commands) { | |
return new SequentialCommandGroup(commands); | ||
} | ||
|
||
/** | ||
* Runs individual commands in a series without grouped behavior. | ||
* | ||
* <p>Each command is run independently by proxy. The requirements of | ||
* each command are reserved only for the duration of that command and | ||
* are not reserved for an entire group process as they are in a | ||
* grouped sequence. | ||
* | ||
* @param commands the commands to include in the series | ||
* @return the command to run the series of commands | ||
* @see #sequence(Command...) use sequence() to invoke group sequence behavior | ||
*/ | ||
public static Command disjointSequence(Command... commands) { | ||
return sequence(proxyAll(commands)); | ||
} | ||
|
||
/** | ||
* Runs a group of commands in series, one after the other. Once the last command ends, the group | ||
* is restarted. | ||
|
@@ -236,6 +252,24 @@ public static Command repeatingSequence(Command... commands) { | |
return sequence(commands).repeatedly(); | ||
} | ||
|
||
/** | ||
* Runs individual commands in a series without grouped behavior; once the last command ends, the series is restarted. | ||
* | ||
* <p>Each command is run independently by proxy. The requirements of | ||
* each command are reserved only for the duration of that command and | ||
* are not reserved for an entire group process as they are in a | ||
* grouped sequence. | ||
* | ||
* @param commands the commands to include in the series | ||
* @return the command to run the series of commands repeatedly | ||
* @see #sequence(Command...) use sequenceRepeatedly() to invoke repeated group sequence behavior | ||
* @see #disjointSequence(Command...) | ||
* @see Command#repeatedly() | ||
*/ | ||
public static Command repeatingDisjointSequence(Command... commands) { | ||
return disjointSequence(commands).repeatedly(); | ||
} | ||
|
||
/** | ||
* Runs a group of commands at the same time. Ends once all commands in the group finish. | ||
* | ||
|
@@ -246,6 +280,23 @@ public static Command repeatingSequence(Command... commands) { | |
public static Command parallel(Command... commands) { | ||
return new ParallelCommandGroup(commands); | ||
} | ||
|
||
/** | ||
* Runs individual commands at the same time without grouped behavior and ends once all commands finish. | ||
* | ||
* <p>Each command is run independently by proxy. The requirements of | ||
* each command are reserved only for the duration of that command and | ||
* are not reserved for an entire group process as they are in a | ||
* grouped parallel. | ||
* | ||
* @param commands the commands to run in parallel | ||
* @return the command to run the commands in parallel | ||
* @see #parallel(Command...) use parallel() to invoke group parallel behavior | ||
*/ | ||
public static Command disjointParallel(Command... commands) { | ||
new ParallelCommandGroup(commands); // check parallel constraints | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should implement the check for disjoint requirements separately (probably in a private static convenience method)- It's self-documenting and (more importantly) will not register the commands as composed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I agree that my proposed validation that has a side-effect of registering the commands that then must be unregistered is a bit stinky but it appears to save duplicating a lot of code. Tracing the // from ParallelDeadlineGroup.java
public final void setDeadline(Command deadline) {
@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!");
}
addCommands(deadline);
m_deadline = deadline;
}
/**
* Adds the given commands to the group.
*
* @param commands Commands to add to the group.
*/
public final void addCommands(Command... commands) {
if (!m_finished) {
throw new IllegalStateException(
"Commands cannot be added to a composition while it's running");
}
CommandScheduler.getInstance().registerComposedCommands(commands);
for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), m_requirements)) {
throw new IllegalArgumentException(
"Multiple commands in a parallel group cannot require the same subsystems");
}
m_commands.put(command, false);
m_requirements.addAll(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
if (command.getInterruptionBehavior() == InterruptionBehavior.kCancelSelf) {
m_interruptBehavior = InterruptionBehavior.kCancelSelf;
}
}
}
// from CommandScheduler.java
private final Map<Command, Exception> m_composedCommands = new WeakHashMap<>();
/**
* Register commands as composed. An exception will be thrown if these commands are scheduled
* directly or added to a composition.
*
* @param commands the commands to register
* @throws IllegalArgumentException if the given commands have already been composed, or the array
* of commands has duplicates.
*/
public void registerComposedCommands(Command... commands) {
Set<Command> commandSet;
try {
commandSet = Set.of(commands);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(
"Cannot compose a command twice in the same composition! (Original exception: "
+ e
+ ")");
}
requireNotComposedOrScheduled(commandSet);
var exception = new Exception("Originally composed at:");
exception.fillInStackTrace();
for (var command : commands) {
m_composedCommands.put(command, exception);
}
}
/**
* Requires that the specified command hasn't already been added to a composition, and is not
* currently scheduled.
*
* @param command The command to check
* @throws IllegalArgumentException if the given command has already been composed or scheduled.
*/
public void requireNotComposedOrScheduled(Command command) {
if (isScheduled(command)) {
throw new IllegalArgumentException(
"Commands that have been scheduled individually may not be added to a composition!");
}
requireNotComposed(command);
}
/**
* Requires that the specified commands have not already been added to a composition, and are not
* currently scheduled.
*
* @param commands The commands to check
* @throws IllegalArgumentException if the given commands have already been composed or scheduled.
*/
public void requireNotComposedOrScheduled(Collection<Command> commands) {
for (var command : commands) {
requireNotComposedOrScheduled(command);
}
}
/**
* Requires that the specified command hasn't already been added to a composition.
*
* @param commands The commands to check
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public void requireNotComposed(Command... commands) {
for (var command : commands) {
var exception = m_composedCommands.getOrDefault(command, null);
if (exception != null) {
throw new IllegalArgumentException(
"Commands that have been composed may not be added to another composition or scheduled "
+ "individually!",
exception);
}
}
}
/**
* Requires that the specified commands have not already been added to a composition.
*
* @param commands The commands to check
* @throws IllegalArgumentException if the given commands have already been composed.
*/
public void requireNotComposed(Collection<Command> commands) {
requireNotComposed(commands.toArray(Command[]::new));
}
/**
* Whether the given commands are running. Note that this only works on commands that are directly
* scheduled by the scheduler; it will not work on commands inside compositions, as the scheduler
* does not see them.
*
* @param commands the command to query
* @return whether the command is currently scheduled
*/
public boolean isScheduled(Command... commands) {
return m_scheduledCommands.containsAll(Set.of(commands));
} |
||
return parallel(proxyAll(commands)); | ||
} | ||
|
||
/** | ||
* Runs a group of commands at the same time. Ends once any command in the group finishes, and | ||
|
@@ -273,6 +324,24 @@ public static Command deadline(Command deadline, Command... otherCommands) { | |
return new ParallelDeadlineGroup(deadline, otherCommands); | ||
} | ||
|
||
/** | ||
* Runs individual commands at the same time without grouped behavior; when the deadline command ends the otherCommands are cancelled. | ||
* | ||
* <p>Each otherCommand is run independently by proxy. The requirements of | ||
* each command are reserved only for the duration of that command and are | ||
* not reserved for an entire group process as they are in a grouped deadline. | ||
* | ||
* @param deadline the deadline command | ||
* @param otherCommands the other commands to include and will be cancelled when the deadline ends | ||
* @return the command to run the deadline command and otherCommands | ||
* @see #deadline(Command, Command...) use deadline() to invoke group parallel deadline behavior | ||
* @throws IllegalArgumentException if the deadline command is also in the otherCommands argument | ||
*/ | ||
public static Command disjointDeadline(Command deadline, Command... otherCommands) { | ||
new ParallelDeadlineGroup(deadline, otherCommands); // check parallel deadline constraints | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same as |
||
return deadline(deadline, proxyAll(otherCommands)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Maybe but doing that here doesn't help. If the If the The Proxy for the deadline as a grouped command has to be added when that group is made (not when used or referenced later) because after its creation later additional wrapping groupings don't know about the inner groups. This points out again that the Proxy has to be carefully considered minimally applying it starting from the inner commands to the outer commands. Edit after more thought that I might not understand the objective of this disjoint work and I gave a somewhat wrong answer above. I have mostly been thinking about the disjoint of a single group. The Likely this results in a lot of redundant and inefficient wrappings (of wrappings of wrappings). So we're back to "hand-tuning" the requirements by judicious addition of So yes - proxy everything if that's the objective and I now assume it is. |
||
} | ||
|
||
private Commands() { | ||
throw new UnsupportedOperationException("This is a utility class"); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is
sequence(Command...)
this the right method to link to?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed it to a better reference.