Skip to content
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

[commands] Clarified error messages for parallel composition commands #7161

Open
wants to merge 18 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,38 @@ public WrapperCommand withName(String name) {
return wrapper;
}

/**
* Throws an error if a parallel group already shares
* one or more requirements with a command
* that will be added to it.
Comment on lines +585 to +587
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an FYI, this will need to be formatted (and since your main branch is a PR branch, /format on your PRs won't work). You can look at the comment-command .yml file to see what steps you should run to format. (pip3 install wpiformat==2024.45, wpiformat, and ./gradlew spotlessApply)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah forgot to run /format

*
* @param parallelGroup The parallel group command.
* @param toAdd The command that will be added to the parallel group.
*/
protected void ensureDisjointRequirements(Command parallelGroup, Command toAdd) {
var sharedRequirements = new HashSet<>(parallelGroup.getRequirements());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected void ensureDisjointRequirements(Command parallelGroup, Command toAdd) {
var sharedRequirements = new HashSet<>(parallelGroup.getRequirements());
protected void ensureDisjointRequirements(Command toAdd) {
var sharedRequirements = new HashSet<>(getRequirements());

Might as well use that it's non-static.

sharedRequirements.retainAll(toAdd.getRequirements());
if (!sharedRequirements.isEmpty()) {
StringBuilder sharedRequirementsStr = new StringBuilder();
boolean first = true;
for (Subsystem requirement: sharedRequirements) {
if (first) {
first = false;
} else {
sharedRequirementsStr.append(", ");
}
sharedRequirementsStr.append(requirement.getName());
}
throw new IllegalArgumentException(
String.format(
"Command %s could not be added to this parallel group"
+ " because the subsystems [%s] are already required in this command."
+ " Multiple commands in a parallel composition cannot require"
+ " the same subsystems.",
toAdd.getName(), sharedRequirementsStr));
}
}

@Override
public void initSendable(SendableBuilder builder) {
builder.setSmartDashboardType("Command");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package edu.wpi.first.wpilibj2.command;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

Expand Down Expand Up @@ -51,10 +50,7 @@ public final void addCommands(Command... commands) {
CommandScheduler.getInstance().registerComposedCommands(commands);

for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), getRequirements())) {
throw new IllegalArgumentException(
"Multiple commands in a parallel composition cannot require the same subsystems");
}
Commands.ensureDisjointRequirements(this, command);
m_commands.put(command, false);
addRequirements(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
package edu.wpi.first.wpilibj2.command;

import edu.wpi.first.util.sendable.SendableBuilder;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

Expand Down Expand Up @@ -81,10 +80,7 @@ public final void addCommands(Command... commands) {
CommandScheduler.getInstance().registerComposedCommands(commands);

for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), getRequirements())) {
throw new IllegalArgumentException(
"Multiple commands in a parallel group cannot require the same subsystems");
}
Commands.ensureDisjointRequirements(this, command);
m_commands.put(command, false);
addRequirements(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@

package edu.wpi.first.wpilibj2.command;

import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;

Expand Down Expand Up @@ -52,10 +51,7 @@ public final void addCommands(Command... commands) {
CommandScheduler.getInstance().registerComposedCommands(commands);

for (Command command : commands) {
if (!Collections.disjoint(command.getRequirements(), getRequirements())) {
throw new IllegalArgumentException(
"Multiple commands in a parallel composition cannot require the same subsystems");
}
Commands.ensureDisjointRequirements(this, command);
m_commands.add(command);
addRequirements(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
Expand Down
30 changes: 24 additions & 6 deletions wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

#include <string>
#include <utility>
#include <vector>
Daniel1464 marked this conversation as resolved.
Show resolved Hide resolved

#include <wpi/StackTrace.h>
#include <wpi/sendable/SendableBuilder.h>
Expand Down Expand Up @@ -218,12 +219,29 @@ void Command::InitSendable(wpi::SendableBuilder& builder) {
}

namespace frc2 {
bool RequirementsDisjoint(Command* first, Command* second) {
bool disjoint = true;
auto&& requirements = second->GetRequirements();
for (auto&& requirement : first->GetRequirements()) {
disjoint &= requirements.find(requirement) == requirements.end();

void EnsureDisjointRequirements(Command* parallelGroup, Command* toAdd) {
std::string sharedRequirementsStr = "";
bool hasSharedRequirements = false;
auto&& requirementsToAdd = toAdd->GetRequirements();
for (auto&& requirement : parallelGroup->GetRequirements()) {
if (requirementsToAdd.find(requirement) != requirementsToAdd.end()) {
sharedRequirementsStr.append(requirement->GetName());
if (!hasSharedRequirements) {
hasSharedRequirements = true; // ensures formatting like "a, b, c"
} else {
sharedRequirementsStr.append(", ");
}
}
}
if (hasSharedRequirements) {
throw FRC_MakeError(
frc::err::CommandIllegalUse,
"Command {} could not be added to this parallel group"
" because the subsystems [{}] are already required in this command."
" Multiple commands in a parallel composition cannot require the "
"same subsystems.",
toAdd->GetName(), sharedRequirementsStr);
}
return disjoint;
}
} // namespace frc2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "frc2/command/ParallelCommandGroup.h"

#include <string>
Daniel1464 marked this conversation as resolved.
Show resolved Hide resolved
#include <utility>
#include <vector>

Expand Down Expand Up @@ -75,19 +76,14 @@ void ParallelCommandGroup::AddCommands(
}

for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) {
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,
"Multiple commands in a parallel group cannot "
"require the same subsystems");
EnsureDisjointRequirements(this, command.get());
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include "frc2/command/ParallelDeadlineGroup.h"

#include <memory>
#include <string>
Daniel1464 marked this conversation as resolved.
Show resolved Hide resolved
#include <utility>
#include <vector>

Expand Down Expand Up @@ -75,20 +76,15 @@ void ParallelDeadlineGroup::AddCommands(
}

for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) {
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,
"Multiple commands in a parallel group cannot "
"require the same subsystems");
EnsureDisjointRequirements(this, command.get());
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command), false);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "frc2/command/ParallelRaceGroup.h"

#include <string>
Daniel1464 marked this conversation as resolved.
Show resolved Hide resolved
#include <utility>
#include <vector>

Expand Down Expand Up @@ -62,19 +63,14 @@ void ParallelRaceGroup::AddCommands(
}

for (auto&& command : commands) {
if (RequirementsDisjoint(this, command.get())) {
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command));
} else {
throw FRC_MakeError(frc::err::CommandIllegalUse,
"Multiple commands in a parallel group cannot "
"require the same subsystems");
EnsureDisjointRequirements(this, command.get());
command->SetComposed(true);
AddRequirements(command->GetRequirements());
m_runWhenDisabled &= command->RunsWhenDisabled();
if (command->GetInterruptionBehavior() ==
Command::InterruptionBehavior::kCancelSelf) {
m_interruptBehavior = Command::InterruptionBehavior::kCancelSelf;
}
m_commands.emplace_back(std::move(command));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <memory>
#include <optional>
#include <string>
#include <vector>
Daniel1464 marked this conversation as resolved.
Show resolved Hide resolved

#include <units/time.h>
#include <wpi/Demangle.h>
Expand Down Expand Up @@ -492,11 +493,12 @@ class Command : public wpi::Sendable, public wpi::SendableHelper<Command> {
};

/**
* Checks if two commands have disjoint requirement sets.
* Throws an error if a parallel group already shares
* one or more requirements with a command
* that will be added to it.
*
* @param first The first command to check.
* @param second The second command to check.
* @return False if first and second share a requirement.
* @param parallelGroup The parallel group command.
* @param toAdd The command that will be added to the parallel group.
*/
bool RequirementsDisjoint(Command* first, Command* second);
void EnsureDisjointRequirements(Command* parallelGroup, Command* toAdd);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could move this into the Command class too.

} // namespace frc2