Skip to content

Commit 02ab309

Browse files
author
James Hagborg
committed
Add while loops to CommandBuilder
This has not yet been tested. Tests will come at some point, but may be delayed by homework.
1 parent b3110c8 commit 02ab309

File tree

2 files changed

+170
-0
lines changed

2 files changed

+170
-0
lines changed
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
package edu.wpi.first.wpilibj.command;
2+
3+
import java.util.Collections;
4+
import java.util.function.BooleanSupplier;
5+
6+
import edu.wpi.first.wpilibj.command.Command;
7+
8+
/**
9+
* A command which loops repeatedly while checking a condition.
10+
*
11+
* Given a command as a body and a condition to check, this command will
12+
* repeatedly check the condition, and execute the command if it is met.
13+
*
14+
* The requirements of the body command are removed, and transferred over
15+
* to this command at construction. This allows CommandGroups and cancelling
16+
* to work properly.
17+
*
18+
* If this command is interrupted (cancelled or booted by another command),
19+
* then the body command will be cancelled. If the body command is cancelled,
20+
* then it will start again on the next iteration.
21+
*
22+
* Because HyperLib is currently in pre-release, these semantics may change
23+
* based on what we consider "reasonable defaults".
24+
*
25+
* @author James Hagborg
26+
*
27+
*/
28+
public class WhileCommand extends Command {
29+
30+
private final BooleanSupplier m_condition;
31+
private final Command m_body;
32+
33+
private boolean m_hasFinished;
34+
35+
/**
36+
* Construct a new WhileCommand from a condition and a body.
37+
*
38+
* After construction, the body command's requirements are moved to this
39+
* command. That means you should not re-use the body command after
40+
* passing it to this method. Instead, create a new one.
41+
*
42+
* @param condition The condition to check
43+
* @param body The command to run as the body
44+
*/
45+
public WhileCommand(BooleanSupplier condition, Command body) {
46+
if (body == null) {
47+
throw new NullPointerException("body == null");
48+
}
49+
if (condition == null) {
50+
throw new NullPointerException("condition == null");
51+
}
52+
53+
m_condition = condition;
54+
m_body = body;
55+
transferRequirements();
56+
}
57+
58+
/**
59+
* Construct a new WhileCommand from a condition and a body, with the
60+
* given name.
61+
*
62+
* After construction, the body command's requirements are moved to this
63+
* command. That means you should not re-use the body command after
64+
* passing it to this method. Instead, create a new one.
65+
*
66+
* @param name The name of the command
67+
* @param condition The condition to check
68+
* @param body The command to run as the body
69+
*/
70+
public WhileCommand(String name, BooleanSupplier condition, Command body) {
71+
super(name);
72+
if (body == null) {
73+
throw new NullPointerException("body == null");
74+
}
75+
if (condition == null) {
76+
throw new NullPointerException("condition == null");
77+
}
78+
79+
m_condition = condition;
80+
m_body = body;
81+
transferRequirements();
82+
}
83+
84+
@SuppressWarnings("unchecked")
85+
private void transferRequirements() {
86+
for (Object req : Collections.list(m_body.getRequirements())) {
87+
requires((Subsystem) req);
88+
}
89+
90+
m_body.clearRequirements();
91+
}
92+
93+
private void checkCondition() {
94+
m_hasFinished = !m_condition.getAsBoolean();
95+
}
96+
97+
/**
98+
* Reset the state as if this command has not yet been run.
99+
*
100+
* We don't call checkCondition() here, because the condition is already
101+
* checked in execute(), and it makes sense semantically to check the
102+
* condition once per iteration. This might matter if the condition
103+
* passed in has side effects.
104+
*/
105+
@Override
106+
protected void initialize() {
107+
m_hasFinished = false;
108+
}
109+
110+
/**
111+
* If the loop has already ended, do nothing. Otherwise, check if the
112+
* body command is running. If it is not, check the condition and start
113+
* it if necessary.
114+
*/
115+
@Override
116+
protected void execute() {
117+
if (m_hasFinished) {
118+
return;
119+
}
120+
121+
if (!m_body.isRunning()) {
122+
checkCondition();
123+
if (!m_hasFinished) {
124+
m_body.start();
125+
}
126+
}
127+
}
128+
129+
/**
130+
* Cancel the body command.
131+
*/
132+
@Override
133+
protected void interrupted() {
134+
m_body.cancel();
135+
}
136+
137+
/**
138+
* Check if the loop has finished. This is only true if the body command
139+
* has ended and the condition returned false on most recent check.
140+
*/
141+
@Override
142+
protected boolean isFinished() {
143+
return m_hasFinished;
144+
}
145+
146+
}

src/main/java/org/usfirst/frc/team69/util/CommandBuilder.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import edu.wpi.first.wpilibj.command.WaitCommand;
1313
import edu.wpi.first.wpilibj.command.WaitForChildren;
1414
import edu.wpi.first.wpilibj.command.WaitUntilCommand;
15+
import edu.wpi.first.wpilibj.command.WhileCommand;
1516

1617
/**
1718
* A builder class to create commands in autonomous. Currently, it wraps a
@@ -168,6 +169,29 @@ public CommandBuilder ifThenElse(BooleanSupplier condition, Command ifTrue, Comm
168169
});
169170
return this;
170171
}
172+
173+
/**
174+
* Loop repeatedly while checking a condition.
175+
*
176+
* Given a command as a body and a condition to check, this command will
177+
* repeatedly check the condition, and execute the command if it is met.
178+
*
179+
* The requirements of the body command are removed when this method is
180+
* called. This means you cannot re-use the command object you pass to
181+
* this method. This allows CommandGroups and cancelling to work properly.
182+
*
183+
* Because HyperLib is currently in pre-release, these semantics may change
184+
* based on what we consider "reasonable defaults".
185+
*
186+
* @param condition The condition to check
187+
* @param body The command to run as the loop body
188+
* @return This CommandBuilder object
189+
* @see WhileCommand
190+
*/
191+
public CommandBuilder whileLoop(BooleanSupplier condition, Command body) {
192+
m_cmdGroup.addSequential(new WhileCommand(condition, body));
193+
return this;
194+
}
171195

172196
/**
173197
* End any command currently running on the subsystem. This is accomplished

0 commit comments

Comments
 (0)