Skip to content

Commit e72efbf

Browse files
committed
coments about precondition added. fix #917
1 parent 2c71b41 commit e72efbf

4 files changed

Lines changed: 168 additions & 24 deletions

File tree

.github/workflows/cmake_ubuntu.yml

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -99,18 +99,18 @@ jobs:
9999
--ignore-errors unused
100100
lcov --list coverage.info
101101
102-
- name: Upload coverage reports to Codecov
103-
uses: codecov/codecov-action@v5
104-
continue-on-error: true
105-
with:
106-
files: coverage.info
107-
flags: unittests
108-
disable_search: true
109-
disable_file_fixes: false
110-
plugins: noop
111-
network_filter: >-
112-
include/behaviortree_cpp/,src/
113-
token: ${{ secrets.CODECOV_TOKEN }}
102+
# - name: Upload coverage reports to Codecov
103+
# uses: codecov/codecov-action@v5
104+
# continue-on-error: true
105+
# with:
106+
# files: coverage.info
107+
# flags: unittests
108+
# disable_search: true
109+
# disable_file_fixes: false
110+
# plugins: noop
111+
# network_filter: >-
112+
# include/behaviortree_cpp/,src/
113+
# token: ${{ secrets.CODECOV_TOKEN }}
114114

115115
# --- Coveralls ---
116116
- name: Upload to Coveralls
@@ -121,14 +121,6 @@ jobs:
121121
format: lcov
122122
github-token: ${{ secrets.GITHUB_TOKEN }}
123123

124-
# --- Codacy ---
125-
- name: Upload to Codacy
126-
uses: codacy/codacy-coverage-reporter-action@v1
127-
continue-on-error: true
128-
with:
129-
project-token: ${{ secrets.CODACY_PROJECT_TOKEN }}
130-
coverage-reports: coverage.info
131-
132124
# --- SonarCloud ---
133125
- name: Run SonarCloud analysis
134126
uses: SonarSource/sonarcloud-github-action@v5

docs/pre_postconditions.md

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
# Pre-conditions and Post-conditions
2+
3+
This document describes the pre-condition and post-condition attributes that can be attached to any node in XML.
4+
5+
## Pre-conditions
6+
7+
Pre-conditions are evaluated **before** a node's `tick()` method is called. They can short-circuit the node execution by returning a status immediately.
8+
9+
### Available Pre-conditions
10+
11+
| Attribute | When Evaluated | Behavior |
12+
|-----------|----------------|----------|
13+
| `_failureIf` | IDLE only | If true, return FAILURE without calling tick() |
14+
| `_successIf` | IDLE only | If true, return SUCCESS without calling tick() |
15+
| `_skipIf` | IDLE only | If true, return SKIPPED without calling tick() |
16+
| `_while` | IDLE and RUNNING | If false when IDLE, return SKIPPED. If false when RUNNING, halt node and return SKIPPED |
17+
18+
### Evaluation Order
19+
20+
When a node has multiple pre-conditions, they are evaluated in this order:
21+
1. `_failureIf`
22+
2. `_successIf`
23+
3. `_skipIf`
24+
4. `_while`
25+
26+
The first condition that triggers will determine the result.
27+
28+
### Important: One-time Evaluation
29+
30+
**`_failureIf`, `_successIf`, and `_skipIf` are evaluated only once** when the node transitions from IDLE (or SKIPPED) to another state. They are **NOT re-evaluated** while the node is RUNNING.
31+
32+
This means if you have:
33+
```xml
34+
<MyAction _successIf="condition"/>
35+
```
36+
37+
The `condition` is checked only when `MyAction` starts. If `MyAction` returns RUNNING, subsequent ticks will continue executing `MyAction` without re-checking the condition.
38+
39+
### The `_while` Exception
40+
41+
`_while` is the only pre-condition that is re-evaluated on every tick, even while the node is RUNNING. If the condition becomes false while the node is running, the node is halted and returns SKIPPED.
42+
43+
```xml
44+
<MyAction _while="battery_ok"/>
45+
```
46+
47+
If `battery_ok` becomes false while `MyAction` is running, the action is interrupted.
48+
49+
### When to Use Each Pre-condition
50+
51+
- **`_skipIf`**: Skip a node without failing the parent (useful in Sequences)
52+
- **`_failureIf`**: Fail early based on a condition (useful in Fallbacks)
53+
- **`_successIf`**: Succeed early based on a condition
54+
- **`_while`**: Guard that must remain true for the entire execution
55+
56+
### Re-evaluating Conditions Every Tick
57+
58+
If you need a condition to be checked on every tick (not just when transitioning from IDLE), use the `<Precondition>` decorator node instead of inline attributes:
59+
60+
```xml
61+
<!-- This checks the condition on every tick while child is RUNNING -->
62+
<Precondition if="my_condition" else="RUNNING">
63+
<MyAction/>
64+
</Precondition>
65+
```
66+
67+
With `else="RUNNING"`, if the condition is false, the decorator returns RUNNING (keeping the tree alive) rather than SUCCESS/FAILURE/SKIPPED.
68+
69+
## Post-conditions
70+
71+
Post-conditions are scripts executed **after** a node completes (or is halted).
72+
73+
### Available Post-conditions
74+
75+
| Attribute | When Executed |
76+
|-----------|---------------|
77+
| `_onSuccess` | After node returns SUCCESS |
78+
| `_onFailure` | After node returns FAILURE |
79+
| `_onHalted` | After node is halted (including by `_while`) |
80+
| `_post` | After any completion (SUCCESS, FAILURE, or HALTED) |
81+
82+
### Example
83+
84+
```xml
85+
<MyAction
86+
_onSuccess="result := 'ok'"
87+
_onFailure="result := 'failed'"
88+
_onHalted="result := 'interrupted'"/>
89+
```
90+
91+
## Common Patterns
92+
93+
### Conditional Execution in Sequence
94+
95+
```xml
96+
<Sequence>
97+
<CheckBattery/>
98+
<MoveToGoal _skipIf="already_at_goal"/>
99+
<PickObject/>
100+
</Sequence>
101+
```
102+
103+
If `already_at_goal` is true, `MoveToGoal` is skipped and the sequence continues with `PickObject`.
104+
105+
### Early Exit in Fallback
106+
107+
```xml
108+
<Fallback>
109+
<CachedResult _successIf="cache_valid"/>
110+
<ComputeResult/>
111+
</Fallback>
112+
```
113+
114+
If `cache_valid` is true, `CachedResult` succeeds immediately without executing.
115+
116+
### Guarded Action
117+
118+
```xml
119+
<MoveToGoal _while="battery_level > 20"/>
120+
```
121+
122+
The movement continues only while battery is sufficient. If battery drops, the action is halted.
123+
124+
## Related
125+
126+
- [Port Connection Rules](PORT_CONNECTION_RULES.md) - How ports connect between nodes
127+
- [Name Validation Rules](name_validation_rules.md) - Valid names for ports and nodes

include/behaviortree_cpp/tree_node.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,9 +44,30 @@ struct TreeNodeManifest
4444
using PortsRemapping = std::unordered_map<std::string, std::string>;
4545
using NonPortAttributes = std::unordered_map<std::string, std::string>;
4646

47+
/**
48+
* @brief Pre-conditions that can be attached to any node via XML attributes.
49+
*
50+
* Pre-conditions are evaluated in the order defined by this enum (FAILURE_IF first,
51+
* then SUCCESS_IF, then SKIP_IF, then WHILE_TRUE).
52+
*
53+
* **Important**: FAILURE_IF, SUCCESS_IF, and SKIP_IF are evaluated **only once**
54+
* when the node transitions from IDLE (or SKIPPED) to another state.
55+
* They are NOT re-evaluated while the node is RUNNING.
56+
*
57+
* - `_failureIf="<script>"`: If true when node is IDLE, return FAILURE immediately (node's tick() is not called).
58+
* - `_successIf="<script>"`: If true when node is IDLE, return SUCCESS immediately (node's tick() is not called).
59+
* - `_skipIf="<script>"`: If true when node is IDLE, return SKIPPED immediately (node's tick() is not called).
60+
* - `_while="<script>"`: Checked both on IDLE and RUNNING states.
61+
*
62+
* If false when IDLE, return SKIPPED. If false when RUNNING, halt the node
63+
* and return SKIPPED. This is the only pre-condition that can interrupt
64+
* a running node.
65+
*
66+
* If you need a condition to be re-evaluated on every tick, use the
67+
* `<Precondition>` decorator node with `else="RUNNING"` instead of these attributes.
68+
*/
4769
enum class PreCond
4870
{
49-
// order of the enums also tell us the execution order
5071
FAILURE_IF = 0,
5172
SUCCESS_IF,
5273
SKIP_IF,

src/tree_node.cpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,9 @@ Expected<NodeStatus> TreeNode::checkPreConditions()
186186
{
187187
Ast::Environment env = { config().blackboard, config().enums };
188188

189-
// check the pre-conditions
189+
// Check pre-conditions in order: FAILURE_IF, SUCCESS_IF, SKIP_IF, WHILE_TRUE.
190+
// IMPORTANT: _failureIf, _successIf, and _skipIf are evaluated ONLY when the
191+
// node is IDLE or SKIPPED. They are NOT re-evaluated while the node is RUNNING.
190192
for(size_t index = 0; index < size_t(PreCond::COUNT_); index++)
191193
{
192194
const auto& parse_executor = _p->pre_parsed[index];
@@ -197,7 +199,8 @@ Expected<NodeStatus> TreeNode::checkPreConditions()
197199

198200
const auto preID = static_cast<PreCond>(index);
199201

200-
// Some preconditions are applied only when the node state is IDLE or SKIPPED
202+
// _failureIf, _successIf, _skipIf: only checked when IDLE or SKIPPED
203+
// _while: checked here AND also when RUNNING (see below)
201204
if(_p->status == NodeStatus::IDLE || _p->status == NodeStatus::SKIPPED)
202205
{
203206
// what to do if the condition is true
@@ -224,7 +227,8 @@ Expected<NodeStatus> TreeNode::checkPreConditions()
224227
}
225228
else if(_p->status == NodeStatus::RUNNING && preID == PreCond::WHILE_TRUE)
226229
{
227-
// what to do if the condition is false
230+
// _while is the ONLY precondition checked while RUNNING.
231+
// If the condition becomes false, halt the node and return SKIPPED.
228232
if(!parse_executor(env).cast<bool>())
229233
{
230234
haltNode();

0 commit comments

Comments
 (0)