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

[cmd] whileTrueLowPriority on trigger #7097

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

oh-yes-0-fps
Copy link
Contributor

I want some input on this before I port it to C++.

Specifically how do people feel about the Command Trigger factories and whats a better name for whileTrueLowPriority (maybe whileTrueDefault?).

@oh-yes-0-fps oh-yes-0-fps requested a review from a team as a code owner September 19, 2024 03:47
Copy link
Contributor

This PR modifies commands. Please open a corresponding PR in Python Commands and include a link to this PR.

@oh-yes-0-fps oh-yes-0-fps marked this pull request as draft September 19, 2024 03:51
@SamCarlberg
Copy link
Member

These seem footgun-y to me. Composed commands are invisible to the scheduler, so their isScheduled method calls will always return false:

var nextCommand = subsystem.run(...);

controller.a().onTrue(subsystem.someCommand().andThen(nextCommand));

// The scheduler does not see this command due to its inclusion in a composition
// This trigger can never fire
nextCommand.done().onTrue(...);

They are also specific to the command instances on which they're invoked, so something like this would never work:

controller.a().onTrue(subsystem.someCommand())

// New instance from someCommand() that never gets scheduled
// This trigger can never fire
subsystem.someCommand().done().onTrue(subsystem.someOtherCommand());

@oh-yes-0-fps oh-yes-0-fps changed the title [cmd] whileTrueLowPriority on trigger and command trigger factories [cmd] whileTrueLowPriority on trigger Sep 19, 2024
@Starlight220
Copy link
Member

I think that whileTrueLowPriority is a bit too complex/niche and could be solved by a Subsystem.idle trigger that would be more useful to more use cases.
A few years ago I tried reimplementing default commands that way and ran into the problem of triggers not triggering on the initial state.

As for the triggers on Command, I'm not sure how useful they'd be? Triggering on end can be done via finallyDo/andThen

@SamCarlberg
Copy link
Member

@oh-yes-0-fps
Copy link
Contributor Author

oh-yes-0-fps commented Sep 19, 2024

I think that whileTrueLowPriority is a bit too complex/niche

This is something that's being "upstreamed" from the new choreolib rewrite.
You can find examples here, the loop.enabled().whileTrueDefault(spinnup()); is the usage example. I'm not opposed to Subsystem.idle() trigger aswell but something like this would be very helpful for these trigger based autos.

@KangarooKoala
Copy link
Contributor

If a Subsystem.idle() trigger factory is added, then the logic could be implemented as condition.and(mySubsystem.idle()).whileTrue(command.repeatedly()), which reads well and is explicit about how it's implemented, so user's can better understand any potential weirdness. Up to other people whether that sort of thing would be worth adding syntax sugar for.

@oh-yes-0-fps
Copy link
Contributor Author

oh-yes-0-fps commented Sep 19, 2024

Unfortunately condition.and(mySubsystem.idle()).whileTrue(command.repeatedly()) does not have the same functionality.
command.repeatedly() acts like a sequential command with an infinite length of the same element.
Once it is canceled it won't reschedule itself and whileTrue does not reschedule an ended command either.
Another thing is whileTrueLowPriority will not interrupt other commands besides the default command when scheduled.
nvm the idle can fix allat

@oh-yes-0-fps
Copy link
Contributor Author

oh-yes-0-fps commented Sep 20, 2024

So the subsystem.idle() has one slight problem, if it's used to schedule a command it thus makes itself false.

  /**
   * Returns a {@link Trigger} that is true when this subsystem is
   * currently being required by no commands or being required by the default command.
   * 
   * This can be used to create a control flow similar to a temporary default command
   * <pre><code>
   * {
   *  DriveSubsystem drive = ...;
   *  Command myCommand = ...;
   *  Trigger otherCondition = ...;
   *  
   *  otherCondition.and(drive.idle(loop)).whileTrue(myCommand.repeatedly());
   *}</code></pre><p>
   * 
   * @param loop the event loop to poll this trigger on
   * @return a Trigger that is true when this subsystem is not being required by any commands other than the default command
   */
  default Trigger idle(EventLoop loop) {
    return new Trigger(loop, () -> {
      Command currentCommand = getCurrentCommand();
      return currentCommand == null || currentCommand == getDefaultCommand();
    });
  }

This was my implementation

A solution to this is to do otherCondition.and(drive.idle(loop)).onTrue(myCommand.repeatedly().onlyWhile(otherCondition)); but that is incredibly verbose so we end up back into the syntactic sugar realm

@oh-yes-0-fps
Copy link
Contributor Author

oh-yes-0-fps commented Sep 22, 2024

Maybe adding a

//inside Subsystem.java
default void defaultCommandWhile(BooleanSupplier condition, Command cmd) {
    Trigger condTrigger = new Trigger(condTrigger );
    condTrigger.and(this.idle()).onTrue(cmd.repeatedly().onlyWhile(condTrigger).withName(cmd.getName()));
}

would be a sufficient abstraction

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants