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();