Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 @@ -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;
Expand All @@ -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;
Expand All @@ -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...
Expand All @@ -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
Expand All @@ -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();
}

}

50 changes: 50 additions & 0 deletions strongback/src/org/strongback/Strongback.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 <code>false</code> 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 <code>true</code> 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 <code>false</code> 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 <code>true</code> 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
Expand Down
97 changes: 89 additions & 8 deletions strongback/src/org/strongback/command/Command.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
};
}

/**
Expand All @@ -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);
}

/**
Expand All @@ -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<String> 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<String> toString,
Requirable... requirements) {
protected static Command create(double timeoutInSeconds, BooleanSupplier executeFunction, Runnable endFunction,
Supplier<String> 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();
Expand Down