Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
1fcdb26
[commands] Clarified error messages for parallel composition commands…
Daniel1464 Oct 4, 2024
f66ee4f
FRC_MakeError usage correction
Daniel1464 Oct 4, 2024
2651199
Added formatting corrections
Daniel1464 Oct 4, 2024
e704b58
Changed error message to only mention infringing/shared requirements …
Daniel1464 Oct 5, 2024
148fd34
Formatting part 2
Daniel1464 Oct 5, 2024
c971a81
Merge branch 'wpilibsuite:main' into main
Daniel1464 Oct 5, 2024
a95e41f
Changed "==" to "!="
Daniel1464 Oct 5, 2024
aed05dc
Added vector #include calls
Daniel1464 Oct 27, 2024
c5432ed
Merge branch 'wpilibsuite:main' into main
Daniel1464 Oct 28, 2024
09cce4b
Replaced stream api usage
Daniel1464 Nov 5, 2024
178ab5b
Reduced boilerplate
Daniel1464 Nov 7, 2024
6712d4f
Fixed syntax err
Daniel1464 Nov 7, 2024
3d276de
changed ensureDisjointRequirements to a protected instance method of …
Daniel1464 Nov 7, 2024
b5e15d3
Standardized c++ and java usage
Daniel1464 Nov 7, 2024
96be9c9
Fixed small issue
Daniel1464 Nov 7, 2024
e713965
Fixed small issue
Daniel1464 Nov 7, 2024
05f8fc6
Fixed minor formatting issue
Daniel1464 Nov 8, 2024
49277f6
Removed unecessary imports
Daniel1464 Nov 18, 2024
cd277f2
Initial
Daniel1464 Jan 23, 2025
445aef0
Apply suggestions from code review
Daniel1464 Feb 6, 2025
34d62ac
Merge branch 'main', remote-tracking branch 'origin' into profile-sta…
Daniel1464 Aug 5, 2025
8062070
temp commit, yet to fix static assertions
Daniel1464 Aug 5, 2025
4580ec0
Lint
Daniel1464 Aug 6, 2025
7f1d5a9
Trying various things; none of them work
Daniel1464 Aug 6, 2025
afa8643
Removed profile state structs for cpp because they legit dont work
Daniel1464 Aug 6, 2025
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 @@ -605,6 +605,37 @@ 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.
*
* @param toAdd The command that will be added to the parallel group.
*/
protected void ensureDisjointRequirements(Command toAdd) {
var sharedRequirements = new HashSet<>(getRequirements());
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");
}
ensureDisjointRequirements(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");
}
ensureDisjointRequirements(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");
}
ensureDisjointRequirements(command);
m_commands.add(command);
addRequirements(command.getRequirements());
m_runWhenDisabled &= command.runsWhenDisabled();
Expand Down
36 changes: 25 additions & 11 deletions wpilibNewCommands/src/main/native/cpp/frc2/command/Command.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,31 @@ std::optional<std::string> Command::GetPreviousCompositionSite() const {
return m_previousComposition;
}

void Command::EnsureDisjointRequirements(Command* toAdd) {
std::string sharedRequirementsStr = "";
bool hasSharedRequirements = false;
auto&& requirementsToAdd = toAdd->GetRequirements();
for (auto&& requirement : this->GetRequirements()) {
if (requirementsToAdd.find(requirement) != requirementsToAdd.end()) {
if (!hasSharedRequirements) {
hasSharedRequirements = true; // ensures formatting like "a, b, c"
} else {
sharedRequirementsStr.append(", ");
}
sharedRequirementsStr.append(requirement->GetName());
}
}
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);
}
}

void Command::InitSendable(wpi::SendableBuilder& builder) {
builder.SetSmartDashboardType("Command");
builder.AddStringProperty(".name", [this] { return GetName(); }, nullptr);
Expand Down Expand Up @@ -220,14 +245,3 @@ void Command::InitSendable(wpi::SendableBuilder& builder) {
builder.AddBooleanProperty(
"runsWhenDisabled", [this] { return RunsWhenDisabled(); }, nullptr);
}

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();
}
return disjoint;
}
} // namespace frc2
Original file line number Diff line number Diff line change
Expand Up @@ -75,19 +75,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(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 @@ -75,20 +75,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(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 @@ -62,19 +62,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(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 @@ -479,19 +479,21 @@ class Command : public wpi::Sendable, public wpi::SendableHelper<Command> {
protected:
Command();

/**
* Throws an error if a parallel group already shares
* one or more requirements with a command
* that will be added to it.
*
* @param parallelGroup The parallel group command.
* @param toAdd The command that will be added to the parallel group.
*/
void EnsureDisjointRequirements(Command* toAdd);

private:
/// Requirements set.
wpi::SmallSet<Subsystem*, 4> m_requirements;

std::optional<std::string> m_previousComposition;
};

/**
* Checks if two commands have disjoint requirement sets.
*
* @param first The first command to check.
* @param second The second command to check.
* @return False if first and second share a requirement.
*/
bool RequirementsDisjoint(Command* first, Command* second);
} // namespace frc2
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@

package edu.wpi.first.math.trajectory;

import edu.wpi.first.math.trajectory.struct.ExponentialProfileStateStruct;
import edu.wpi.first.math.trajectory.struct.ExponentialProfileTimingStruct;
import edu.wpi.first.util.struct.StructSerializable;

import java.util.Objects;

/**
Expand Down Expand Up @@ -40,7 +44,10 @@ public class ExponentialProfile {
private final Constraints m_constraints;

/** Profile timing. */
public static class ProfileTiming {
public static class ProfileTiming implements StructSerializable {
/** The struct used for serializing this class. */
public static final ExponentialProfileTimingStruct struct = new ExponentialProfileTimingStruct();

/** Profile inflection time. */
public final double inflectionTime;

Expand All @@ -53,7 +60,7 @@ public static class ProfileTiming {
* @param inflectionTime Profile inflection time.
* @param totalTime Total profile time.
*/
protected ProfileTiming(double inflectionTime, double totalTime) {
public ProfileTiming(double inflectionTime, double totalTime) {
this.inflectionTime = inflectionTime;
this.totalTime = totalTime;
}
Expand Down Expand Up @@ -128,7 +135,10 @@ public static Constraints fromStateSpace(double maxInput, double A, double B) {
}

/** Profile state. */
public static class State {
public static class State implements StructSerializable {
/** The struct used for serializing this class. */
public static final ExponentialProfileStateStruct struct = new ExponentialProfileStateStruct();

/** The position at this state. */
public double position;

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

import edu.wpi.first.math.MathSharedStore;
import edu.wpi.first.math.MathUsageId;
import edu.wpi.first.math.trajectory.struct.TrapezoidProfileStateStruct;
import edu.wpi.first.util.struct.StructSerializable;

import java.util.Objects;

/**
Expand Down Expand Up @@ -75,7 +78,10 @@ public Constraints(double maxVelocity, double maxAcceleration) {
}

/** Profile state. */
public static class State {
public static class State implements StructSerializable {
/** The struct used for serializing this class. */
public static final TrapezoidProfileStateStruct struct = new TrapezoidProfileStateStruct();

/** The position at this state. */
public double position;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// 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.

package edu.wpi.first.math.trajectory.struct;

import edu.wpi.first.math.trajectory.ExponentialProfile;
import edu.wpi.first.util.struct.Struct;

import java.nio.ByteBuffer;

public class ExponentialProfileStateStruct implements Struct<ExponentialProfile.State> {
@Override
public Class<ExponentialProfile.State> getTypeClass() {
return ExponentialProfile.State.class;
}

@Override
public String getTypeName() {
return "ExponentialProfileState";
}

@Override
public int getSize() {
return kSizeDouble * 2;
}

@Override
public String getSchema() {
return "double position;double velocity";
}

@Override
public ExponentialProfile.State unpack(ByteBuffer bb) {
return new ExponentialProfile.State(bb.getDouble(), bb.getDouble());
}

@Override
public void pack(ByteBuffer bb, ExponentialProfile.State value) {
bb.putDouble(value.position);
bb.putDouble(value.velocity);
}
}
Loading
Loading