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
14 changes: 5 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,6 @@ and should be evaluated as code. For a concrete example see below:
In the above example the `qos_profile` is evaluated as python code. Notice to
use any python module you must use the fully qualified name.

note: Boolean values should be considered python as checking the truth of a
python string will always be evaluated as `True`, e.g. `if "False":` will evaluate
to `True`.

### Idioms

Idioms are also now supported. An idiom is a special function that produces
Expand All @@ -79,14 +75,14 @@ Create an XML file that represents your behavior tree. The XML structure should
define the nodes and their attributes. It should be similar to the following:

```xml
<py_trees.composites.Parallel name="TutorialOne" synchronise="$(False)">
<py_trees.composites.Sequence name="Topics2BB" memory="$(False)">
<py_trees.composites.Parallel name="TutorialOne" synchronise="False">
<py_trees.composites.Sequence name="Topics2BB" memory="False">
<py_trees_ros.battery.ToBlackboard name="Battery2BB"
topic_name="/battery/state"
qos_profile="$(py_trees_ros.utilities.qos_profile_unlatched())"
threshold="30.0" />
</py_trees.composites.Sequence>
<py_trees.composites.Selector name="Tasks" memory="$(False)">
<py_trees.composites.Selector name="Tasks" memory="False">
<py_trees.behaviours.Running name="Idle" />
<py_trees.behaviours.Periodic name="Flip Eggs" n="2" />
</py_trees.composites.Selector>
Expand Down Expand Up @@ -162,7 +158,7 @@ another subtree. However, be aware that the all directories are absolute, but it
is possible to use python to determine the path like so:

```xml
<py_trees.composites.Parallel name="Subtree Tutorial" synchronise="$(False)">
<py_trees.composites.Parallel name="Subtree Tutorial" synchronise="False">
<subtree
name="my_subtree"
include="$(os.path.join(ament_index_python.packages.get_package_share_directory('my_package'), 'tree', 'subtree.xml'))" />
Expand Down Expand Up @@ -222,4 +218,4 @@ and finally in subtree2
<py_trees.composites.Sequence name="Cascading Arg Tutorial">
<py_trees.behaviors.Success name="${baz}" />
</py_trees.composites.Sequence>
```
```
18 changes: 18 additions & 0 deletions py_trees_parser/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,22 @@ def is_float(value: str) -> bool:
return False


def is_bool(value: str) -> bool:
"""
Check if a string is a boolean type, i.e. True or False.

Args:
----
value: The string to check.

Returns:
-------
True if the string is "true", "True", "false", or "False", False otherwise.

"""
return value.lower() == "true" or value.lower() == "false"


def is_code(value: str) -> bool:
"""
Check if a string is intended to be code.
Expand Down Expand Up @@ -244,6 +260,8 @@ def _string_num_or_code(self, value: str) -> Any:
value = int(value)
elif is_float(value):
value = float(value)
elif is_bool(value):
value = value.lower() == "true"
elif is_code(value):
value = self._parse_code(value)

Expand Down
4 changes: 2 additions & 2 deletions test/data/test1.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
https://py-trees-ros-tutorials.readthedocs.io/en/release-2.1.x/tutorials.html#tree -->
<py_trees.composites.Parallel name="TutorialOne"
policy="$(py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False))">
<py_trees.composites.Sequence name="Topics2BB" memory="$(False)">
<py_trees.composites.Sequence name="Topics2BB" memory="False">
<py_trees_ros.battery.ToBlackboard name="Battery2BB"
topic_name="/battery/state"
qos_profile="$(py_trees_ros.utilities.qos_profile_unlatched())"
threshold="30.0" />
</py_trees.composites.Sequence>
<py_trees.composites.Selector name="Tasks" memory="$(False)">
<py_trees.composites.Selector name="Tasks" memory="False">
<py_trees.behaviours.Running name="Idle" />
<py_trees.behaviours.Periodic name="Flip Eggs" n="2" />
</py_trees.composites.Selector>
Expand Down
8 changes: 4 additions & 4 deletions test/data/test6.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@
https://py-trees-ros-tutorials.readthedocs.io/en/release-2.1.x/tutorials.html#id13 -->
<py_trees.composites.Parallel name="TutorialSix"
policy="$(py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False))">
<py_trees.composites.Sequence name="Topics2BB" memory="$(False)">
<py_trees.composites.Sequence name="Topics2BB" memory="False">
<py_trees_ros.subscribers.EventToBlackboard name="Scan2BB"
topic_name="/dashboard/scan"
qos_profile="$(py_trees_ros.utilities.qos_profile_unlatched())"
variable_name="event_scan_button" />
<py_trees_ros.battery.ToBlackboard name="Battery2BB" topic_name="/battery/state"
qos_profile="py_trees_ros.utilities.qos_profile_unlatched()" threshold="30.0" />
</py_trees.composites.Sequence>
<py_trees.composites.Selector name="Tasks" memory="$(False)">
<py_trees.composites.Selector name="Tasks" memory="False">
<py_trees.decorators.EternalGuard name="Battery Low?"
condition="$(py_trees_parser.behaviors.testing_behaviors.check_battery_low_on_blackboard)"
blackboard_keys="$({'battery_low_warning'})">
<py_trees_parser.behaviors.testing_behaviors.FlashLedStrip name="Flash Red" colour="red" />
</py_trees.decorators.EternalGuard>
<py_trees.composites.Sequence name="Scan" memory="$(False)">
<py_trees.composites.Sequence name="Scan" memory="False">
<py_trees.behaviours.CheckBlackboardVariableValue name="Scan?"
check="$(py_trees.common.ComparisonExpression(variable='event_scan_button', value=True, operator=operator.eq))" />
<py_trees.composites.Selector name="Preempt?" memory="$(False)">
<py_trees.composites.Selector name="Preempt?" memory="False">
<py_trees.decorators.SuccessIsRunning name="SuccessIsRunning">
<py_trees.behaviours.CheckBlackboardVariableValue name="Scan?"
check="$(py_trees.common.ComparisonExpression(variable='event_scan_button', value=True, operator=operator.eq))" />
Expand Down
4 changes: 2 additions & 2 deletions test/data/test_arg_substitution.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<py_trees.composites.Selector name="Argument Substitution Test" memory="$(False)">
<py_trees.composites.Selector name="Argument Substitution Test" memory="False">
<py_trees.behaviours.Running name="prefix_${arg1}_suffix" />
<py_trees.behaviours.Success name="task_${arg2}_complete" />
<py_trees.behaviours.Periodic name="periodic_${arg3}" n="${n}" />
<py_trees.behaviours.Success name="multiple_${arg1}_and_${arg2}_args" />
</py_trees.composites.Selector>
</py_trees.composites.Selector>
2 changes: 1 addition & 1 deletion test/data/test_arg_substitution_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
<arg name="arg2" value="value2" />
<arg name="arg3" value="value3" />
<arg name="n" value="3" />
</subtree>
</subtree>
5 changes: 5 additions & 0 deletions test/data/test_bool.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<py_trees.composites.Sequence name="No Memory" memory="False">
<py_trees.composites.Sequence name="Memory" memory="True">
<py_trees.behaviours.Success name="Feature 1" />
</py_trees.composites.Sequence>
</py_trees.composites.Sequence>
2 changes: 1 addition & 1 deletion test/data/test_idioms.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<py_trees.composites.Sequence name="Idioms" memory="$(False)">
<py_trees.composites.Sequence name="Idioms" memory="False">
<py_trees.idioms.oneshot name="OneShot"
variable_name="one_shot"
policy="$(py_trees.common.OneShotPolicy)">
Expand Down
2 changes: 1 addition & 1 deletion test/data/test_subtree_args.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<py_trees.composites.Selector name="${selector_name}" memory="$(False)">
<py_trees.composites.Selector name="${selector_name}" memory="False">
<py_trees.behaviours.Running name="${idle_name}" />
<py_trees.behaviours.Periodic name="${flip_name}" n="${n}" />
</py_trees.composites.Selector>
2 changes: 1 addition & 1 deletion test/data/test_subtree_main.xml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<py_trees.composites.Parallel name="SubtreeMain"
policy="$(py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False))">
<py_trees.composites.Sequence name="Topics2BB" memory="$(False)">
<py_trees.composites.Sequence name="Topics2BB" memory="False">
<py_trees_ros.battery.ToBlackboard name="Battery2BB" topic_name="/battery/state"
qos_profile="$(py_trees_ros.utilities.qos_profile_unlatched())" threshold="30.0" />
</py_trees.composites.Sequence>
Expand Down
2 changes: 1 addition & 1 deletion test/data/test_subtree_sub.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<py_trees.composites.Selector name="SubTreeSub" memory="$(False)">
<py_trees.composites.Selector name="SubTreeSub" memory="False">
<py_trees.behaviours.Running name="Idle" />
<py_trees.behaviours.Periodic name="Flip Eggs" n="2" />
</py_trees.composites.Selector>
11 changes: 11 additions & 0 deletions test/test_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,3 +130,14 @@ def test_arg_substitution_within_strings(setup_parser):
assert child.period == 3
else:
assert False, f"Unexpected child node type {type(child)}" # noqa


def test_bool(setup_parser):
"""Test that True, true, False, or false are evaluated as booleans."""
tree_file = "test/data/test_bool.xml"
root = setup_parser(tree_file)

assert root.name == "No Memory"
assert root.memory is False
assert root.children[0].name == "Memory"
assert root.children[0].memory
Loading