Skip to content

Commit 00d91cc

Browse files
committed
Completely rewrite sequence and fallback execution with concurrency logic.
1 parent 9c0d00e commit 00d91cc

File tree

2 files changed

+129
-158
lines changed

2 files changed

+129
-158
lines changed

ihmc-high-level-behaviors/src/main/java/us/ihmc/behaviors/behaviorTree/BehaviorTreeRootNodeExecutor.java

Lines changed: 77 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
import us.ihmc.behaviors.tools.walkingController.ControllerStatusTracker;
1111
import us.ihmc.communication.crdt.CRDTInfo;
1212
import us.ihmc.handsros2.abilityHand.AbilityHandROS2HardwareCommunication;
13-
import us.ihmc.log.LogTools;
1413
import us.ihmc.tools.io.WorkspaceResourceDirectory;
1514

1615
import java.util.ArrayList;
@@ -25,7 +24,6 @@ public class BehaviorTreeRootNodeExecutor extends BehaviorTreeNodeExecutor<Behav
2524
private final List<LeafNodeExecutor<?, ?>> currentlyExecutingLeaves = new ArrayList<>();
2625
private final List<LeafNodeExecutor<?, ?>> failedLeaves = new ArrayList<>();
2726
private final List<LeafNodeExecutor<?, ?>> successfulLeaves = new ArrayList<>();
28-
private final List<LeafNodeExecutor<?, ?>> failedLeavesWithoutFallback = new ArrayList<>();
2927

3028
public BehaviorTreeRootNodeExecutor(long id,
3129
CRDTInfo crdtInfo,
@@ -66,7 +64,10 @@ private void updateNodeListsRecursive(BehaviorTreeNodeExecutor<?, ?> node)
6664
orderedActions.add(actionNode);
6765
}
6866
else if (child instanceof FallbackNodeExecutor fallbackNode)
67+
{
68+
fallbackNode.update();
6969
fallbackNodes.add(fallbackNode);
70+
}
7071

7172
updateNodeListsRecursive(child);
7273
}
@@ -101,189 +102,133 @@ public void update()
101102
}
102103
}
103104

104-
// Update is next for execution
105+
// Determine the concurrent group
106+
int next = state.getExecutionNextIndex();
105107
for (int i = 0; i < state.getOrderedLeaves().size(); i++)
106108
{
107-
int next = state.getExecutionNextIndex();
108-
int after = state.getOrderedLeaves().get(i).getExecuteAfterLeafIndex();
109-
for (int j = after + 1; j < i; j++) // Might have to wait on nearer leaves
110-
after = Math.max(after, state.getOrderedLeaves().get(j).getExecuteAfterLeafIndex());
111-
state.getOrderedLeaves().get(i).setIsNextForExecution(i >= next && after < next);
109+
LeafNodeExecutor<?, ?> leaf = orderedLeaves.get(i);
110+
int after = effectiveExecuteAfterLeafIndex(leaf);
111+
leaf.getState().setIsNextForExecution(i >= next && after < next);
112112
}
113113

114-
failedLeaves.clear();
115-
successfulLeaves.clear();
116114
for (LeafNodeExecutor<?, ?> currentlyExecutingLeaf : currentlyExecutingLeaves)
117115
{
118116
currentlyExecutingLeaf.updateCurrentlyExecuting();
119117

120-
// This leaf has completed on this update
121118
if (!currentlyExecutingLeaf.getState().getIsExecuting())
122-
{
123-
boolean success = !currentlyExecutingLeaf.getState().getFailed();
124-
if (success)
125-
successfulLeaves.add(currentlyExecutingLeaf);
126-
else
127-
failedLeaves.add(currentlyExecutingLeaf);
128-
129-
reportLeafCeasedExecution(currentlyExecutingLeaf, success);
130-
}
119+
leafCeasedExecution(currentlyExecutingLeaf);
131120
}
132121
currentlyExecutingLeaves.removeAll(failedLeaves);
133122
currentlyExecutingLeaves.removeAll(successfulLeaves);
134123

135-
failedLeavesWithoutFallback.clear();
136-
failedLeavesWithoutFallback.addAll(failedLeaves);
137-
for (FallbackNodeExecutor fallbackNode : fallbackNodes)
138-
{
139-
fallbackNode.update();
140-
141-
if (!fallbackNode.getFallbackLeaves().isEmpty())
142-
failedLeavesWithoutFallback.removeAll(fallbackNode.getTryLeaves());
143-
}
144-
145-
// Handle skipping the fallback if try leaf group is successful
146-
boolean leafEnded = !failedLeaves.isEmpty() || !successfulLeaves.isEmpty();
147-
if (leafEnded && currentlyExecutingLeaves.isEmpty() && !isEndOfSequence())
148-
{
149-
LeafNodeExecutor<?, ?> nextNodeToExecute = orderedLeaves.get(state.getExecutionNextIndex());
150-
151-
for (FallbackNodeExecutor fallbackNode : fallbackNodes)
152-
{
153-
if (fallbackNode.getFallbackLeaves().indexOf(nextNodeToExecute) == 0)
154-
{
155-
boolean anyFailed = false;
156-
for (LeafNodeExecutor<?, ?> tryLeaf : fallbackNode.getTryLeaves())
157-
{
158-
if (tryLeaf.getState().getFailed())
159-
{
160-
anyFailed = true;
161-
break;
162-
}
163-
}
164-
165-
if (anyFailed) // Nothing is executing and a tried leaf failed -- falling back
166-
{
167-
LogTools.warn("Leaves failed, fallback leaves are next for execution.");
168-
}
169-
else // Nothing is executing and none of the try leaves failed -- skip fallback
170-
{
171-
LeafNodeExecutor<?, ?> lastFallbackLeaf = fallbackNode.getFallbackLeaves().get(fallbackNode.getFallbackLeaves().size() - 1);
172-
LogTools.info("Leaves successful, skipping fallback leaves(s)");
173-
state.setExecutionNextIndex(lastFallbackLeaf.getState().getLeafIndex() + 1);
174-
}
175-
}
176-
}
177-
}
178-
179-
if (state.getAutomaticExecution())
124+
boolean keepTrying = state.getAutomaticExecution() || state.pollManualExecutionRequested();
125+
executionLoop:
126+
while (keepTrying)
180127
{
181128
if (isEndOfSequence())
182129
{
183130
state.getLogger().info("End of sequence.");
184131
state.setAutomaticExecution(false);
132+
break;
185133
}
186-
else if (!failedLeavesWithoutFallback.isEmpty())
134+
135+
LeafNodeExecutor<?, ?> leafToExecute = orderedLeaves.get(state.getExecutionNextIndex());
136+
137+
if (!state.getAutomaticExecution() && !leafToExecute.getState().getIsNextForExecution())
138+
break;
139+
140+
for (FallbackNodeExecutor fallbackNode : fallbackNodes)
141+
if (fallbackNode.tryLeafIsBlocking(leafToExecute))
142+
break executionLoop;
143+
// Break if anything earlier than effective after execute is still going
144+
for (int i = effectiveExecuteAfterLeafIndex(leafToExecute); i >= 0; i--)
145+
if (state.getOrderedLeaves().get(i).getIsExecuting())
146+
break executionLoop;
147+
148+
leafToExecute.update(); // Make sure can execute is up to date
149+
if (leafToExecute.getState().getCanExecute())
187150
{
188-
state.getLogger().error("A leaf failed. Disabling automatic execution.\n Failed: %s".formatted(failedLeavesWithoutFallback));
189-
state.setAutomaticExecution(false);
151+
state.getLogger().info("%s executing leaf: %s (%s)".formatted(state.getAutomaticExecution() ? "Automatically" : "Manually",
152+
leafToExecute.getDefinition().getName(),
153+
leafToExecute.getClass().getSimpleName()));
154+
leafToExecute.triggerExecution();
155+
if (!leafToExecute.getState().getIsExecuting()) // Handle immediately ceased execution
156+
keepTrying = leafCeasedExecution(leafToExecute);
157+
else
158+
currentlyExecutingLeaves.add(leafToExecute);
159+
state.stepForwardNextExecutionIndex();
190160
}
191161
else
192162
{
193-
while (wouldExecuteNextLeaf() && tryExecuteNextLeaf("Automatically"));
163+
state.getLogger().error("Cannot execute leaf: %s: %s\n%s".formatted(leafToExecute.getClass().getSimpleName(),
164+
leafToExecute.getDefinition().getName(),
165+
leafToExecute.getCantExecuteMessage()));
166+
keepTrying = false;
194167
}
195168
}
196-
else if (state.pollManualExecutionRequested())
197-
{
198-
while (wouldExecuteNextLeaf() && tryExecuteNextLeaf("Manually"));
199-
}
200169

201170
if (state.pollFailureResetRequested())
202-
{
203-
failedLeaves.clear();
204171
for (int i = 0; i < state.getOrderedLeaves().size(); i++)
205172
{
206173
state.getOrderedLeaves().get(i).setFailed(false);
207174
state.getOrderedLeaves().get(i).setIsExecuting(false);
208175
}
209-
}
210176
}
211177

212-
private boolean tryExecuteNextLeaf(String adjective)
178+
private boolean leafCeasedExecution(LeafNodeExecutor<?, ?> leaf)
213179
{
214180
boolean keepGoing = true;
215-
LeafNodeExecutor<?, ?> leafToExecute = orderedLeaves.get(state.getExecutionNextIndex());
216-
leafToExecute.update(); // Make sure can execute is up to date
217-
if (leafToExecute.getState().getCanExecute())
181+
String name = leaf.getDefinition().getName();
182+
boolean failed = leaf.getState().getFailed();
183+
if (leaf.getState() instanceof ActionNodeState<?> actionNodeState)
218184
{
219-
state.getLogger().info("%s executing leaf: %s (%s)".formatted(adjective,
220-
leafToExecute.getDefinition().getName(),
221-
leafToExecute.getClass().getSimpleName()));
222-
leafToExecute.triggerExecution();
223-
if (leafToExecute.getState().getFailed()) // Handle immediate failure
224-
{
225-
keepGoing = false;
226-
reportLeafCeasedExecution(leafToExecute, false);
227-
}
228-
else
229-
currentlyExecutingLeaves.add(leafToExecute);
230-
state.stepForwardNextExecutionIndex();
185+
String resultMessage = failed ? "failed after" : "completed successfully in";
186+
double elapsedExecutionTime = actionNodeState.getElapsedExecutionTime();
187+
state.getLogger().log(failed ? Level.ERROR : Level.INFO, "Action %s %.2f s: %s".formatted(resultMessage, elapsedExecutionTime, name));
231188
}
232189
else
233190
{
234-
state.getLogger().error("Cannot execute leaf: %s: %s\n%s".formatted(leafToExecute.getClass().getSimpleName(),
235-
leafToExecute.getDefinition().getName(),
236-
leafToExecute.getCantExecuteMessage()));
237-
keepGoing = false;
191+
String resultMessage = failed ? "failed" : "completed successfully";
192+
state.getLogger().log(failed ? Level.ERROR : Level.INFO, "Leaf %s: %s".formatted(resultMessage, name));
238193
}
239194

240-
if (!keepGoing)
195+
boolean isTryLeaf = false;
196+
for (FallbackNodeExecutor fallbackNode : fallbackNodes)
197+
if (fallbackNode.getChildrenLeaves().contains(leaf))
198+
isTryLeaf |= fallbackNode.leafCeasedExecution(leaf);
199+
200+
if (!isTryLeaf && failed)
201+
{
202+
state.getLogger().error("A leaf failed. Disabling automatic execution.\n Failed: %s".formatted(leaf));
241203
state.setAutomaticExecution(false);
204+
keepGoing = false;
205+
}
206+
207+
if (failed)
208+
failedLeaves.add(leaf);
209+
else
210+
successfulLeaves.add(leaf);
242211

243212
return keepGoing;
244213
}
245214

246-
private boolean wouldExecuteNextLeaf()
215+
private int effectiveExecuteAfterLeafIndex(LeafNodeExecutor<?, ?> leaf)
247216
{
248-
if (isEndOfSequence())
249-
return false;
217+
int i = leaf.getState().getLeafIndex();
250218

251-
LeafNodeExecutor<?, ?> nextNodeToExecute = orderedLeaves.get(state.getExecutionNextIndex());
219+
if (!state.getConcurrencyEnabled())
220+
return i - 1;
252221

253-
// If a fallback catch is up next, block if any corresponding try leaves are executing
254-
for (FallbackNodeExecutor fallbackNode : fallbackNodes)
255-
if (fallbackNode.getFallbackLeaves().indexOf(nextNodeToExecute) == 0) // The first fallback catch leaf is next
256-
for (LeafNodeExecutor<?, ?> tryLeaf : fallbackNode.getTryLeaves())
257-
if (tryLeaf.getState().getIsExecuting())
258-
return false;
222+
int after = leaf.getState().getExecuteAfterLeafIndex();
259223

260-
if (state.getConcurrencyEnabled())
261-
{
262-
int executeAfterLeafIndex = nextNodeToExecute.getState().getExecuteAfterLeafIndex();
224+
for (int j = after + 1; j < i; j++) // Might have to wait on nearer leaves
225+
after = Math.max(after, state.getOrderedLeaves().get(j).getExecuteAfterLeafIndex());
263226

264-
if (executeAfterLeafIndex < 0) // Execute after beginning
265-
return true;
266-
else
267-
return !orderedLeaves.get(executeAfterLeafIndex).getState().getIsExecuting();
268-
}
269-
else
270-
return currentlyExecutingLeaves.isEmpty();
271-
}
227+
for (FallbackNodeExecutor fallbackNode : fallbackNodes) // catch group can't execute with anything above catch
228+
if (fallbackNode.getCatchLeaves().contains(leaf))
229+
after = Math.max(after, fallbackNode.getCatchLeaves().get(0).getState().getLeafIndex() - 1);
272230

273-
private void reportLeafCeasedExecution(LeafNodeExecutor<?, ?> currentlyExecutingLeaf, boolean success)
274-
{
275-
String name = currentlyExecutingLeaf.getDefinition().getName();
276-
if (currentlyExecutingLeaf.getState() instanceof ActionNodeState<?> actionNodeState)
277-
{
278-
String resultMessage = success ? "completed successfully in" : "failed after";
279-
double elapsedExecutionTime = actionNodeState.getElapsedExecutionTime();
280-
state.getLogger().log(success ? Level.INFO : Level.ERROR, "Action %s %.2f s: %s".formatted(resultMessage, elapsedExecutionTime, name));
281-
}
282-
else
283-
{
284-
String resultMessage = success ? "completed successfully" : "failed";
285-
state.getLogger().log(success ? Level.INFO : Level.ERROR, "Leaf %s: %s".formatted(resultMessage, name));
286-
}
231+
return after;
287232
}
288233

289234
public boolean isEndOfSequence()

0 commit comments

Comments
 (0)