From 278e9b972ad7a4f2e9f16b021fc713a16c7abb44 Mon Sep 17 00:00:00 2001 From: Randall Hauch Date: Tue, 20 Oct 2015 17:57:56 -0500 Subject: [PATCH] Overload method to submit commands with functions or lambdas Adds more overloaded `Strongback.submi(...)` methods that take functions, lambdas or other parameters. These are merely convenience methods to simplify running very simple commands. --- .../simple/SimpleAutoTankDriveRobot.java | 11 +-- strongback/src/org/strongback/Strongback.java | 50 ++++++++++ .../src/org/strongback/command/Command.java | 97 +++++++++++++++++-- 3 files changed, 142 insertions(+), 16 deletions(-) diff --git a/strongback-examples/src/org/strongback/example/simple/SimpleAutoTankDriveRobot.java b/strongback-examples/src/org/strongback/example/simple/SimpleAutoTankDriveRobot.java index dbbc57f..cb7405b 100644 --- a/strongback-examples/src/org/strongback/example/simple/SimpleAutoTankDriveRobot.java +++ b/strongback-examples/src/org/strongback/example/simple/SimpleAutoTankDriveRobot.java @@ -17,7 +17,6 @@ package org.strongback.example.simple; import org.strongback.Strongback; -import org.strongback.command.Command; import org.strongback.components.Motor; import org.strongback.components.ui.ContinuousRange; import org.strongback.components.ui.FlightStick; @@ -41,7 +40,6 @@ public class SimpleAutoTankDriveRobot extends IterativeRobot { private static final int RF_MOTOR_PORT = 3; private static final int RR_MOTOR_PORT = 4; - private Command autonomousCommand; private TankDrive drive; private ContinuousRange driveSpeed; private ContinuousRange turnSpeed; @@ -64,11 +62,6 @@ public void robotInit() { driveSpeed = joystick.getPitch().scale(sensitivity::read); // scaled turnSpeed = joystick.getRoll().scale(sensitivity::read).invert(); // scaled and inverted - // Set up the command we'll use during autonomous. We'd probably use a custom CommandGroup that did something more - // complicated, but for this very simple example we'll just drive forward at 50% speed for 5 seconds, - // so we can use a simple lambda ... - autonomousCommand = Command.create(5.0, () -> drive.tank(0.5, 0.5)); - // Set up the data recorder to capture the left & right motor speeds (since both motors on the same side should // be at the same speed, we can just use the composed motors for each) and the sensitivity. We have to do this // before we start Strongback... @@ -83,7 +76,7 @@ public void robotInit() { public void autonomousInit() { // Start Strongback functions ... Strongback.start(); - Strongback.submit(autonomousCommand); + Strongback.submit(()->drive.tank(0.5, 0.5), 5.0, drive::stop); } @Override @@ -99,8 +92,10 @@ public void teleopPeriodic() { @Override public void disabledInit() { + drive.stop(); // Tell Strongback that the robot is disabled so it can flush and kill commands. Strongback.disable(); } } + diff --git a/strongback/src/org/strongback/Strongback.java b/strongback/src/org/strongback/Strongback.java index 29cd9dd..288deae 100644 --- a/strongback/src/org/strongback/Strongback.java +++ b/strongback/src/org/strongback/Strongback.java @@ -21,6 +21,7 @@ import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.LockSupport; +import java.util.function.BooleanSupplier; import java.util.function.Function; import java.util.function.LongConsumer; import java.util.function.Supplier; @@ -484,6 +485,55 @@ public static void submit(Command command) { if (command != null) INSTANCE.scheduler.submit(command); } + /** + * Submit to Strongback's internal scheduler a {@link Command} that runs the supplied function one time and completes + * immediately. + * + * @param executeFunction the function to be called during execution; may not be null + */ + public static void submit(Runnable executeFunction) { + submit(Command.create(executeFunction)); + } + + /** + * Submit to Strongback's internal scheduler a {@link Command} that runs the supplied function one time, waits the + * prescribed amount of time, and then calls the second function. + * + * @param first the first function to be called; may not be null + * @param delayInSeconds the delay in seconds after the first function completes; must be positive + * @param second the second function to be called after the delay; may be null if not needed + */ + public static void submit(Runnable first, double delayInSeconds, Runnable second) { + submit(Command.create(delayInSeconds, first, second)); + } + + /** + * Submit to Strongback's internal scheduler a {@link Command} that runs the supplied function one or more times until it + * returns false or until the prescribed maximum time has passed, whichever comes first. + * + * @param function the function to be called at least one time and that should return true if it is to be + * called again; may not be null + * @param maxDurationInSeconds the maximum amount of time that the first function should be repeatedly called; must be + * positive + */ + public static void submit(BooleanSupplier function, double maxDurationInSeconds) { + submit(Command.create(maxDurationInSeconds, function)); + } + + /** + * Submit to Strongback's internal scheduler a {@link Command} that runs the supplied function one or more times until it + * returns false or until the prescribed maximum time has passed, and then calls the second function. + * + * @param first the first function to be called at least one time and that should return true if it is to be + * called again; may not be null + * @param maxDurationInSeconds the maximum amount of time that the first function should be repeatedly called; must be + * positive + * @param second the second function to be called after the delay; may be null if not needed + */ + public static void submit(BooleanSupplier first, double maxDurationInSeconds, Runnable second) { + submit(Command.create(maxDurationInSeconds, first, second)); + } + /** * Get Strongback's {@link SwitchReactor} that can be used to call functions when {@link Switch switches} change state or * while they remain in a specific state. The switch reactor is registered with the {@link #executor() executor}, so it diff --git a/strongback/src/org/strongback/command/Command.java b/strongback/src/org/strongback/command/Command.java index 7720105..94c22c5 100644 --- a/strongback/src/org/strongback/command/Command.java +++ b/strongback/src/org/strongback/command/Command.java @@ -207,10 +207,38 @@ public static Command create(Runnable executeFunction) { * @return the new command; never null */ public static Command create(double durationInSeconds, Runnable executeFunction) { - return create(durationInSeconds, () -> { - executeFunction.run(); - return false; - } , () -> "Command (one-time, duration=" + durationInSeconds + " sec) " + executeFunction); + return create(durationInSeconds, executeFunction, null); + } + + /** + * Create a new command that runs once by executing the supplied function, then wait the prescribed amount of time, and then + * call the supplied {@code endFunction}. The resulting command will have no {@link Requirable}s. + * + * @param durationInSeconds the maximum duration in seconds that the command should execute; must be positive + * @param executeFunction the function to be called during execution; may not be null + * @param endFunction the function to be called when the command terminates; may be null + * @return the new command; never null + */ + public static Command create(double durationInSeconds, Runnable executeFunction, Runnable endFunction) { + return new Command(durationInSeconds) { + boolean completed = false; + @Override + public boolean execute() { + if ( !completed ) { + executeFunction.run(); + completed = true; + } + return true; + } + @Override + public void end() { + if ( endFunction != null ) endFunction.run(); + } + @Override + public String toString() { + return "one-time, duration=" + durationInSeconds + " sec) " + executeFunction; + } + }; } /** @@ -221,7 +249,19 @@ public static Command create(double durationInSeconds, Runnable executeFunction) * @return the new command; never null */ public static Command create(BooleanSupplier executeFunction) { - return create(0.0, executeFunction, () -> "Command " + executeFunction); + return create(0.0, executeFunction, null, () -> "Command " + executeFunction); + } + + /** + * Create a new command that runs one or more times by executing the supplied function. When the command terminates, the + * {@code endFunction} will be called. The resulting command will have no {@link Requirable}s. + * + * @param executeFunction the function to be called during execution; may not be null + * @param endFunction the function to be called when the command terminates; may be null + * @return the new command; never null + */ + public static Command create(BooleanSupplier executeFunction, Runnable endFunction) { + return create(0.0, executeFunction, endFunction, () -> "Command " + executeFunction); } /** @@ -240,22 +280,63 @@ public static Command create(double timeoutInSeconds, BooleanSupplier executeFun /** * Create a new command that runs one or more times by executing the supplied function, but that will timeout if taking - * longer than the specified timeout. The resulting command will have no {@link Requirable}s. + * longer than the specified timeout. When the command terminates, the {@code endFunction} will be called. The resulting + * command will have no {@link Requirable}s. * * @param timeoutInSeconds the time in seconds * @param executeFunction the function to be called during execution; may not be null + * @param endFunction the function to be called when the command terminates; may be null + * @return the new command; never null + */ + public static Command create(double timeoutInSeconds, BooleanSupplier executeFunction, Runnable endFunction) { + return create(timeoutInSeconds, + executeFunction, + endFunction, + () -> "Command (timeout=" + timeoutInSeconds + " sec,repeatable) " + executeFunction); + } + + /** + * Create a new command that runs one or more times by executing the supplied function, but that will timeout if taking + * longer than the specified timeout. When the command terminates, the {@code endFunction} will be called. The resulting + * command will have no {@link Requirable}s. + * + * @param timeoutInSeconds the time in seconds + * @param executeFunction the function to be called during execution; may not be null + * @param endFunction the function to be called when the command terminates; may be null + * @param toString the function to be called when {@link Command#toString()} is called; may not be null + * @param requirements the {@link Requirable}s for the command + * @return the new command; never null + */ + protected static Command create(double timeoutInSeconds, BooleanSupplier executeFunction, + Supplier toString, Requirable... requirements) { + return create(timeoutInSeconds, executeFunction, null, toString, requirements); + } + + /** + * Create a new command that runs one or more times by executing the supplied function, but that will timeout if taking + * longer than the specified timeout. When the command terminates, the {@code endFunction} will be called. The resulting + * command will have no {@link Requirable}s. + * + * @param timeoutInSeconds the time in seconds + * @param executeFunction the function to be called during execution; may not be null + * @param endFunction the function to be called when the command terminates; may be null * @param toString the function to be called when {@link Command#toString()} is called; may not be null * @param requirements the {@link Requirable}s for the command * @return the new command; never null */ - protected static Command create(double timeoutInSeconds, BooleanSupplier executeFunction, Supplier toString, - Requirable... requirements) { + protected static Command create(double timeoutInSeconds, BooleanSupplier executeFunction, Runnable endFunction, + Supplier toString, Requirable... requirements) { return new Command(timeoutInSeconds, requirements) { @Override public boolean execute() { return executeFunction.getAsBoolean(); } + @Override + public void end() { + if (endFunction != null) endFunction.run(); + } + @Override public String toString() { return toString.get();