diff --git a/docs/name_validation_rules.md b/docs/name_validation_rules.md index 35abb4a5e..e7bd1803e 100644 --- a/docs/name_validation_rules.md +++ b/docs/name_validation_rules.md @@ -39,7 +39,7 @@ The following ASCII characters are forbidden in **Model Names** and **Port Names ### Port Name - **Cannot be empty** - **Cannot start with a digit** -- **Cannot be a reserved attribute**: `ID`, `name`, `_description`, `_skipIf`, `_successIf`, `_failureIf`, `_while`, `_onSuccess`, `_onFailure`, `_onHalted`, `_post`, `_autoremap`, `__shared_blackboard` +- **Cannot be a reserved attribute**: `ID`, `name`, `_description`, `_skipIf`, `_successIf`, `_failureIf`, `_while`, `_onlyIf`, `_onStart`, `_onSuccess`, `_onFailure`, `_onHalted`, `_post`, `_autoremap`, `__shared_blackboard` - No forbidden characters (see table above) ### Instance Name diff --git a/docs/pre_postconditions.md b/docs/pre_postconditions.md index cc2243c06..2aec8e86e 100644 --- a/docs/pre_postconditions.md +++ b/docs/pre_postconditions.md @@ -14,6 +14,8 @@ Pre-conditions are evaluated **before** a node's `tick()` method is called. They | `_successIf` | IDLE only | If true, return SUCCESS without calling tick() | | `_skipIf` | IDLE only | If true, return SKIPPED without calling tick() | | `_while` | IDLE and RUNNING | If false when IDLE, return SKIPPED. If false when RUNNING, halt node and return SKIPPED | +| `_onlyIf` | IDLE only | If false, return SKIPPED without calling tick() (the opposite of `_skipIf`) | +| `_onStart` | IDLE only | Not a condition: execute the script right before the first tick() | ### Evaluation Order @@ -22,12 +24,15 @@ When a node has multiple pre-conditions, they are evaluated in this order: 2. `_successIf` 3. `_skipIf` 4. `_while` +5. `_onlyIf` +6. `_onStart` -The first condition that triggers will determine the result. +The first condition that triggers will determine the result. `_onStart` comes last, +so its script is executed only if the node is actually going to tick. ### Important: One-time Evaluation -**`_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. +**`_failureIf`, `_successIf`, `_skipIf`, `_onlyIf` and `_onStart` 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. This means if you have: ```xml @@ -48,10 +53,12 @@ If `battery_ok` becomes false while `MyAction` is running, the action is interru ### When to Use Each Pre-condition -- **`_skipIf`**: Skip a node without failing the parent (useful in Sequences) +- **`_skipIf`** / **`_onlyIf`**: Skip a node without failing the parent (useful in Sequences) - **`_failureIf`**: Fail early based on a condition (useful in Fallbacks) - **`_successIf`**: Succeed early based on a condition - **`_while`**: Guard that must remain true for the entire execution +- **`_onStart`**: Initialize or reset blackboard entries when the node starts, + e.g. `` ### The `` Decorator diff --git a/include/behaviortree_cpp/tree_node.h b/include/behaviortree_cpp/tree_node.h index b4ae9506f..bc6fdcda9 100644 --- a/include/behaviortree_cpp/tree_node.h +++ b/include/behaviortree_cpp/tree_node.h @@ -48,9 +48,9 @@ using NonPortAttributes = std::unordered_map; * @brief Pre-conditions that can be attached to any node via XML attributes. * * Pre-conditions are evaluated in the order defined by this enum (FAILURE_IF first, - * then SUCCESS_IF, then SKIP_IF, then WHILE_TRUE). + * then SUCCESS_IF, SKIP_IF, WHILE_TRUE, ONLY_IF and finally ON_START). * - * **Important**: FAILURE_IF, SUCCESS_IF, and SKIP_IF are evaluated **only once** + * **Important**: all of them except WHILE_TRUE 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. * @@ -62,6 +62,11 @@ using NonPortAttributes = std::unordered_map; * If false when IDLE, return SKIPPED. If false when RUNNING, halt the node * and return SKIPPED. This is the only pre-condition that can interrupt * a running node. + * - `_onlyIf="