View Single Post
  #5   Spotlight this post!  
Unread 15-11-2015, 16:20
notmattlythgoe's Avatar
notmattlythgoe notmattlythgoe is offline
Flywheel Police
AKA: Matthew Lythgoe
FRC #2363 (Triple Helix)
Team Role: Mentor
 
Join Date: Feb 2010
Rookie Year: 2009
Location: Newport News, VA
Posts: 1,715
notmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond reputenotmattlythgoe has a reputation beyond repute
Re: Java Conditional Command

Quote:
Originally Posted by randallh View Post
Be sure that the command that precedes the conditional command is a one-shot command that executes once and returns immediately. Otherwise, it will never complete and your conditional command will never get a chance to run.

If you're using Java 8, you could simplify most of your code by using lambdas rather than hard-code the comparison operations. For example, your ConditionalWaitCommand can use the `java.util.function.Predicate` functional interface to define when it is completed:

Code:
public class ConditionalWaitCommand extends Command {
	
	public static ConditionalWaitCommand waitUntil( Predicate isComplete ) {
	}

	private final Predicate isComplete;

	private ConditionalWaitCommand(Predicate isComplete) {
		this.isComplete = isComplete;
	}

	@Override
	protected void initialize() { }

	@Override
	protected void execute() {
		return isComplete().test();
	}

	protected boolean isFinished() { }

	@Override
	protected void end() { }

	@Override
	protected void interrupted() { }
}
This makes this class very simple but even more flexible than your version. Your DriveToLine command group becomes:

Code:
public class DriveToLine extends CommandGroup {
    
    public  DriveToLine() {
    	addSequential(new DriveCommand(0.5));
        addSequential(waitUntil(drivetrain.getDistance() > 50));
        addSequential(new DriveCommand(0));
    }
}
But sometimes it is much easier just to create a generic and reusable command that is easily instantiated with custom lambdas using static methods. One example might be a very simple but generic Command subclass that takes an optional lambda to run once, a predicate to know when it is complete, and an optional function to run when complete. The static factory methods make this really easy to reuse without having to create a concrete subclass.

Here's what that might look like to create a command instance that drives at 50% power while the distance is greater than 50, and once that condition has occurred then stop:

Code:
    Drivetrain driveTrain = ...
    Command myCommand = ConditionalCommand.runUntil(driveTrain.drive(0.5),
                                                    drivetrain.getDistance() > 50.0,
                                                    driveTrain.stop());
where

Code:
public class ConditionalCommand extends Command {
	
	public static ConditionalCommand waitUntil( Runnable initial, Predicate isComplete, Runnable uponComplete ) {
		return new ConditionalCommand(initial, isComplete, uponComplete);
	}

	public static ConditionalCommand waitUntil( Predicate isComplete, Runnable uponComplete ) {
		return new ConditionalCommand(initial, isComplete, uponComplete);
	}

	private final Runnable initial;
	private final Predicate isComplete;
	private final Runnable uponComplete;
	private boolean completed;

	private ConditionalCommand(Predicate isComplete) {
		this.isComplete = isComplete;
	}

	@Override
	protected void initialize() {
		if ( initial != null ) initial.run();
	}

	@Override
	protected void execute() {
		if ( isComplete().test() ) {
			if ( uponComplete != null ) uponComplete.run();
			completed = true;
		}
	}

	protected boolean isFinished() {
		return completed;
	}

	@Override
	protected void end() { }

	@Override
	protected void interrupted() { }
}
This is not the only pattern, either.
Yeah, I should have set the first command as parallel not sequential. I like the lambda ideas. Going to have to play around with them.

GeeTwo, it works because getDistance() returns an object not a value. So the getValue() call on the object at the time of the comparison will get the current value.

Last edited by notmattlythgoe : 15-11-2015 at 16:26.
Reply With Quote