Demos

py-trees-demo-action-behaviour

A py_trees demo.

Demonstrates the characteristics of a typical ‘action’ behaviour.

  • Mocks an external process and connects to it in the setup() method

  • Kickstarts new goals with the external process in the initialise() method

  • Monitors the ongoing goal status in the update() method

  • Determines RUNNING/SUCCESS pending feedback from the external process

usage: py-trees-demo-action-behaviour [-h]
_images/action.gif
class py_trees.demos.action.Action(name: str)[source]

Bases: Behaviour

Demonstrates the at-a-distance style action behaviour.

This behaviour connects to a separately running process (initiated in setup()) and proceeeds to work with that subprocess to initiate a task and monitor the progress of that task at each tick until completed. While the task is running the behaviour returns RUNNING.

On completion, the the behaviour returns with success or failure (depending on success or failure of the task itself).

Key point - this behaviour itself should not be doing any work!

Parameters:

name (str) –

__init__(name: str)[source]

Configure the name of the behaviour.

Parameters:

name (str) –

initialise() None[source]

Reset a counter variable.

Return type:

None

setup(**kwargs: Any) None[source]

Kickstart the separate process this behaviour will work with.

Ordinarily this process will be already running. In this case, setup is usually just responsible for verifying it exists.

Parameters:

kwargs (Any) –

Return type:

None

terminate(new_status: Status) None[source]

Nothing to clean up in this example.

Parameters:

new_status (Status) –

Return type:

None

update() Status[source]

Increment the counter, monitor and decide on a new status.

Return type:

Status

py_trees.demos.action.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.action.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.action.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.action.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees.demos.action.planning(pipe_connection: Connection) None[source]

Emulate a (potentially) long running external process.

Parameters:

pipe_connection (Connection) – connection to the planning process

Return type:

None

py_trees/demos/action.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.action
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-action-behaviour
 17
 18.. image:: images/action.gif
 19"""
 20
 21##############################################################################
 22# Imports
 23##############################################################################
 24
 25import argparse
 26import atexit
 27import multiprocessing
 28import multiprocessing.connection
 29import time
 30import typing
 31
 32import py_trees.common
 33import py_trees.console as console
 34
 35##############################################################################
 36# Classes
 37##############################################################################
 38
 39
 40def description() -> str:
 41    """
 42    Print description and usage information about the program.
 43
 44    Returns:
 45       the program description string
 46    """
 47    content = "Demonstrates the characteristics of a typical 'action' behaviour.\n"
 48    content += "\n"
 49    content += "* Mocks an external process and connects to it in the setup() method\n"
 50    content += "* Kickstarts new goals with the external process in the initialise() method\n"
 51    content += "* Monitors the ongoing goal status in the update() method\n"
 52    content += "* Determines RUNNING/SUCCESS pending feedback from the external process\n"
 53
 54    if py_trees.console.has_colours:
 55        banner_line = console.green + "*" * 79 + "\n" + console.reset
 56        s = banner_line
 57        s += console.bold_white + "Action Behaviour".center(79) + "\n" + console.reset
 58        s += banner_line
 59        s += "\n"
 60        s += content
 61        s += "\n"
 62        s += banner_line
 63    else:
 64        s = content
 65    return s
 66
 67
 68def epilog() -> str | None:
 69    """
 70    Print a noodly epilog for --help.
 71
 72    Returns:
 73       the noodly message
 74    """
 75    if py_trees.console.has_colours:
 76        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 77    else:
 78        return None
 79
 80
 81def command_line_argument_parser() -> argparse.ArgumentParser:
 82    """
 83    Process command line arguments.
 84
 85    Returns:
 86        the argument parser
 87    """
 88    return argparse.ArgumentParser(
 89        description=description(),
 90        epilog=epilog(),
 91        formatter_class=argparse.RawDescriptionHelpFormatter,
 92    )
 93
 94
 95def planning(pipe_connection: multiprocessing.connection.Connection) -> None:
 96    """Emulate a (potentially) long running external process.
 97
 98    Args:
 99        pipe_connection: connection to the planning process
100    """
101    idle = True
102    percentage_complete = 0
103    try:
104        while True:
105            if pipe_connection.poll():
106                pipe_connection.recv()
107                percentage_complete = 0
108                idle = False
109            if not idle:
110                percentage_complete += 10
111                pipe_connection.send([percentage_complete])
112                if percentage_complete == 100:
113                    idle = True
114            time.sleep(0.5)
115    except KeyboardInterrupt:
116        pass
117
118
119class Action(py_trees.behaviour.Behaviour):
120    """Demonstrates the at-a-distance style action behaviour.
121
122    This behaviour connects to a separately running process
123    (initiated in setup()) and proceeeds to work with that subprocess to
124    initiate a task and monitor the progress of that task at each tick
125    until completed. While the task is running the behaviour returns
126    :data:`~py_trees.common.Status.RUNNING`.
127
128    On completion, the the behaviour returns with success or failure
129    (depending on success or failure of the task itself).
130
131    Key point - this behaviour itself should not be doing any work!
132    """
133
134    def __init__(self, name: str):
135        """Configure the name of the behaviour."""
136        super().__init__(name)
137        self.logger.debug(f"{self.__class__.__name__}.__init__()")
138
139    def setup(self, **kwargs: typing.Any) -> None:
140        """Kickstart the separate process this behaviour will work with.
141
142        Ordinarily this process will be already running. In this case,
143        setup is usually just responsible for verifying it exists.
144        """
145        self.logger.debug(f"{self.__class__.__name__}.setup()->connections to an external process")
146        self.parent_connection, self.child_connection = multiprocessing.Pipe()
147        self.planning = multiprocessing.Process(target=planning, args=(self.child_connection,))
148        atexit.register(self.planning.terminate)
149        self.planning.start()
150
151    def initialise(self) -> None:
152        """Reset a counter variable."""
153        self.logger.debug(f"{self.__class__.__name__}.initialise()->sending new goal")
154        self.parent_connection.send(["new goal"])
155        self.percentage_completion = 0
156
157    def update(self) -> py_trees.common.Status:
158        """Increment the counter, monitor and decide on a new status."""
159        new_status = py_trees.common.Status.RUNNING
160        if self.parent_connection.poll():
161            self.percentage_completion = self.parent_connection.recv().pop()
162            if self.percentage_completion == 100:
163                new_status = py_trees.common.Status.SUCCESS
164        if new_status == py_trees.common.Status.SUCCESS:
165            self.feedback_message = "Processing finished"
166            self.logger.debug(
167                f"{self.__class__.__name__}.update()[{self.status}->{new_status}][{self.feedback_message}]"
168            )
169        else:
170            self.feedback_message = f"{self.percentage_completion}%"
171            self.logger.debug(f"{self.__class__.__name__}.update()[{self.status}][{self.feedback_message}]")
172        return new_status
173
174    def terminate(self, new_status: py_trees.common.Status) -> None:
175        """Nothing to clean up in this example."""
176        self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}]")
177
178
179##############################################################################
180# Main
181##############################################################################
182
183
184def main() -> None:
185    """Entry point for the demo script."""
186    command_line_argument_parser().parse_args()
187
188    print(description())
189
190    py_trees.logging.level = py_trees.logging.Level.DEBUG
191
192    action = Action(name="Action")
193    action.setup()
194    try:
195        for _unused_i in range(0, 12):
196            action.tick_once()
197            time.sleep(0.5)
198        print("\n")
199    except KeyboardInterrupt:
200        pass

py-trees-demo-behaviour-lifecycle

A py_trees demo.

. argparse::
module:

py_trees.demos.lifecycle

func:

command_line_argument_parser

prog:

py-trees-demo-behaviour-lifecycle

_images/lifecycle.gif
class py_trees.demos.lifecycle.Counter(name: str = 'Counter')[source]

Bases: Behaviour

Simple counting behaviour.

  • Increments a counter from zero at each tick

  • Finishes with success if the counter reaches three

  • Resets the counter in the initialise() method.

Parameters:

name (str) –

__init__(name: str = 'Counter')[source]

Configure the name of the behaviour.

Parameters:

name (str) –

initialise() None[source]

Reset a counter variable.

Return type:

None

setup(**kwargs: Any) None[source]

No delayed initialisation required for this example.

Parameters:

kwargs (Any) –

Return type:

None

terminate(new_status: Status) None[source]

Nothing to clean up in this example.

Parameters:

new_status (Status) –

Return type:

None

update() Status[source]

Increment the counter and decide on a new status.

Return type:

Status

py_trees.demos.lifecycle.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.lifecycle.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.lifecycle.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.lifecycle.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/lifecycle.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13. argparse::
 14   :module: py_trees.demos.lifecycle
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-behaviour-lifecycle
 17
 18.. image:: images/lifecycle.gif
 19"""
 20
 21##############################################################################
 22# Imports
 23##############################################################################
 24
 25import argparse
 26import time
 27import typing
 28
 29import py_trees
 30import py_trees.console as console
 31
 32##############################################################################
 33# Classes
 34##############################################################################
 35
 36
 37def description() -> str:
 38    """
 39    Print description and usage information about the program.
 40
 41    Returns:
 42       the program description string
 43    """
 44    content = "Demonstrates a typical day in the life of a behaviour.\n\n"
 45    content += "This behaviour will count from 1 to 3 and then reset and repeat. As it does\n"
 46    content += "so, it logs and displays the methods as they are called - construction, setup,\n"
 47    content += "initialisation, ticking and termination.\n"
 48    if py_trees.console.has_colours:
 49        banner_line = console.green + "*" * 79 + "\n" + console.reset
 50        s = banner_line
 51        s += console.bold_white + "Behaviour Lifecycle".center(79) + "\n" + console.reset
 52        s += banner_line
 53        s += "\n"
 54        s += content
 55        s += "\n"
 56        s += banner_line
 57    else:
 58        s = content
 59    return s
 60
 61
 62def epilog() -> str | None:
 63    """
 64    Print a noodly epilog for --help.
 65
 66    Returns:
 67       the noodly message
 68    """
 69    if py_trees.console.has_colours:
 70        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 71    else:
 72        return None
 73
 74
 75def command_line_argument_parser() -> argparse.ArgumentParser:
 76    """
 77    Process command line arguments.
 78
 79    Returns:
 80        the argument parser
 81    """
 82    return argparse.ArgumentParser(
 83        description=description(),
 84        epilog=epilog(),
 85        formatter_class=argparse.RawDescriptionHelpFormatter,
 86    )
 87
 88
 89class Counter(py_trees.behaviour.Behaviour):
 90    """Simple counting behaviour.
 91
 92    * Increments a counter from zero at each tick
 93    * Finishes with success if the counter reaches three
 94    * Resets the counter in the initialise() method.
 95    """
 96
 97    def __init__(self, name: str = "Counter"):
 98        """Configure the name of the behaviour."""
 99        super().__init__(name)
100        self.logger.debug(f"{self.__class__.__name__}.__init__()")
101
102    def setup(self, **kwargs: typing.Any) -> None:
103        """No delayed initialisation required for this example."""
104        self.logger.debug(f"{self.__class__.__name__}.setup()")
105
106    def initialise(self) -> None:
107        """Reset a counter variable."""
108        self.logger.debug(f"{self.__class__.__name__}.initialise()")
109        self.counter = 0
110
111    def update(self) -> py_trees.common.Status:
112        """Increment the counter and decide on a new status."""
113        self.counter += 1
114        new_status = py_trees.common.Status.SUCCESS if self.counter == 3 else py_trees.common.Status.RUNNING
115        if new_status == py_trees.common.Status.SUCCESS:
116            self.feedback_message = f"counting...{self.counter} - phew, thats enough for today"
117        else:
118            self.feedback_message = "still counting"
119        self.logger.debug(f"{self.__class__.__name__}.update()[{self.status}->{new_status}][{self.feedback_message}]")
120        return new_status
121
122    def terminate(self, new_status: py_trees.common.Status) -> None:
123        """Nothing to clean up in this example."""
124        self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}]")
125
126
127##############################################################################
128# Main
129##############################################################################
130
131
132def main() -> None:
133    """Entry point for the demo script."""
134    command_line_argument_parser().parse_args()
135
136    print(description())
137
138    py_trees.logging.level = py_trees.logging.Level.DEBUG
139
140    counter = Counter()
141    counter.setup()
142    try:
143        for _unused_i in range(0, 7):
144            counter.tick_once()
145            time.sleep(0.5)
146        print("\n")
147    except KeyboardInterrupt:
148        print("")
149        pass

py-trees-demo-blackboard

A py_trees demo.

Demonstrates usage of the blackboard and related behaviours.

A sequence is populated with a few behaviours that exercise reading and writing on the Blackboard in interesting ways.

usage: py-trees-demo-blackboard [-h] [-r | --render-with-blackboard-variables]

Named Arguments

-r, --render

render dot tree to file

Default: False

--render-with-blackboard-variables

render dot tree to file with blackboard variables

Default: False

digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Blackboard Demo" [label="Blackboard Demo", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Set Nested" [label="Set Nested", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Set Nested";
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> Writer;
"Check Nested Foo" [label="Check Nested Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> "Check Nested Foo";
ParamsAndState [label=ParamsAndState, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Blackboard Demo" -> ParamsAndState;
subgraph  {
label="children_of_Blackboard Demo";
rank=same;
"Set Nested" [label="Set Nested", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Writer [label=Writer, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Check Nested Foo" [label="Check Nested Foo", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
ParamsAndState [label=ParamsAndState, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

Configuration [label=Configuration, shape=ellipse, style=filled, color=blue, fillcolor=gray, fontsize=7, fontcolor=blue];
"/dude" [label="/dude: Bob", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/dude" -> Writer  [color=blue, constraint=False];
Configuration -> "/dude"  [color=blue, constraint=False];
"/parameters/default_speed" [label="/parameters/default_speed: 30.0", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/parameters/default_speed" -> ParamsAndState  [color=blue, constraint=False];
Configuration -> "/parameters/default_speed"  [color=blue, constraint=False];
"/nested" [label="/nested: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
"/nested" -> "Check Nested Foo"  [color=blue, constraint=False];
"Set Nested" -> "/nested"  [color=blue, constraint=True];
"/spaghetti" [label="/spaghetti: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
Writer -> "/spaghetti"  [color=blue, constraint=True];
"/state/current_speed" [label="/state/current_speed: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
ParamsAndState -> "/state/current_speed"  [color=blue, constraint=True];
}

Dot Graph

_images/blackboard_demo.png

Console Screenshot

class py_trees.demos.blackboard.BlackboardWriter(name: str)[source]

Bases: Behaviour

Write some more interesting / complex types to the blacbkoard.

Parameters:

name (str) –

__init__(name: str)[source]

Set up the blackboard.

Parameters:

name (str) – behaviour name

update() Status[source]

Write a dictionary to the blackboard.

This beaviour always returns SUCCESS.

Return type:

Status

class py_trees.demos.blackboard.Nested[source]

Bases: object

A more complex object to interact with on the blackboard.

__init__() None[source]

Initialise variables to some arbitrary defaults.

Return type:

None

__str__() str[source]

Return str(self).

Return type:

str

__weakref__

list of weak references to the object

class py_trees.demos.blackboard.ParamsAndState(name: str)[source]

Bases: Behaviour

Parameter and state storage on the blackboard.

This behaviour demonstrates the usage of namespaces and multiple clients to perform getting and setting of parameters and state in a concise and convenient manner.

Parameters:

name (str) –

__init__(name: str)[source]

Set up separate blackboard clients for parameters and state.

Parameters:

name (str) – behaviour name

initialise() None[source]

Initialise speed from the stored parameter variable on the blackboard.

Return type:

None

update() Status[source]

Check speed and either increment, or complete if it has reached a threshold.

Returns:

RUNNING if incrementing, SUCCESS otherwise.

Return type:

Status

py_trees.demos.blackboard.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.blackboard.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.blackboard.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.blackboard.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.blackboard.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/blackboard.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.blackboard
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-blackboard
 17
 18.. graphviz:: dot/demo-blackboard.dot
 19   :align: center
 20   :caption: Dot Graph
 21
 22.. figure:: images/blackboard_demo.png
 23   :align: center
 24
 25   Console Screenshot
 26"""
 27
 28##############################################################################
 29# Imports
 30##############################################################################
 31
 32import argparse
 33import operator
 34import sys
 35
 36import py_trees
 37import py_trees.console as console
 38
 39##############################################################################
 40# Classes
 41##############################################################################
 42
 43
 44def description() -> str:
 45    """
 46    Print description and usage information about the program.
 47
 48    Returns:
 49       the program description string
 50    """
 51    content = "Demonstrates usage of the blackboard and related behaviours.\n"
 52    content += "\n"
 53    content += "A sequence is populated with a few behaviours that exercise\n"
 54    content += "reading and writing on the Blackboard in interesting ways.\n"
 55
 56    if py_trees.console.has_colours:
 57        banner_line = console.green + "*" * 79 + "\n" + console.reset
 58        s = banner_line
 59        s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
 60        s += banner_line
 61        s += "\n"
 62        s += content
 63        s += "\n"
 64        s += banner_line
 65    else:
 66        s = content
 67    return s
 68
 69
 70def epilog() -> str | None:
 71    """
 72    Print a noodly epilog for --help.
 73
 74    Returns:
 75       the noodly message
 76    """
 77    if py_trees.console.has_colours:
 78        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 79    else:
 80        return None
 81
 82
 83def command_line_argument_parser() -> argparse.ArgumentParser:
 84    """
 85    Process command line arguments.
 86
 87    Returns:
 88        the argument parser
 89    """
 90    parser = argparse.ArgumentParser(
 91        description=description(),
 92        epilog=epilog(),
 93        formatter_class=argparse.RawDescriptionHelpFormatter,
 94    )
 95    render_group = parser.add_mutually_exclusive_group()
 96    render_group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 97    render_group.add_argument(
 98        "--render-with-blackboard-variables",
 99        action="store_true",
100        help="render dot tree to file with blackboard variables",
101    )
102    return parser
103
104
105class Nested:
106    """A more complex object to interact with on the blackboard."""
107
108    def __init__(self) -> None:
109        """Initialise variables to some arbitrary defaults."""
110        self.foo = "bar"
111
112    def __str__(self) -> str:
113        return str({"foo": self.foo})
114
115
116class BlackboardWriter(py_trees.behaviour.Behaviour):
117    """Write some more interesting / complex types to the blacbkoard."""
118
119    def __init__(self, name: str):
120        """Set up the blackboard.
121
122        Args:
123            name: behaviour name
124        """
125        super().__init__(name=name)
126        self.blackboard = self.attach_blackboard_client()
127        self.blackboard.register_key(key="dude", access=py_trees.common.Access.READ)
128        self.blackboard.register_key(key="spaghetti", access=py_trees.common.Access.WRITE)
129
130        self.logger.debug(f"{self.__class__.__name__}.__init__()")
131
132    def update(self) -> py_trees.common.Status:
133        """Write a dictionary to the blackboard.
134
135        This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
136        """
137        self.logger.debug(f"{self.__class__.__name__}.update()")
138        try:
139            _ = self.blackboard.dude
140        except KeyError:
141            pass
142        try:
143            _ = self.blackboard.dudette
144        except AttributeError:
145            pass
146        try:
147            self.blackboard.dudette = "Jane"
148        except AttributeError:
149            pass
150        self.blackboard.spaghetti = {"type": "Carbonara", "quantity": 1}
151        self.blackboard.spaghetti = {"type": "Gnocchi", "quantity": 2}
152        try:
153            self.blackboard.set("spaghetti", {"type": "Bolognese", "quantity": 3}, overwrite=False)
154        except AttributeError:
155            pass
156        return py_trees.common.Status.SUCCESS
157
158
159class ParamsAndState(py_trees.behaviour.Behaviour):
160    """Parameter and state storage on the blackboard.
161
162    This behaviour demonstrates the usage of namespaces and
163    multiple clients to perform getting and setting of
164    parameters and state in a concise and convenient manner.
165    """
166
167    def __init__(self, name: str):
168        """Set up separate blackboard clients for parameters and state.
169
170        Args:
171           name: behaviour name
172        """
173        super().__init__(name=name)
174        # namespaces can include the separator or may leave it out
175        # they can also be nested, e.g. /agent/state, /agent/parameters
176        self.parameters = self.attach_blackboard_client("Params", "parameters")
177        self.state = self.attach_blackboard_client("State", "state")
178        self.parameters.register_key(key="default_speed", access=py_trees.common.Access.READ)
179        self.state.register_key(key="current_speed", access=py_trees.common.Access.WRITE)
180
181    def initialise(self) -> None:
182        """Initialise speed from the stored parameter variable on the blackboard."""
183        try:
184            self.state.current_speed = self.parameters.default_speed
185        except KeyError as e:
186            raise RuntimeError(f"parameter 'default_speed' not found [{str(e)}]") from e
187
188    def update(self) -> py_trees.common.Status:
189        """
190        Check speed and either increment, or complete if it has reached a threshold.
191
192        Returns:
193            :data:`~py_trees.common.Status.RUNNING` if incrementing, :data:`~py_trees.common.Status.SUCCESS` otherwise.
194        """
195        if self.state.current_speed > 40.0:
196            return py_trees.common.Status.SUCCESS
197        else:
198            self.state.current_speed += 1.0
199            return py_trees.common.Status.RUNNING
200
201
202def create_root() -> py_trees.behaviour.Behaviour:
203    """
204    Create the root behaviour and its subtree.
205
206    Returns:
207        the root behaviour
208    """
209    root = py_trees.composites.Sequence(name="Blackboard Demo", memory=True)
210    set_blackboard_variable = py_trees.behaviours.SetBlackboardVariable(
211        name="Set Nested",
212        variable_name="nested",
213        variable_value=Nested(),
214        overwrite=True,
215    )
216    write_blackboard_variable = BlackboardWriter(name="Writer")
217    check_blackboard_variable = py_trees.behaviours.CheckBlackboardVariableValue(
218        name="Check Nested Foo",
219        check=py_trees.common.ComparisonExpression(variable="nested.foo", value="bar", operator=operator.eq),
220    )
221    params_and_state = ParamsAndState(name="ParamsAndState")
222    root.add_children(
223        [
224            set_blackboard_variable,
225            write_blackboard_variable,
226            check_blackboard_variable,
227            params_and_state,
228        ]
229    )
230    return root
231
232
233##############################################################################
234# Main
235##############################################################################
236
237
238def main() -> None:
239    """Entry point for the demo script."""
240    args = command_line_argument_parser().parse_args()
241    print(description())
242    py_trees.logging.level = py_trees.logging.Level.DEBUG
243    py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
244    blackboard = py_trees.blackboard.Client(name="Configuration")
245    blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
246    blackboard.register_key(key="/parameters/default_speed", access=py_trees.common.Access.EXCLUSIVE_WRITE)
247    blackboard.dude = "Bob"
248    blackboard.parameters.default_speed = 30.0
249
250    root = create_root()
251
252    ####################
253    # Rendering
254    ####################
255    if args.render:
256        py_trees.display.render_dot_tree(root, with_blackboard_variables=False)
257        sys.exit()
258    if args.render_with_blackboard_variables:
259        py_trees.display.render_dot_tree(root, with_blackboard_variables=True)
260        sys.exit()
261
262    ####################
263    # Execute
264    ####################
265    root.setup_with_descendants()
266    unset_blackboard = py_trees.blackboard.Client(name="Unsetter")
267    unset_blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
268    print("\n--------- Tick 0 ---------\n")
269    root.tick_once()
270    print("\n")
271    print(py_trees.display.unicode_tree(root, show_status=True))
272    print("--------------------------\n")
273    print(py_trees.display.unicode_blackboard())
274    print("--------------------------\n")
275    print(py_trees.display.unicode_blackboard(display_only_key_metadata=True))
276    print("--------------------------\n")
277    unset_blackboard.unset("foo")
278    print(py_trees.display.unicode_blackboard_activity_stream())

py-trees-demo-blackboard-namespaces

A py_trees demo.

Demonstrates usage of blackboard namespaces.

usage: py-trees-demo-blackboard-namespaces [-h]
_images/blackboard_namespaces.png

Console Screenshot

py_trees.demos.blackboard_namespaces.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.blackboard_namespaces.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.blackboard_namespaces.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.blackboard_namespaces.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/blackboard_namespaces.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.blackboard_namespaces
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-blackboard-namespaces
 17
 18.. figure:: images/blackboard_namespaces.png
 19   :align: center
 20
 21   Console Screenshot
 22"""
 23
 24##############################################################################
 25# Imports
 26##############################################################################
 27
 28import argparse
 29
 30import py_trees
 31import py_trees.console as console
 32
 33##############################################################################
 34# Classes
 35##############################################################################
 36
 37
 38def description() -> str:
 39    """
 40    Print description and usage information about the program.
 41
 42    Returns:
 43       the program description string
 44    """
 45    content = "Demonstrates usage of blackboard namespaces.\n"
 46    content += "\n"
 47
 48    if py_trees.console.has_colours:
 49        banner_line = console.green + "*" * 79 + "\n" + console.reset
 50        s = banner_line
 51        s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
 52        s += banner_line
 53        s += "\n"
 54        s += content
 55        s += "\n"
 56        s += banner_line
 57    else:
 58        s = content
 59    return s
 60
 61
 62def epilog() -> str | None:
 63    """
 64    Print a noodly epilog for --help.
 65
 66    Returns:
 67       the noodly message
 68    """
 69    if py_trees.console.has_colours:
 70        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 71    else:
 72        return None
 73
 74
 75def command_line_argument_parser() -> argparse.ArgumentParser:
 76    """
 77    Process command line arguments.
 78
 79    Returns:
 80        the argument parser
 81    """
 82    parser = argparse.ArgumentParser(
 83        description=description(),
 84        epilog=epilog(),
 85        formatter_class=argparse.RawDescriptionHelpFormatter,
 86    )
 87    return parser
 88
 89
 90##############################################################################
 91# Main
 92##############################################################################
 93
 94
 95def main() -> None:
 96    """Entry point for the demo script."""
 97    _ = command_line_argument_parser().parse_args()  # configuration only, no args to process
 98    print(description())
 99    print("-------------------------------------------------------------------------------")
100    print("$ py_trees.blackboard.Client(name='Blackboard')")
101    print("$ foo.register_key(key='dude', access=py_trees.common.Access.WRITE)")
102    print("$ foo.register_key(key='/dudette', access=py_trees.common.Access.WRITE)")
103    print("$ foo.register_key(key='/foo/bar/wow', access=py_trees.common.Access.WRITE)")
104    print("-------------------------------------------------------------------------------")
105    blackboard = py_trees.blackboard.Client(name="Blackboard")
106    blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
107    blackboard.register_key(key="/dudette", access=py_trees.common.Access.WRITE)
108    blackboard.register_key(key="/foo/bar/wow", access=py_trees.common.Access.WRITE)
109    print(blackboard)
110    print("-------------------------------------------------------------------------------")
111    print("$ blackboard.dude = 'Bob'")
112    print("$ blackboard.dudette = 'Jade'")
113    print("-------------------------------------------------------------------------------")
114    blackboard.dude = "Bob"
115    blackboard.dudette = "Jade"
116    print(py_trees.display.unicode_blackboard())
117    print("-------------------------------------------------------------------------------")
118    print("$ blackboard.foo.bar.wow = 'foobar'")
119    print("-------------------------------------------------------------------------------")
120    blackboard.foo.bar.wow = "foobar"
121    print(py_trees.display.unicode_blackboard())
122    print("-------------------------------------------------------------------------------")
123    print("$ py_trees.blackboard.Client(name='Foo', namespace='foo')")
124    print("$ foo.register_key(key='awesome', access=py_trees.common.Access.WRITE)")
125    print("$ foo.register_key(key='/brilliant', access=py_trees.common.Access.WRITE)")
126    print("$ foo.register_key(key='/foo/clever', access=py_trees.common.Access.WRITE)")
127    print("-------------------------------------------------------------------------------")
128    foo = py_trees.blackboard.Client(name="Foo", namespace="foo")
129    foo.register_key(key="awesome", access=py_trees.common.Access.WRITE)
130    # TODO: should /brilliant be namespaced or go directly to root?
131    foo.register_key(key="/brilliant", access=py_trees.common.Access.WRITE)
132    # absolute names are ok, so long as they include the namespace
133    foo.register_key(key="/foo/clever", access=py_trees.common.Access.WRITE)
134    print(foo)
135    print("-------------------------------------------------------------------------------")
136    print("$ foo.awesome = True")
137    print("$ foo.set('/brilliant', False)")
138    print("$ foo.clever = True")
139    print("-------------------------------------------------------------------------------")
140    foo.awesome = True
141    # Only accessible via set since it's not in the namespace
142    foo.set("/brilliant", False)
143    # This will fail since it looks for the namespaced /foo/brilliant key
144    # foo.brilliant = False
145    foo.clever = True
146    print(py_trees.display.unicode_blackboard())

py-trees-demo-blackboard-remappings

A py_trees demo.

Demonstrates usage of blackbord remappings.

Demonstration is via an exemplar behaviour making use of remappings..

usage: py-trees-demo-blackboard-remappings [-h]
_images/blackboard_remappings.png

Console Screenshot

class py_trees.demos.blackboard_remappings.Remap(name: str, remap_to: dict[str, str])[source]

Bases: Behaviour

Custom writer that submits a more complicated variable to the blackboard.

Parameters:
__init__(name: str, remap_to: dict[str, str])[source]

Set up the blackboard and remap variables.

Parameters:
  • name (str) – behaviour name

  • remap_to (dict[str, str]) – remappings (from variable name to variable name)

update() Status[source]

Write a dictionary to the blackboard.

This beaviour always returns SUCCESS.

Return type:

Status

py_trees.demos.blackboard_remappings.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.blackboard_remappings.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.blackboard_remappings.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.blackboard_remappings.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/blackboard_remappings.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.blackboard_remappings
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-blackboard-remappings
 17
 18.. figure:: images/blackboard_remappings.png
 19   :align: center
 20
 21   Console Screenshot
 22"""
 23
 24##############################################################################
 25# Imports
 26##############################################################################
 27
 28import argparse
 29
 30import py_trees
 31import py_trees.console as console
 32
 33##############################################################################
 34# Classes
 35##############################################################################
 36
 37
 38def description() -> str:
 39    """
 40    Print description and usage information about the program.
 41
 42    Returns:
 43       the program description string
 44    """
 45    content = "Demonstrates usage of blackbord remappings.\n"
 46    content += "\n"
 47    content += "Demonstration is via an exemplar behaviour making use of remappings..\n"
 48
 49    if py_trees.console.has_colours:
 50        banner_line = console.green + "*" * 79 + "\n" + console.reset
 51        s = banner_line
 52        s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
 53        s += banner_line
 54        s += "\n"
 55        s += content
 56        s += "\n"
 57        s += banner_line
 58    else:
 59        s = content
 60    return s
 61
 62
 63def epilog() -> str | None:
 64    """
 65    Print a noodly epilog for --help.
 66
 67    Returns:
 68       the noodly message
 69    """
 70    if py_trees.console.has_colours:
 71        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 72    else:
 73        return None
 74
 75
 76def command_line_argument_parser() -> argparse.ArgumentParser:
 77    """
 78    Process command line arguments.
 79
 80    Returns:
 81        the argument parser
 82    """
 83    parser = argparse.ArgumentParser(
 84        description=description(),
 85        epilog=epilog(),
 86        formatter_class=argparse.RawDescriptionHelpFormatter,
 87    )
 88    return parser
 89
 90
 91class Remap(py_trees.behaviour.Behaviour):
 92    """Custom writer that submits a more complicated variable to the blackboard."""
 93
 94    def __init__(self, name: str, remap_to: dict[str, str]):
 95        """
 96        Set up the blackboard and remap variables.
 97
 98        Args:
 99            name: behaviour name
100            remap_to: remappings (from variable name to variable name)
101        """
102        super().__init__(name=name)
103        self.logger.debug(f"{self.__class__.__name__}.__init__()")
104        self.blackboard = self.attach_blackboard_client()
105        self.blackboard.register_key(
106            key="/foo/bar/wow",
107            access=py_trees.common.Access.WRITE,
108            remap_to=remap_to["/foo/bar/wow"],
109        )
110
111    def update(self) -> py_trees.common.Status:
112        """Write a dictionary to the blackboard.
113
114        This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
115        """
116        self.logger.debug(f"{self.__class__.__name__}.update()")
117        self.blackboard.foo.bar.wow = "colander"
118
119        return py_trees.common.Status.SUCCESS
120
121
122##############################################################################
123# Main
124##############################################################################
125
126
127def main() -> None:
128    """Entry point for the demo script."""
129    _ = command_line_argument_parser().parse_args()  # configuration only, no arg processing
130    print(description())
131    py_trees.logging.level = py_trees.logging.Level.DEBUG
132    py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
133    root = Remap(name="Remap", remap_to={"/foo/bar/wow": "/parameters/wow"})
134
135    ####################
136    # Execute
137    ####################
138    root.tick_once()
139    print(root.blackboard)
140    print(py_trees.display.unicode_blackboard())
141    print(py_trees.display.unicode_blackboard_activity_stream())

py-trees-demo-context-switching

A py_trees demo.

Demonstrates context switching with parallels and sequences.

A context switching behaviour is run in parallel with a work sequence. Switching the context occurs in the initialise() and terminate() methods of the context switching behaviour. Note that whether the sequence results in failure or success, the context switch behaviour will always call the terminate() method to restore the context. It will also call terminate() to restore the context in the event of a higher priority parent cancelling this parallel subtree.

usage: py-trees-demo-context-switching [-h] [-r]

Named Arguments

-r, --render

render dot tree to file

Default: False

digraph parallel {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Parallel [fillcolor=gold, fontcolor=black, fontsize=11, shape=note, style=filled];
Context [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Parallel -> Context;
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Parallel -> Sequence;
"Action 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 1";
"Action 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Action 2";
}
_images/context_switching.gif
class py_trees.demos.context_switching.ContextSwitch(name: str = 'ContextSwitch')[source]

Bases: Behaviour

An example of a context switching class.

This class sets (in initialise()) and restores a context (in terminate()). Use in parallel with a sequence/subtree that does the work while in this context.

Attention

Simply setting a pair of behaviours (set and reset context) on either end of a sequence will not suffice for context switching. In the case that one of the work behaviours in the sequence fails, the final reset context switch will never trigger.

Parameters:

name (str) –

__init__(name: str = 'ContextSwitch')[source]

Initialise with a behaviour name.

Parameters:

name (str) –

initialise() None[source]

Backup and set a new context.

Return type:

None

terminate(new_status: Status) None[source]

Restore the context with the previously backed up context.

Parameters:

new_status (Status) –

Return type:

None

update() Status[source]

Just returns RUNNING while it waits for other activities to finish.

Return type:

Status

py_trees.demos.context_switching.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.context_switching.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.context_switching.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.context_switching.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.context_switching.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/contex_switching.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.context_switching
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-context-switching
 17
 18.. graphviz:: dot/demo-context_switching.dot
 19
 20.. image:: images/context_switching.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import sys
 29import time
 30
 31import py_trees
 32import py_trees.console as console
 33
 34##############################################################################
 35# Classes
 36##############################################################################
 37
 38
 39def description() -> str:
 40    """
 41    Print description and usage information about the program.
 42
 43    Returns:
 44       the program description string
 45    """
 46    content = "Demonstrates context switching with parallels and sequences.\n"
 47    content += "\n"
 48    content += "A context switching behaviour is run in parallel with a work sequence.\n"
 49    content += "Switching the context occurs in the initialise() and terminate() methods\n"
 50    content += "of the context switching behaviour. Note that whether the sequence results\n"
 51    content += "in failure or success, the context switch behaviour will always call the\n"
 52    content += "terminate() method to restore the context. It will also call terminate()\n"
 53    content += "to restore the context in the event of a higher priority parent cancelling\n"
 54    content += "this parallel subtree.\n"
 55    if py_trees.console.has_colours:
 56        banner_line = console.green + "*" * 79 + "\n" + console.reset
 57        s = banner_line
 58        s += console.bold_white + "Context Switching".center(79) + "\n" + console.reset
 59        s += banner_line
 60        s += "\n"
 61        s += content
 62        s += "\n"
 63        s += banner_line
 64    else:
 65        s = content
 66    return s
 67
 68
 69def epilog() -> str | None:
 70    """
 71    Print a noodly epilog for --help.
 72
 73    Returns:
 74       the noodly message
 75    """
 76    if py_trees.console.has_colours:
 77        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 78    else:
 79        return None
 80
 81
 82def command_line_argument_parser() -> argparse.ArgumentParser:
 83    """
 84    Process command line arguments.
 85
 86    Returns:
 87        the argument parser
 88    """
 89    parser = argparse.ArgumentParser(
 90        description=description(),
 91        epilog=epilog(),
 92        formatter_class=argparse.RawDescriptionHelpFormatter,
 93    )
 94    parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 95    return parser
 96
 97
 98class ContextSwitch(py_trees.behaviour.Behaviour):
 99    """
100    An example of a context switching class.
101
102    This class sets (in ``initialise()``)
103    and restores a context (in ``terminate()``). Use in parallel with a
104    sequence/subtree that does the work while in this context.
105
106    .. attention:: Simply setting a pair of behaviours (set and reset context) on
107        either end of a sequence will not suffice for context switching. In the case
108        that one of the work behaviours in the sequence fails, the final reset context
109        switch will never trigger.
110    """
111
112    def __init__(self, name: str = "ContextSwitch"):
113        """Initialise with a behaviour name."""
114        super().__init__(name)
115        self.feedback_message = "no context"
116
117    def initialise(self) -> None:
118        """Backup and set a new context."""
119        self.logger.debug(f"{self.__class__.__name__}.initialise()[switch context]")
120        # Some actions that:
121        #   1. retrieve the current context from somewhere
122        #   2. cache the context internally
123        #   3. apply a new context
124        self.feedback_message = "new context"
125
126    def update(self) -> py_trees.common.Status:
127        """Just returns RUNNING while it waits for other activities to finish."""
128        self.logger.debug(f"{self.__class__.__name__}.update()[RUNNING][{self.feedback_message}]")
129        return py_trees.common.Status.RUNNING
130
131    def terminate(self, new_status: py_trees.common.Status) -> None:
132        """Restore the context with the previously backed up context."""
133        self.logger.debug(f"{self.__class__.__name__}.terminate()[{self.status}->{new_status}][restore context]")
134        # Some actions that:
135        #   1. restore the cached context
136        self.feedback_message = "restored context"
137
138
139def create_root() -> py_trees.behaviour.Behaviour:
140    """
141    Create the root behaviour and its subtree.
142
143    Returns:
144        the root behaviour
145    """
146    root = py_trees.composites.Parallel(name="Parallel", policy=py_trees.common.ParallelPolicy.SuccessOnOne())
147    context_switch = ContextSwitch(name="Context")
148    sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
149    for job in ["Action 1", "Action 2"]:
150        success_after_two = py_trees.behaviours.StatusQueue(
151            name=job,
152            queue=[py_trees.common.Status.RUNNING, py_trees.common.Status.RUNNING],
153            eventually=py_trees.common.Status.SUCCESS,
154        )
155        sequence.add_child(success_after_two)
156    root.add_child(context_switch)
157    root.add_child(sequence)
158    return root
159
160
161##############################################################################
162# Main
163##############################################################################
164
165
166def main() -> None:
167    """Entry point for the demo script."""
168    args = command_line_argument_parser().parse_args()
169    print(description())
170    py_trees.logging.level = py_trees.logging.Level.DEBUG
171
172    root = create_root()
173
174    ####################
175    # Rendering
176    ####################
177    if args.render:
178        py_trees.display.render_dot_tree(root)
179        sys.exit()
180
181    ####################
182    # Execute
183    ####################
184    root.setup_with_descendants()
185    for i in range(1, 6):
186        try:
187            print(f"\n--------- Tick {i} ---------\n")
188            root.tick_once()
189            print("\n")
190            print(f"{py_trees.display.unicode_tree(root, show_status=True)}")
191            time.sleep(1.0)
192        except KeyboardInterrupt:
193            break
194    print("\n")

py-trees-demo-dot-graphs

A py_trees demo.

Renders a dot graph for a simple tree, with blackboxes.

usage: py-trees-demo-dot-graphs [-h]
                                [-l {all,fine_detail,detail,component,big_picture}]

Named Arguments

-l, --level

Possible choices: all, fine_detail, detail, component, big_picture

visibility level

Default: 'fine_detail'

digraph demo_dot_graphs_fine_detail {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Demo Dot Graphs fine_detail" [fillcolor=cyan, fontcolor=black, fontsize=11, shape=octagon, style=filled];
"BlackBox 1" [fillcolor=gray20, fontcolor=white, fontsize=11, shape=box, style=filled];
"Demo Dot Graphs fine_detail" -> "BlackBox 1";
Worker [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> Worker;
"Worker*" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> "Worker*";
"Worker**" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"BlackBox 1" -> "Worker**";
"Blackbox 3" [fillcolor=gray20, fontcolor=dodgerblue, fontsize=11, shape=box, style=filled];
"BlackBox 1" -> "Blackbox 3";
"Worker***" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker***";
"Worker****" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker****";
"Worker*****" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 3" -> "Worker*****";
"Blackbox 2" [fillcolor=gray20, fontcolor=lawngreen, fontsize=11, shape=box, style=filled];
"Demo Dot Graphs fine_detail" -> "Blackbox 2";
"Worker******" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker******";
"Worker*******" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker*******";
"Worker********" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
"Blackbox 2" -> "Worker********";
}
py_trees.demos.dot_graphs.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.dot_graphs.create_tree(level: str) Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Parameters:

level (str) –

Return type:

Behaviour

py_trees.demos.dot_graphs.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.dot_graphs.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.dot_graphs.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/dot_graphs.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.dot_graphs
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-dot-graphs
 17
 18.. graphviz:: dot/demo-dot-graphs.dot
 19
 20"""
 21
 22##############################################################################
 23# Imports
 24##############################################################################
 25
 26import argparse
 27import subprocess
 28
 29import py_trees
 30import py_trees.console as console
 31
 32##############################################################################
 33# Classes
 34##############################################################################
 35
 36
 37def description() -> str:
 38    """
 39    Print description and usage information about the program.
 40
 41    Returns:
 42       the program description string
 43    """
 44    name = "py-trees-demo-dot-graphs"
 45    content = "Renders a dot graph for a simple tree, with blackboxes.\n"
 46    if py_trees.console.has_colours:
 47        banner_line = console.green + "*" * 79 + "\n" + console.reset
 48        s = banner_line
 49        s += console.bold_white + "Dot Graphs".center(79) + "\n" + console.reset
 50        s += banner_line
 51        s += "\n"
 52        s += content
 53        s += "\n"
 54        s += console.white
 55        s += console.bold + "    Generate Full Dot Graph" + console.reset + "\n"
 56        s += "\n"
 57        s += console.cyan + f"        {name}" + console.reset + "\n"
 58        s += "\n"
 59        s += console.bold + "    With Varying Visibility Levels" + console.reset + "\n"
 60        s += "\n"
 61        s += console.cyan + f"        {name}" + console.yellow + " --level=all" + console.reset + "\n"
 62        s += console.cyan + f"        {name}" + console.yellow + " --level=detail" + console.reset + "\n"
 63        s += console.cyan + f"        {name}" + console.yellow + " --level=component" + console.reset + "\n"
 64        s += console.cyan + f"        {name}" + console.yellow + " --level=big_picture" + console.reset + "\n"
 65        s += "\n"
 66        s += banner_line
 67    else:
 68        s = content
 69    return s
 70
 71
 72def epilog() -> str | None:
 73    """
 74    Print a noodly epilog for --help.
 75
 76    Returns:
 77       the noodly message
 78    """
 79    if py_trees.console.has_colours:
 80        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 81    else:
 82        return None
 83
 84
 85def command_line_argument_parser() -> argparse.ArgumentParser:
 86    """
 87    Process command line arguments.
 88
 89    Returns:
 90        the argument parser
 91    """
 92    parser = argparse.ArgumentParser(
 93        description=description(),
 94        epilog=epilog(),
 95        formatter_class=argparse.RawDescriptionHelpFormatter,
 96    )
 97    parser.add_argument(
 98        "-l",
 99        "--level",
100        action="store",
101        default="fine_detail",
102        choices=["all", "fine_detail", "detail", "component", "big_picture"],
103        help="visibility level",
104    )
105    return parser
106
107
108def create_tree(level: str) -> py_trees.behaviour.Behaviour:
109    """
110    Create the root behaviour and its subtree.
111
112    Returns:
113        the root behaviour
114    """
115    root = py_trees.composites.Selector(name=f"Demo Dot Graphs {level}", memory=False)
116    first_blackbox = py_trees.composites.Sequence(name="BlackBox 1", memory=True)
117    first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
118    first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
119    first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
120    first_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.BIG_PICTURE
121    second_blackbox = py_trees.composites.Sequence(name="Blackbox 2", memory=True)
122    second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
123    second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
124    second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
125    second_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
126    third_blackbox = py_trees.composites.Sequence(name="Blackbox 3", memory=True)
127    third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
128    third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
129    third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
130    third_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.DETAIL
131    root.add_child(first_blackbox)
132    root.add_child(second_blackbox)
133    first_blackbox.add_child(third_blackbox)
134    return root
135
136
137##############################################################################
138# Main
139##############################################################################
140
141
142def main() -> None:
143    """Entry point for the demo script."""
144    args = command_line_argument_parser().parse_args()
145    args.enum_level = py_trees.common.string_to_visibility_level(args.level)
146    print(description())
147    py_trees.logging.level = py_trees.logging.Level.DEBUG
148
149    root = create_tree(args.level)
150    py_trees.display.render_dot_tree(root, args.enum_level)
151
152    if py_trees.utilities.which("xdot"):
153        try:
154            subprocess.call(["xdot", f"demo_dot_graphs_{args.level}.dot"])
155        except KeyboardInterrupt:
156            pass
157    else:
158        print("")
159        console.logerror("No xdot viewer found, skipping display [hint: sudo apt install xdot]")
160        print("")

py-trees-demo-either-or

A py_trees demo.

A demonstration of the ‘either_or’ idiom.

This behaviour tree pattern enables triggering of subtrees with equal priority (first in, first served).

EVENTS

  • 3 : joystick one enabled, task one starts

  • 5 : task one finishes

  • 6 : joystick two enabled, task two starts

  • 7 : joystick one enabled, task one ignored, task two continues

  • 8 : task two finishes

usage: py-trees-demo-either-or [-h] [-r | -i]

Named Arguments

-r, --render

render dot tree to file

Default: False

-i, --interactive

pause and wait for keypress at each tick

Default: False

digraph pastafarianism {
ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Root [label="Root\n--SuccessOnAll(-)--", shape=parallelogram, style=filled, fillcolor=gold, fontsize=9, fontcolor=black];
Reset [label=Reset, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> Reset;
"Joy1 - Disabled" [label="Joy1 - Disabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Reset -> "Joy1 - Disabled";
"Joy2 - Disabled" [label="Joy2 - Disabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Reset -> "Joy2 - Disabled";
"Joy1 Events" [label="Joy1 Events", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> "Joy1 Events";
FisR [label=FisR, shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
"Joy1 Events" -> FisR;
"Joystick 1" [label="Joystick 1", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
FisR -> "Joystick 1";
"Joy1 - Enabled" [label="Joy1 - Enabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Joy1 Events" -> "Joy1 - Enabled";
"Joy2 Events" [label="Joy2 Events", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Root -> "Joy2 Events";
"FisR*" [label="FisR*", shape=ellipse, style=filled, fillcolor=ghostwhite, fontsize=9, fontcolor=black];
"Joy2 Events" -> "FisR*";
"Joystick 2" [label="Joystick 2", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"FisR*" -> "Joystick 2";
"Joy2 - Enabled" [label="Joy2 - Enabled", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Joy2 Events" -> "Joy2 - Enabled";
Tasks [label=Tasks, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
Root -> Tasks;
EitherOr [label=EitherOr, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Tasks -> EitherOr;
XOR [label=XOR, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
EitherOr -> XOR;
Chooser [label=Chooser, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EitherOr -> Chooser;
"Option 1" [label="Option 1", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Chooser -> "Option 1";
"Enabled?" [label="Enabled?", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 1" -> "Enabled?";
"Task 1" [label="Task 1", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 1" -> "Task 1";
"Option 2" [label="Option 2", shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Chooser -> "Option 2";
"Enabled?*" [label="Enabled?*", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 2" -> "Enabled?*";
"Task 2" [label="Task 2", shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Option 2" -> "Task 2";
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Tasks -> Idle;
}
_images/either_or.gif
py_trees.demos.either_or.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.either_or.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.either_or.description(root: Behaviour) str[source]

Print description and usage information about the program.

Returns:

the program description string

Parameters:

root (Behaviour) –

Return type:

str

py_trees.demos.either_or.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.either_or.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees.demos.either_or.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]

Print data about the part of the tree visited.

Parameters:
  • snapshot_handler – gather data about the part of the tree visited

  • behaviour_tree (BehaviourTree) – tree to gather data from

  • snapshot_visitor (SnapshotVisitor) –

Return type:

None

py_trees.demos.either_or.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]

Print a banner with current tick count prior to ticking the tree.

Parameters:

behaviour_tree (BehaviourTree) – the tree to tick (used to fetch the count number)

Return type:

None

py_trees/demos/either_or.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.either_or
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-either-or
 17
 18.. graphviz:: dot/demo-either-or.dot
 19
 20.. image:: images/either_or.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import functools
 29import operator
 30import sys
 31import time
 32
 33import py_trees
 34import py_trees.console as console
 35
 36##############################################################################
 37# Classes
 38##############################################################################
 39
 40
 41def description(root: py_trees.behaviour.Behaviour) -> str:
 42    """
 43    Print description and usage information about the program.
 44
 45    Returns:
 46       the program description string
 47    """
 48    content = "A demonstration of the 'either_or' idiom.\n\n"
 49    content += "This behaviour tree pattern enables triggering of subtrees\n"
 50    content += "with equal priority (first in, first served).\n"
 51    content += "\n"
 52    content += "EVENTS\n"
 53    content += "\n"
 54    content += " -  3 : joystick one enabled, task one starts\n"
 55    content += " -  5 : task one finishes\n"
 56    content += " -  6 : joystick two enabled, task two starts\n"
 57    content += " -  7 : joystick one enabled, task one ignored, task two continues\n"
 58    content += " -  8 : task two finishes\n"
 59    content += "\n"
 60    if py_trees.console.has_colours:
 61        banner_line = console.green + "*" * 79 + "\n" + console.reset
 62        s = banner_line
 63        s += console.bold_white + "Either Or".center(79) + "\n" + console.reset
 64        s += banner_line
 65        s += "\n"
 66        s += content
 67        s += "\n"
 68        s += banner_line
 69    else:
 70        s = content
 71    return s
 72
 73
 74def epilog() -> str | None:
 75    """
 76    Print a noodly epilog for --help.
 77
 78    Returns:
 79       the noodly message
 80    """
 81    if py_trees.console.has_colours:
 82        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 83    else:
 84        return None
 85
 86
 87def command_line_argument_parser() -> argparse.ArgumentParser:
 88    """
 89    Process command line arguments.
 90
 91    Returns:
 92        the argument parser
 93    """
 94    parser = argparse.ArgumentParser(
 95        description=description(create_root()),
 96        epilog=epilog(),
 97        formatter_class=argparse.RawDescriptionHelpFormatter,
 98    )
 99    group = parser.add_mutually_exclusive_group()
100    group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
101    group.add_argument(
102        "-i",
103        "--interactive",
104        action="store_true",
105        help="pause and wait for keypress at each tick",
106    )
107    return parser
108
109
110def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
111    """Print a banner with current tick count prior to ticking the tree.
112
113    Args:
114       behaviour_tree: the tree to tick (used to fetch the count number)
115    """
116    print(f"\n--------- Run {behaviour_tree.count} ---------\n")
117
118
119def post_tick_handler(
120    snapshot_visitor: py_trees.visitors.SnapshotVisitor,
121    behaviour_tree: py_trees.trees.BehaviourTree,
122) -> None:
123    """
124    Print data about the part of the tree visited.
125
126    Args:
127        snapshot_handler: gather data about the part of the tree visited
128        behaviour_tree: tree to gather data from
129    """
130    print(
131        "\n"
132        + py_trees.display.unicode_tree(
133            root=behaviour_tree.root,
134            visited=snapshot_visitor.visited,
135            previously_visited=snapshot_visitor.previously_visited,
136        )
137    )
138    print(py_trees.display.unicode_blackboard())
139
140
141def create_root() -> py_trees.behaviour.Behaviour:
142    """
143    Create the root behaviour and its subtree.
144
145    Returns:
146        the root behaviour
147    """
148    trigger_one = py_trees.decorators.FailureIsRunning(
149        name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 1", n=4)
150    )
151    trigger_two = py_trees.decorators.FailureIsRunning(
152        name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 2", n=7)
153    )
154    enable_joystick_one = py_trees.behaviours.SetBlackboardVariable(
155        name="Joy1 - Enabled",
156        variable_name="joystick_one",
157        variable_value="enabled",
158        overwrite=True,
159    )
160    enable_joystick_two = py_trees.behaviours.SetBlackboardVariable(
161        name="Joy2 - Enabled",
162        variable_name="joystick_two",
163        variable_value="enabled",
164        overwrite=True,
165    )
166    reset_joystick_one = py_trees.behaviours.SetBlackboardVariable(
167        name="Joy1 - Disabled",
168        variable_name="joystick_one",
169        variable_value="disabled",
170        overwrite=True,
171    )
172    reset_joystick_two = py_trees.behaviours.SetBlackboardVariable(
173        name="Joy2 - Disabled",
174        variable_name="joystick_two",
175        variable_value="disabled",
176        overwrite=True,
177    )
178    task_one = py_trees.behaviours.TickCounter(
179        name="Task 1", duration=2, completion_status=py_trees.common.Status.SUCCESS
180    )
181    task_two = py_trees.behaviours.TickCounter(
182        name="Task 2", duration=2, completion_status=py_trees.common.Status.SUCCESS
183    )
184    idle = py_trees.behaviours.Running(name="Idle")
185    either_or = py_trees.idioms.either_or(
186        name="Either Or",
187        conditions=[
188            py_trees.common.ComparisonExpression("joystick_one", "enabled", operator.eq),
189            py_trees.common.ComparisonExpression("joystick_two", "enabled", operator.eq),
190        ],
191        subtrees=[task_one, task_two],
192        namespace="either_or",
193    )
194    root = py_trees.composites.Parallel(
195        name="Root",
196        policy=py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False),
197    )
198    reset = py_trees.composites.Sequence(name="Reset", memory=True)
199    reset.add_children([reset_joystick_one, reset_joystick_two])
200    joystick_one_events = py_trees.composites.Sequence(name="Joy1 Events", memory=True)
201    joystick_one_events.add_children([trigger_one, enable_joystick_one])
202    joystick_two_events = py_trees.composites.Sequence(name="Joy2 Events", memory=True)
203    joystick_two_events.add_children([trigger_two, enable_joystick_two])
204    tasks = py_trees.composites.Selector(name="Tasks", memory=False)
205    tasks.add_children([either_or, idle])
206    root.add_children([reset, joystick_one_events, joystick_two_events, tasks])
207    return root
208
209
210##############################################################################
211# Main
212##############################################################################
213
214
215def main() -> None:
216    """Entry point for the demo script."""
217    args = command_line_argument_parser().parse_args()
218    # py_trees.logging.level = py_trees.logging.Level.DEBUG
219    root = create_root()
220    print(description(root))
221
222    ####################
223    # Rendering
224    ####################
225    if args.render:
226        py_trees.display.render_dot_tree(root)
227        sys.exit()
228
229    ####################
230    # Tree Stewardship
231    ####################
232    behaviour_tree = py_trees.trees.BehaviourTree(root)
233    behaviour_tree.add_pre_tick_handler(pre_tick_handler)
234    behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
235    snapshot_visitor = py_trees.visitors.SnapshotVisitor()
236    behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
237    behaviour_tree.visitors.append(snapshot_visitor)
238    behaviour_tree.setup(timeout=15)
239
240    ####################
241    # Tick Tock
242    ####################
243    if args.interactive:
244        py_trees.console.read_single_keypress()
245    for _unused_i in range(1, 11):
246        try:
247            behaviour_tree.tick()
248            if args.interactive:
249                py_trees.console.read_single_keypress()
250            else:
251                time.sleep(0.5)
252        except KeyboardInterrupt:
253            break
254    print("\n")

py-trees-demo-eternal-guard

A py_trees demo.

A demonstration of the ‘eternal guard’ concept.

Two binary (F|S) conditional checks will fire every tick, thus providing a fail-fast mechanism for the long running sequence of tasks behind them.

usage: py-trees-demo-eternal-guard [-h] [-r | -i]

Named Arguments

-r, --render

render dot tree to file

Default: False

-i, --interactive

pause and wait for keypress at each tick

Default: False

digraph pastafarianism {
ordering=out;
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Eternal Guard" [fillcolor=orange, fontcolor=black, fontsize=9, label="Eternal Guard", shape=box, style=filled];
"Condition 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 1", shape=ellipse, style=filled];
"Eternal Guard" -> "Condition 1";
"Condition 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Condition 2", shape=ellipse, style=filled];
"Eternal Guard" -> "Condition 2";
"Task Sequence" [fillcolor=orange, fontcolor=black, fontsize=9, label="Ⓜ Task Sequence", shape=box, style=filled];
"Eternal Guard" -> "Task Sequence";
"Worker 1" [fillcolor=gray, fontcolor=black, fontsize=9, label="Worker 1", shape=ellipse, style=filled];
"Task Sequence" -> "Worker 1";
"Worker 2" [fillcolor=gray, fontcolor=black, fontsize=9, label="Worker 2", shape=ellipse, style=filled];
"Task Sequence" -> "Worker 2";
}
_images/eternal_guard.gif
py_trees.demos.eternal_guard.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.eternal_guard.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.eternal_guard.description(root: Behaviour) str[source]

Print description and usage information about the program.

Returns:

the program description string

Parameters:

root (Behaviour) –

Return type:

str

py_trees.demos.eternal_guard.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.eternal_guard.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees.demos.eternal_guard.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]

Print data about the part of the tree visited.

Parameters:
  • snapshot_handler – gather data about the part of the tree visited

  • behaviour_tree (BehaviourTree) – tree to gather data from

  • snapshot_visitor (SnapshotVisitor) –

Return type:

None

py_trees.demos.eternal_guard.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]

Print a banner with current tick count prior to ticking the tree.

Parameters:

behaviour_tree (BehaviourTree) – the tree to tick (used to fetch the count number)

Return type:

None

py_trees/demos/eternal_guard.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.eternal_guard
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-eternal-guard
 17
 18.. graphviz:: dot/demo-eternal-guard.dot
 19
 20.. image:: images/eternal_guard.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import functools
 29import sys
 30import time
 31
 32import py_trees
 33import py_trees.console as console
 34
 35##############################################################################
 36# Classes
 37##############################################################################
 38
 39
 40def description(root: py_trees.behaviour.Behaviour) -> str:
 41    """
 42    Print description and usage information about the program.
 43
 44    Returns:
 45       the program description string
 46    """
 47    content = "A demonstration of the 'eternal guard' concept.\n\n"
 48    content += "Two binary (F|S) conditional checks will fire every\n"
 49    content += "tick, thus providing a fail-fast mechanism for the\n"
 50    content += "long running sequence of tasks behind them.\n"
 51    content += "\n"
 52    if py_trees.console.has_colours:
 53        banner_line = console.green + "*" * 79 + "\n" + console.reset
 54        s = banner_line
 55        s += console.bold_white + "Eternal Guard".center(79) + "\n" + console.reset
 56        s += banner_line
 57        s += "\n"
 58        s += content
 59        s += "\n"
 60        s += py_trees.display.unicode_tree(root)
 61        s += "\n"
 62        s += banner_line
 63    else:
 64        s = content
 65    return s
 66
 67
 68def epilog() -> str | None:
 69    """
 70    Print a noodly epilog for --help.
 71
 72    Returns:
 73       the noodly message
 74    """
 75    if py_trees.console.has_colours:
 76        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 77    else:
 78        return None
 79
 80
 81def command_line_argument_parser() -> argparse.ArgumentParser:
 82    """
 83    Process command line arguments.
 84
 85    Returns:
 86        the argument parser
 87    """
 88    parser = argparse.ArgumentParser(
 89        description=description(create_root()),
 90        epilog=epilog(),
 91        formatter_class=argparse.RawDescriptionHelpFormatter,
 92    )
 93    group = parser.add_mutually_exclusive_group()
 94    group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 95    group.add_argument(
 96        "-i",
 97        "--interactive",
 98        action="store_true",
 99        help="pause and wait for keypress at each tick",
100    )
101    return parser
102
103
104def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
105    """Print a banner with current tick count prior to ticking the tree.
106
107    Args:
108       behaviour_tree: the tree to tick (used to fetch the count number)
109    """
110    print(f"\n--------- Run {behaviour_tree.count} ---------\n")
111
112
113def post_tick_handler(
114    snapshot_visitor: py_trees.visitors.SnapshotVisitor,
115    behaviour_tree: py_trees.trees.BehaviourTree,
116) -> None:
117    """
118    Print data about the part of the tree visited.
119
120    Args:
121        snapshot_handler: gather data about the part of the tree visited
122        behaviour_tree: tree to gather data from
123    """
124    print(
125        "\n"
126        + py_trees.display.unicode_tree(
127            root=behaviour_tree.root,
128            visited=snapshot_visitor.visited,
129            previously_visited=snapshot_visitor.previously_visited,
130        )
131    )
132    print(py_trees.display.unicode_blackboard())
133
134
135def create_root() -> py_trees.behaviour.Behaviour:
136    """
137    Create the root behaviour and its subtree.
138
139    Returns:
140        the root behaviour
141    """
142    eternal_guard = py_trees.composites.Sequence(name="Eternal Guard", memory=False)
143    condition_one = py_trees.behaviours.StatusQueue(
144        name="Condition 1",
145        queue=[
146            py_trees.common.Status.SUCCESS,
147            py_trees.common.Status.FAILURE,
148            py_trees.common.Status.SUCCESS,
149        ],
150        eventually=py_trees.common.Status.SUCCESS,
151    )
152    condition_two = py_trees.behaviours.StatusQueue(
153        name="Condition 2",
154        queue=[
155            py_trees.common.Status.SUCCESS,
156            py_trees.common.Status.SUCCESS,
157            py_trees.common.Status.FAILURE,
158        ],
159        eventually=py_trees.common.Status.SUCCESS,
160    )
161    task_sequence = py_trees.composites.Sequence(name="Task Sequence", memory=True)
162    task_one = py_trees.behaviours.Success(name="Worker 1")
163    task_two = py_trees.behaviours.Running(name="Worker 2")
164
165    eternal_guard.add_children([condition_one, condition_two, task_sequence])
166    task_sequence.add_children([task_one, task_two])
167    return eternal_guard
168
169
170##############################################################################
171# Main
172##############################################################################
173
174
175def main() -> None:
176    """Entry point for the demo script."""
177    args = command_line_argument_parser().parse_args()
178    # py_trees.logging.level = py_trees.logging.Level.DEBUG
179    root = create_root()
180    print(description(root))
181
182    ####################
183    # Rendering
184    ####################
185    if args.render:
186        py_trees.display.render_dot_tree(root)
187        sys.exit()
188
189    ####################
190    # Tree Stewardship
191    ####################
192    behaviour_tree = py_trees.trees.BehaviourTree(root)
193    behaviour_tree.add_pre_tick_handler(pre_tick_handler)
194    behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
195    snapshot_visitor = py_trees.visitors.SnapshotVisitor()
196    behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
197    behaviour_tree.visitors.append(snapshot_visitor)
198    behaviour_tree.setup(timeout=15)
199
200    ####################
201    # Tick Tock
202    ####################
203    if args.interactive:
204        py_trees.console.read_single_keypress()
205    for _unused_i in range(1, 11):
206        try:
207            behaviour_tree.tick()
208            if args.interactive:
209                py_trees.console.read_single_keypress()
210            else:
211                time.sleep(0.5)
212        except KeyboardInterrupt:
213            break
214    print("\n")

py-trees-demo-logging

A py_trees demo.

A demonstration of logging with trees.

This demo utilises a SnapshotVisitor to trigger a post-tick handler to dump a serialisation of the tree to a json log file.

This coupling of visitor and post-tick handler can be used for any kind of event handling - the visitor is the trigger and the post-tick handler the action. Aside from logging, the most common use case is to serialise the tree for messaging to a graphical, runtime monitor.

usage: py-trees-demo-logging [-h] [-r | -i]

Named Arguments

-r, --render

render dot tree to file

Default: False

-i, --interactive

pause and wait for keypress at each tick

Default: False

digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Logging [label=Logging, shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Logging -> EveryN;
Sequence [label=Sequence, shape=box, style=filled, fillcolor=gray20, fontsize=9, fontcolor=lawngreen];
Logging -> Sequence;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Guard;
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Periodic;
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Finisher;
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Logging -> Idle;
}
_images/logging.gif
py_trees.demos.logging.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.logging.create_tree() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.logging.description(root: Behaviour) str[source]

Print description and usage information about the program.

Returns:

the program description string

Parameters:

root (Behaviour) –

Return type:

str

py_trees.demos.logging.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.logging.logger(snapshot_visitor: DisplaySnapshotVisitor, behaviour_tree: BehaviourTree) None[source]

Log the tree (relevant parts thereof) to a yaml file.

Use as a post-tick handler for a tree.

Parameters:
Return type:

None

py_trees.demos.logging.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/logging.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.logging
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-logging
 17
 18.. graphviz:: dot/demo-logging.dot
 19
 20.. image:: images/logging.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import functools
 29import json
 30import sys
 31import time
 32import typing
 33
 34import py_trees
 35import py_trees.console as console
 36
 37##############################################################################
 38# Classes
 39##############################################################################
 40
 41
 42def description(root: py_trees.behaviour.Behaviour) -> str:
 43    """
 44    Print description and usage information about the program.
 45
 46    Returns:
 47       the program description string
 48    """
 49    content = "A demonstration of logging with trees.\n\n"
 50    content += "This demo utilises a SnapshotVisitor to trigger\n"
 51    content += "a post-tick handler to dump a serialisation of the\n"
 52    content += "tree to a json log file.\n"
 53    content += "\n"
 54    content += "This coupling of visitor and post-tick handler can be\n"
 55    content += "used for any kind of event handling - the visitor is the\n"
 56    content += "trigger and the post-tick handler the action. Aside from\n"
 57    content += "logging, the most common use case is to serialise the tree\n"
 58    content += "for messaging to a graphical, runtime monitor.\n"
 59    content += "\n"
 60    if py_trees.console.has_colours:
 61        banner_line = console.green + "*" * 79 + "\n" + console.reset
 62        s = banner_line
 63        s += console.bold_white + "Logging".center(79) + "\n" + console.reset
 64        s += banner_line
 65        s += "\n"
 66        s += content
 67        s += "\n"
 68        s += py_trees.display.unicode_tree(root)
 69        s += "\n"
 70        s += banner_line
 71    else:
 72        s = content
 73    return s
 74
 75
 76def epilog() -> str | None:
 77    """
 78    Print a noodly epilog for --help.
 79
 80    Returns:
 81       the noodly message
 82    """
 83    if py_trees.console.has_colours:
 84        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 85    else:
 86        return None
 87
 88
 89def command_line_argument_parser() -> argparse.ArgumentParser:
 90    """
 91    Process command line arguments.
 92
 93    Returns:
 94        the argument parser
 95    """
 96    parser = argparse.ArgumentParser(
 97        description=description(create_tree()),
 98        epilog=epilog(),
 99        formatter_class=argparse.RawDescriptionHelpFormatter,
100    )
101    group = parser.add_mutually_exclusive_group()
102    group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
103    group.add_argument(
104        "-i",
105        "--interactive",
106        action="store_true",
107        help="pause and wait for keypress at each tick",
108    )
109    return parser
110
111
112def logger(
113    snapshot_visitor: py_trees.visitors.DisplaySnapshotVisitor,
114    behaviour_tree: py_trees.trees.BehaviourTree,
115) -> None:
116    """Log the tree (relevant parts thereof) to a yaml file.
117
118    Use as a post-tick handler for a tree.
119    """
120    if snapshot_visitor.changed:
121        print(console.cyan + "Logging.......................yes\n" + console.reset)
122        tree_serialisation = {"tick": behaviour_tree.count, "nodes": []}
123        for node in behaviour_tree.root.iterate():
124            node_type_str = "Behaviour"
125            for behaviour_type in [
126                py_trees.composites.Sequence,
127                py_trees.composites.Selector,
128                py_trees.composites.Parallel,
129                py_trees.decorators.Decorator,
130            ]:
131                if isinstance(node, behaviour_type):
132                    node_type_str = behaviour_type.__name__
133            if node.tip() is not None:
134                node_tip = node.tip()
135                assert node_tip is not None  # help mypy
136                node_tip_id = str(node_tip.id)
137            else:
138                node_tip_id = "none"
139            node_snapshot = {
140                "name": node.name,
141                "id": str(node.id),
142                "parent_id": str(node.parent.id) if node.parent else "none",
143                "child_ids": [str(child.id) for child in node.children],
144                "tip_id": node_tip_id,
145                "class_name": str(node.__module__) + "." + str(type(node).__name__),
146                "type": node_type_str,
147                "status": node.status.value,
148                "message": node.feedback_message,
149                "is_active": True if node.id in snapshot_visitor.visited else False,
150            }
151            typing.cast(list, tree_serialisation["nodes"]).append(node_snapshot)
152        if behaviour_tree.count == 0:
153            with open("dump.json", "w+") as outfile:
154                json.dump(tree_serialisation, outfile, indent=4)
155        else:
156            with open("dump.json", "a") as outfile:
157                json.dump(tree_serialisation, outfile, indent=4)
158    else:
159        print(console.yellow + "Logging.......................no\n" + console.reset)
160
161
162def create_tree() -> py_trees.behaviour.Behaviour:
163    """
164    Create the root behaviour and its subtree.
165
166    Returns:
167        the root behaviour
168    """
169    every_n_success = py_trees.behaviours.SuccessEveryN("EveryN", 5)
170    sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
171    guard = py_trees.behaviours.Success("Guard")
172    periodic_success = py_trees.behaviours.Periodic("Periodic", 3)
173    finisher = py_trees.behaviours.Success("Finisher")
174    sequence.add_child(guard)
175    sequence.add_child(periodic_success)
176    sequence.add_child(finisher)
177    sequence.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
178    idle = py_trees.behaviours.Success("Idle")
179    root = py_trees.composites.Selector(name="Logging", memory=False)
180    root.add_child(every_n_success)
181    root.add_child(sequence)
182    root.add_child(idle)
183    return root
184
185
186##############################################################################
187# Main
188##############################################################################
189
190
191def main() -> None:
192    """Entry point for the demo script."""
193    args = command_line_argument_parser().parse_args()
194    py_trees.logging.level = py_trees.logging.Level.DEBUG
195    tree = create_tree()
196    print(description(tree))
197
198    ####################
199    # Rendering
200    ####################
201    if args.render:
202        py_trees.display.render_dot_tree(tree)
203        sys.exit()
204
205    ####################
206    # Tree Stewardship
207    ####################
208    behaviour_tree = py_trees.trees.BehaviourTree(tree)
209
210    debug_visitor = py_trees.visitors.DebugVisitor()
211    snapshot_visitor = py_trees.visitors.DisplaySnapshotVisitor()
212
213    behaviour_tree.visitors.append(debug_visitor)
214    behaviour_tree.visitors.append(snapshot_visitor)
215
216    behaviour_tree.add_post_tick_handler(functools.partial(logger, snapshot_visitor))
217
218    behaviour_tree.setup(timeout=15)
219
220    ####################
221    # Tick Tock
222    ####################
223    if args.interactive:
224        py_trees.console.read_single_keypress()
225    while True:
226        try:
227            behaviour_tree.tick()
228            if args.interactive:
229                py_trees.console.read_single_keypress()
230            else:
231                time.sleep(0.5)
232        except KeyboardInterrupt:
233            break
234    print("\n")

py-trees-demo-selector

A py_trees demo.

Higher priority switching and interruption in the children of a selector.

In this example the higher priority child is setup to fail initially, falling back to the continually running second child. On the third tick, the first child succeeds and cancels the hitherto running child.

usage: py-trees-demo-selector [-h] [-r]

Named Arguments

-r, --render

render dot tree to file

Default: False

digraph selector {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Selector [fillcolor=cyan, fontcolor=black, fontsize=11, shape=octagon, style=filled];
"After Two" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Selector -> "After Two";
Running [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Selector -> Running;
}
_images/selector.gif
py_trees.demos.selector.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.selector.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.selector.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.selector.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.selector.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/selector.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.selector
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-selector
 17
 18.. graphviz:: dot/demo-selector.dot
 19
 20.. image:: images/selector.gif
 21
 22"""
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import sys
 29import time
 30
 31import py_trees
 32import py_trees.console as console
 33
 34##############################################################################
 35# Classes
 36##############################################################################
 37
 38
 39def description() -> str:
 40    """
 41    Print description and usage information about the program.
 42
 43    Returns:
 44       the program description string
 45    """
 46    content = "Higher priority switching and interruption in the children of a selector.\n"
 47    content += "\n"
 48    content += "In this example the higher priority child is setup to fail initially,\n"
 49    content += "falling back to the continually running second child. On the third\n"
 50    content += "tick, the first child succeeds and cancels the hitherto running child.\n"
 51    if py_trees.console.has_colours:
 52        banner_line = console.green + "*" * 79 + "\n" + console.reset
 53        s = banner_line
 54        s += console.bold_white + "Selectors".center(79) + "\n" + console.reset
 55        s += banner_line
 56        s += "\n"
 57        s += content
 58        s += "\n"
 59        s += banner_line
 60    else:
 61        s = content
 62    return s
 63
 64
 65def epilog() -> str | None:
 66    """
 67    Print a noodly epilog for --help.
 68
 69    Returns:
 70       the noodly message
 71    """
 72    if py_trees.console.has_colours:
 73        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 74    else:
 75        return None
 76
 77
 78def command_line_argument_parser() -> argparse.ArgumentParser:
 79    """
 80    Process command line arguments.
 81
 82    Returns:
 83        the argument parser
 84    """
 85    parser = argparse.ArgumentParser(
 86        description=description(),
 87        epilog=epilog(),
 88        formatter_class=argparse.RawDescriptionHelpFormatter,
 89    )
 90    parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 91    return parser
 92
 93
 94def create_root() -> py_trees.behaviour.Behaviour:
 95    """
 96    Create the root behaviour and its subtree.
 97
 98    Returns:
 99        the root behaviour
100    """
101    root = py_trees.composites.Selector(name="Selector", memory=False)
102    ffs = py_trees.behaviours.StatusQueue(
103        name="FFS",
104        queue=[
105            py_trees.common.Status.FAILURE,
106            py_trees.common.Status.FAILURE,
107            py_trees.common.Status.SUCCESS,
108        ],
109        eventually=py_trees.common.Status.SUCCESS,
110    )
111    always_running = py_trees.behaviours.Running(name="Running")
112    root.add_children([ffs, always_running])
113    return root
114
115
116##############################################################################
117# Main
118##############################################################################
119
120
121def main() -> None:
122    """Entry point for the demo script."""
123    args = command_line_argument_parser().parse_args()
124    print(description())
125    py_trees.logging.level = py_trees.logging.Level.DEBUG
126
127    root = create_root()
128
129    ####################
130    # Rendering
131    ####################
132    if args.render:
133        py_trees.display.render_dot_tree(root)
134        sys.exit()
135
136    ####################
137    # Execute
138    ####################
139    root.setup_with_descendants()
140    for i in range(1, 4):
141        try:
142            print(f"\n--------- Tick {i} ---------\n")
143            root.tick_once()
144            print("\n")
145            print(py_trees.display.unicode_tree(root=root, show_status=True))
146            time.sleep(1.0)
147        except KeyboardInterrupt:
148            break
149    print("\n")

py-trees-demo-sequence

A py_trees demo.

Demonstrates sequences in action.

A sequence is populated with 2-tick jobs that are allowed to run through to completion.

usage: py-trees-demo-sequence [-h] [-r]

Named Arguments

-r, --render

render dot tree to file

Default: False

digraph sequence {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Sequence [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
"Job 1" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 1";
"Job 2" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 2";
"Job 3" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Sequence -> "Job 3";
}
_images/sequence.gif
py_trees.demos.sequence.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.sequence.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.sequence.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.sequence.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.sequence.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees/demos/sequence.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.sequence
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-sequence
 17
 18.. graphviz:: dot/demo-sequence.dot
 19
 20.. image:: images/sequence.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import sys
 29import time
 30
 31import py_trees
 32import py_trees.console as console
 33
 34##############################################################################
 35# Classes
 36##############################################################################
 37
 38
 39def description() -> str:
 40    """
 41    Print description and usage information about the program.
 42
 43    Returns:
 44       the program description string
 45    """
 46    content = "Demonstrates sequences in action.\n\n"
 47    content += "A sequence is populated with 2-tick jobs that are allowed to run through to\n"
 48    content += "completion.\n"
 49
 50    if py_trees.console.has_colours:
 51        banner_line = console.green + "*" * 79 + "\n" + console.reset
 52        s = banner_line
 53        s += console.bold_white + "Sequences".center(79) + "\n" + console.reset
 54        s += banner_line
 55        s += "\n"
 56        s += content
 57        s += "\n"
 58        s += banner_line
 59    else:
 60        s = content
 61    return s
 62
 63
 64def epilog() -> str | None:
 65    """
 66    Print a noodly epilog for --help.
 67
 68    Returns:
 69       the noodly message
 70    """
 71    if py_trees.console.has_colours:
 72        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 73    else:
 74        return None
 75
 76
 77def command_line_argument_parser() -> argparse.ArgumentParser:
 78    """
 79    Process command line arguments.
 80
 81    Returns:
 82        the argument parser
 83    """
 84    parser = argparse.ArgumentParser(
 85        description=description(),
 86        epilog=epilog(),
 87        formatter_class=argparse.RawDescriptionHelpFormatter,
 88    )
 89    parser.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 90    return parser
 91
 92
 93def create_root() -> py_trees.behaviour.Behaviour:
 94    """
 95    Create the root behaviour and its subtree.
 96
 97    Returns:
 98        the root behaviour
 99    """
100    root = py_trees.composites.Sequence(name="Sequence", memory=True)
101    for action in ["Action 1", "Action 2", "Action 3"]:
102        rssss = py_trees.behaviours.StatusQueue(
103            name=action,
104            queue=[
105                py_trees.common.Status.RUNNING,
106                py_trees.common.Status.SUCCESS,
107            ],
108            eventually=py_trees.common.Status.SUCCESS,
109        )
110        root.add_child(rssss)
111    return root
112
113
114##############################################################################
115# Main
116##############################################################################
117
118
119def main() -> None:
120    """Entry point for the demo script."""
121    args = command_line_argument_parser().parse_args()
122    print(description())
123    py_trees.logging.level = py_trees.logging.Level.DEBUG
124
125    root = create_root()
126
127    ####################
128    # Rendering
129    ####################
130    if args.render:
131        py_trees.display.render_dot_tree(root)
132        sys.exit()
133
134    ####################
135    # Execute
136    ####################
137    root.setup_with_descendants()
138    for i in range(1, 6):
139        try:
140            print(f"\n--------- Tick {i} ---------\n")
141            root.tick_once()
142            print("\n")
143            print(py_trees.display.unicode_tree(root=root, show_status=True))
144            time.sleep(1.0)
145        except KeyboardInterrupt:
146            break
147    print("\n")

py-trees-demo-tree-stewardship

A py_trees demo.

A demonstration of tree stewardship.

A slightly less trivial tree that uses a simple stdout pre-tick handler and both the debug and snapshot visitors for logging and displaying the state of the tree.

EVENTS

  • 3 : sequence switches from running to success

  • 4 : selector’s first child flicks to success once only

  • 8 : the fallback idler kicks in as everything else fails

  • 14 : the first child kicks in again, aborting a running sequence behind it

usage: py-trees-demo-tree-stewardship [-h]
                                      [-r | --render-with-blackboard-variables | -i]

Named Arguments

-r, --render

render dot tree to file

Default: False

--render-with-blackboard-variables

render dot tree to file with blackboard variables

Default: False

-i, --interactive

pause and wait for keypress at each tick

Default: False

digraph pastafarianism {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Demo Tree" [label="Demo Tree", shape=octagon, style=filled, fillcolor=cyan, fontsize=9, fontcolor=black];
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Demo Tree" -> EveryN;
Sequence [label=Sequence, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
"Demo Tree" -> Sequence;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Guard;
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Periodic;
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence -> Finisher;
subgraph  {
label=children_of_Sequence;
rank=same;
Guard [label=Guard, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Periodic [label=Periodic, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Finisher [label=Finisher, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
"Demo Tree" -> Idle;
subgraph  {
label="children_of_Demo Tree";
rank=same;
EveryN [label=EveryN, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
Sequence [label=Sequence, shape=box, style=filled, fillcolor=orange, fontsize=9, fontcolor=black];
Idle [label=Idle, shape=ellipse, style=filled, fillcolor=gray, fontsize=9, fontcolor=black];
}

count [label="count: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
count -> Finisher  [color=blue, constraint=False];
EveryN -> count  [color=blue, constraint=True];
period [label="period: -", shape=box, style=filled, color=blue, fillcolor=white, fontsize=8, fontcolor=blue, width=0, height=0, fixedsize=False];
period -> Finisher  [color=blue, constraint=False];
Periodic -> period  [color=blue, constraint=True];
}
_images/tree_stewardship.gif
class py_trees.demos.stewardship.Finisher[source]

Bases: Behaviour

Gathers blackboard data from other behaviours and prints a summary.

To be used at the end of the tree run.

__init__() None[source]

Set up the blackboard.

Return type:

None

update() Status[source]

Fetch blackboard variables and print a summary.

Returns:

Always returns SUCCESS.

Return type:

Status

class py_trees.demos.stewardship.PeriodicSuccess[source]

Bases: Periodic

Write the period from Periodic as a variable on the blackboard.

__init__() None[source]

Initialise Periodic and set up the blackboard.

Return type:

None

update() Status[source]

Run the Periodic update and write to blackboard.

Returns:

Returns the Status of Periodic

Return type:

Status

class py_trees.demos.stewardship.SuccessEveryN[source]

Bases: SuccessEveryN

Add a blackboard counter to SuccessEveryN.

__init__() None[source]

Set up the blackboard.

Return type:

None

update() Status[source]

Run the SuccessEveryN update and write to blackboard.

Returns:

Returns the Status of SuccessEveryN

Return type:

Status

py_trees.demos.stewardship.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.stewardship.create_tree() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.stewardship.description() str[source]

Print description and usage information about the program.

Returns:

the program description string

Return type:

str

py_trees.demos.stewardship.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.stewardship.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees.demos.stewardship.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]

Generate a simple pre-tick banner printing to stdout.

Parameters:

behaviour_tree (BehaviourTree) –

Return type:

None

py_trees/demos/stewardship.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.stewardship
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-tree-stewardship
 17
 18.. graphviz:: dot/demo-tree-stewardship.dot
 19
 20.. image:: images/tree_stewardship.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import sys
 29import time
 30
 31import py_trees
 32import py_trees.console as console
 33
 34##############################################################################
 35# Classes
 36##############################################################################
 37
 38
 39def description() -> str:
 40    """
 41    Print description and usage information about the program.
 42
 43    Returns:
 44       the program description string
 45    """
 46    content = "A demonstration of tree stewardship.\n\n"
 47    content += "A slightly less trivial tree that uses a simple stdout pre-tick handler\n"
 48    content += "and both the debug and snapshot visitors for logging and displaying\n"
 49    content += "the state of the tree.\n"
 50    content += "\n"
 51    content += "EVENTS\n"
 52    content += "\n"
 53    content += " -  3 : sequence switches from running to success\n"
 54    content += " -  4 : selector's first child flicks to success once only\n"
 55    content += " -  8 : the fallback idler kicks in as everything else fails\n"
 56    content += " - 14 : the first child kicks in again, aborting a running sequence behind it\n"
 57    content += "\n"
 58    if py_trees.console.has_colours:
 59        banner_line = console.green + "*" * 79 + "\n" + console.reset
 60        s = banner_line
 61        s += console.bold_white + "Trees".center(79) + "\n" + console.reset
 62        s += banner_line
 63        s += "\n"
 64        s += content
 65        s += "\n"
 66        s += banner_line
 67    else:
 68        s = content
 69    return s
 70
 71
 72def epilog() -> str | None:
 73    """
 74    Print a noodly epilog for --help.
 75
 76    Returns:
 77       the noodly message
 78    """
 79    if py_trees.console.has_colours:
 80        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 81    else:
 82        return None
 83
 84
 85def command_line_argument_parser() -> argparse.ArgumentParser:
 86    """
 87    Process command line arguments.
 88
 89    Returns:
 90        the argument parser
 91    """
 92    parser = argparse.ArgumentParser(
 93        description=description(),
 94        epilog=epilog(),
 95        formatter_class=argparse.RawDescriptionHelpFormatter,
 96    )
 97    group = parser.add_mutually_exclusive_group()
 98    group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
 99    group.add_argument(
100        "--render-with-blackboard-variables",
101        action="store_true",
102        help="render dot tree to file with blackboard variables",
103    )
104    group.add_argument(
105        "-i",
106        "--interactive",
107        action="store_true",
108        help="pause and wait for keypress at each tick",
109    )
110    return parser
111
112
113def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
114    """Generate a simple pre-tick banner printing to stdout."""
115    print(f"\n--------- Run {behaviour_tree.count} ---------\n")
116
117
118class SuccessEveryN(py_trees.behaviours.SuccessEveryN):
119    """Add a blackboard counter to :class:`~py_trees.behaviours.SuccessEveryN`."""
120
121    def __init__(self) -> None:
122        """Set up the blackboard."""
123        super().__init__(name="EveryN", n=5)
124        self.blackboard = self.attach_blackboard_client(name=self.name)
125        self.blackboard.register_key("count", access=py_trees.common.Access.WRITE)
126
127    def update(self) -> py_trees.common.Status:
128        """
129        Run the :class:`~py_trees.behaviours.SuccessEveryN` update and write to blackboard.
130
131        Returns:
132            Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.SuccessEveryN`
133        """
134        status = super().update()
135        self.blackboard.count = self.count
136        return status
137
138
139class PeriodicSuccess(py_trees.behaviours.Periodic):
140    """Write the period from :class:`~py_trees.behaviours.Periodic` as a variable on the blackboard."""
141
142    def __init__(self) -> None:
143        """Initialise :class:`~py_trees.behaviours.Periodic` and set up the blackboard."""
144        super().__init__(name="Periodic", n=3)
145        self.blackboard = self.attach_blackboard_client(name=self.name)
146        self.blackboard.register_key("period", access=py_trees.common.Access.WRITE)
147
148    def update(self) -> py_trees.common.Status:
149        """
150        Run the :class:`~py_trees.behaviours.Periodic` update and write to blackboard.
151
152        Returns:
153            Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.Periodic`
154        """
155        status = super().update()
156        self.blackboard.period = self.period
157        return status
158
159
160class Finisher(py_trees.behaviour.Behaviour):
161    """
162    Gathers blackboard data from other behaviours and prints a summary.
163
164    To be used at the end of the tree run.
165    """
166
167    def __init__(self) -> None:
168        """Set up the blackboard."""
169        super().__init__(name="Finisher")
170        self.blackboard = self.attach_blackboard_client(name=self.name)
171        self.blackboard.register_key("count", access=py_trees.common.Access.READ)
172        self.blackboard.register_key("period", access=py_trees.common.Access.READ)
173
174    def update(self) -> py_trees.common.Status:
175        """
176        Fetch blackboard variables and print a summary.
177
178        Returns:
179            Always returns :data:`~py_trees.common.Status.SUCCESS`.
180        """
181        print(console.green + "---------------------------" + console.reset)
182        print(console.bold + "        Finisher" + console.reset)
183        print(console.green + f"  Count : {self.blackboard.count}" + console.reset)
184        print(console.green + f"  Period: {self.blackboard.period}" + console.reset)
185        print(console.green + "---------------------------" + console.reset)
186        return py_trees.common.Status.SUCCESS
187
188
189def create_tree() -> py_trees.behaviour.Behaviour:
190    """
191    Create the root behaviour and its subtree.
192
193    Returns:
194        the root behaviour
195    """
196    every_n_success = SuccessEveryN()
197    sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
198    guard = py_trees.behaviours.Success("Guard")
199    periodic_success = PeriodicSuccess()
200    finisher = Finisher()
201    sequence.add_child(guard)
202    sequence.add_child(periodic_success)
203    sequence.add_child(finisher)
204    idle = py_trees.behaviours.Success("Idle")
205    root = py_trees.composites.Selector(name="Demo Tree", memory=False)
206    root.add_child(every_n_success)
207    root.add_child(sequence)
208    root.add_child(idle)
209    return root
210
211
212##############################################################################
213# Main
214##############################################################################
215
216
217def main() -> None:
218    """Entry point for the demo script."""
219    args = command_line_argument_parser().parse_args()
220    py_trees.logging.level = py_trees.logging.Level.DEBUG
221    tree = create_tree()
222    print(description())
223
224    ####################
225    # Rendering
226    ####################
227    if args.render:
228        py_trees.display.render_dot_tree(tree)
229        sys.exit()
230
231    if args.render_with_blackboard_variables:
232        py_trees.display.render_dot_tree(tree, with_blackboard_variables=True)
233        sys.exit()
234
235    ####################
236    # Tree Stewardship
237    ####################
238    py_trees.blackboard.Blackboard.enable_activity_stream(100)
239    behaviour_tree = py_trees.trees.BehaviourTree(tree)
240    behaviour_tree.add_pre_tick_handler(pre_tick_handler)
241    behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
242    behaviour_tree.visitors.append(
243        py_trees.visitors.DisplaySnapshotVisitor(display_blackboard=True, display_activity_stream=True)
244    )
245    behaviour_tree.setup(timeout=15)
246
247    ####################
248    # Tick Tock
249    ####################
250    if args.interactive:
251        py_trees.console.read_single_keypress()
252    while True:
253        try:
254            behaviour_tree.tick()
255            if args.interactive:
256                py_trees.console.read_single_keypress()
257            else:
258                time.sleep(0.5)
259        except KeyboardInterrupt:
260            break
261    print("\n")

py-trees-demo-pick-up-where-you-left-off

A py_trees demo.

A demonstration of the ‘pick up where you left off’ idiom.

A common behaviour tree pattern that allows you to resume work after being interrupted by a high priority interrupt.

EVENTS

  • 2 : task one done, task two running

  • 3 : high priority interrupt

  • 7 : task two restarts

  • 9 : task two done

usage: py-trees-demo-pick-up-where-you-left-off [-h] [-r | -i]

Named Arguments

-r, --render

render dot tree to file

Default: False

-i, --interactive

pause and wait for keypress at each tick

Default: False

digraph pick_up_where_you_left_off {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
"Pick Up Where You Left Off" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
"High Priority" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Pick Up Where You Left Off" -> "High Priority";
Tasks [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Pick Up Where You Left Off" -> Tasks;
"Do or Don't" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
Tasks -> "Do or Don't";
"Done?" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Do or Don't" -> "Done?";
Worker [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Do or Don't" -> Worker;
"Task 1" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Worker -> "Task 1";
"Mark\ntask_1_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Worker -> "Mark\ntask_1_done";
"Do or Don't*" [shape=octagon, style=filled, fillcolor=cyan, fontsize=11, fontcolor=black];
Tasks -> "Do or Don't*";
"Done?*" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Do or Don't*" -> "Done?*";
"Worker*" [shape=box, style=filled, fillcolor=orange, fontsize=11, fontcolor=black];
"Do or Don't*" -> "Worker*";
"Task 2" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Worker*" -> "Task 2";
"Mark\ntask_2_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
"Worker*" -> "Mark\ntask_2_done";
"Clear\ntask_1_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Tasks -> "Clear\ntask_1_done";
"Clear\ntask_2_done" [shape=ellipse, style=filled, fillcolor=gray, fontsize=11, fontcolor=black];
Tasks -> "Clear\ntask_2_done";
}
_images/pick_up_where_you_left_off.gif
py_trees.demos.pick_up_where_you_left_off.command_line_argument_parser() ArgumentParser[source]

Process command line arguments.

Returns:

the argument parser

Return type:

ArgumentParser

py_trees.demos.pick_up_where_you_left_off.create_root() Behaviour[source]

Create the root behaviour and its subtree.

Returns:

the root behaviour

Return type:

Behaviour

py_trees.demos.pick_up_where_you_left_off.description(root: Behaviour) str[source]

Print description and usage information about the program.

Returns:

the program description string

Parameters:

root (Behaviour) –

Return type:

str

py_trees.demos.pick_up_where_you_left_off.epilog() str | None[source]

Print a noodly epilog for –help.

Returns:

the noodly message

Return type:

str | None

py_trees.demos.pick_up_where_you_left_off.main() None[source]

Entry point for the demo script.

Return type:

None

py_trees.demos.pick_up_where_you_left_off.post_tick_handler(snapshot_visitor: SnapshotVisitor, behaviour_tree: BehaviourTree) None[source]

Print an ascii tree with the current snapshot status.

Parameters:
Return type:

None

py_trees.demos.pick_up_where_you_left_off.pre_tick_handler(behaviour_tree: BehaviourTree) None[source]

Print a banner immediately before every tick of the tree.

Parameters:

behaviour_tree (BehaviourTree) – the tree custodian

Return type:

None

py_trees/demos/pick_up_where_you_left_off.py
  1#!/usr/bin/env python
  2#
  3# License: BSD
  4#   https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
  5#
  6##############################################################################
  7# Documentation
  8##############################################################################
  9
 10"""
 11A py_trees demo.
 12
 13.. argparse::
 14   :module: py_trees.demos.pick_up_where_you_left_off
 15   :func: command_line_argument_parser
 16   :prog: py-trees-demo-pick-up-where-you-left-off
 17
 18.. graphviz:: dot/pick_up_where_you_left_off.dot
 19
 20.. image:: images/pick_up_where_you_left_off.gif
 21"""
 22
 23##############################################################################
 24# Imports
 25##############################################################################
 26
 27import argparse
 28import functools
 29import sys
 30import time
 31
 32import py_trees
 33import py_trees.console as console
 34
 35##############################################################################
 36# Classes
 37##############################################################################
 38
 39
 40def description(root: py_trees.behaviour.Behaviour) -> str:
 41    """
 42    Print description and usage information about the program.
 43
 44    Returns:
 45       the program description string
 46    """
 47    content = "A demonstration of the 'pick up where you left off' idiom.\n\n"
 48    content += "A common behaviour tree pattern that allows you to resume\n"
 49    content += "work after being interrupted by a high priority interrupt.\n"
 50    content += "\n"
 51    content += "EVENTS\n"
 52    content += "\n"
 53    content += " -  2 : task one done, task two running\n"
 54    content += " -  3 : high priority interrupt\n"
 55    content += " -  7 : task two restarts\n"
 56    content += " -  9 : task two done\n"
 57    content += "\n"
 58    if py_trees.console.has_colours:
 59        banner_line = console.green + "*" * 79 + "\n" + console.reset
 60        s = banner_line
 61        s += console.bold_white + "Pick Up Where you Left Off".center(79) + "\n" + console.reset
 62        s += banner_line
 63        s += "\n"
 64        s += content
 65        s += "\n"
 66        s += py_trees.display.unicode_tree(root)
 67        s += "\n"
 68        s += banner_line
 69    else:
 70        s = content
 71    return s
 72
 73
 74def epilog() -> str | None:
 75    """
 76    Print a noodly epilog for --help.
 77
 78    Returns:
 79       the noodly message
 80    """
 81    if py_trees.console.has_colours:
 82        return console.cyan + "And his noodly appendage reached forth to tickle the blessed...\n" + console.reset
 83    else:
 84        return None
 85
 86
 87def command_line_argument_parser() -> argparse.ArgumentParser:
 88    """
 89    Process command line arguments.
 90
 91    Returns:
 92        the argument parser
 93    """
 94    parser = argparse.ArgumentParser(
 95        description=description(create_root()),
 96        epilog=epilog(),
 97        formatter_class=argparse.RawDescriptionHelpFormatter,
 98    )
 99    group = parser.add_mutually_exclusive_group()
100    group.add_argument("-r", "--render", action="store_true", help="render dot tree to file")
101    group.add_argument(
102        "-i",
103        "--interactive",
104        action="store_true",
105        help="pause and wait for keypress at each tick",
106    )
107    return parser
108
109
110def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
111    """Print a banner immediately before every tick of the tree.
112
113    Args:
114        behaviour_tree (:class:`~py_trees.trees.BehaviourTree`): the tree custodian
115
116    """
117    print(f"\n--------- Run {behaviour_tree.count} ---------\n")
118
119
120def post_tick_handler(
121    snapshot_visitor: py_trees.visitors.SnapshotVisitor,
122    behaviour_tree: py_trees.trees.BehaviourTree,
123) -> None:
124    """Print an ascii tree with the current snapshot status."""
125    print(
126        "\n"
127        + py_trees.display.unicode_tree(
128            root=behaviour_tree.root,
129            visited=snapshot_visitor.visited,
130            previously_visited=snapshot_visitor.previously_visited,
131        )
132    )
133
134
135def create_root() -> py_trees.behaviour.Behaviour:
136    """
137    Create the root behaviour and its subtree.
138
139    Returns:
140        the root behaviour
141    """
142    task_one = py_trees.behaviours.StatusQueue(
143        name="Task 1",
144        queue=[
145            py_trees.common.Status.RUNNING,
146            py_trees.common.Status.RUNNING,
147        ],
148        eventually=py_trees.common.Status.SUCCESS,
149    )
150    task_two = py_trees.behaviours.StatusQueue(
151        name="Task 2",
152        queue=[
153            py_trees.common.Status.RUNNING,
154            py_trees.common.Status.RUNNING,
155        ],
156        eventually=py_trees.common.Status.SUCCESS,
157    )
158    high_priority_interrupt = py_trees.decorators.RunningIsFailure(
159        name="Running is Failure",
160        child=py_trees.behaviours.Periodic(name="High Priority", n=3),
161    )
162    piwylo = py_trees.idioms.pick_up_where_you_left_off(name="Pick Up\nWhere You\nLeft Off", tasks=[task_one, task_two])
163    root = py_trees.composites.Selector(name="Root", memory=False)
164    root.add_children([high_priority_interrupt, piwylo])
165
166    return root
167
168
169##############################################################################
170# Main
171##############################################################################
172
173
174def main() -> None:
175    """Entry point for the demo script."""
176    args = command_line_argument_parser().parse_args()
177    py_trees.logging.level = py_trees.logging.Level.DEBUG
178    root = create_root()
179    print(description(root))
180
181    ####################
182    # Rendering
183    ####################
184    if args.render:
185        py_trees.display.render_dot_tree(root)
186        sys.exit()
187
188    ####################
189    # Tree Stewardship
190    ####################
191    behaviour_tree = py_trees.trees.BehaviourTree(root)
192    behaviour_tree.add_pre_tick_handler(pre_tick_handler)
193    behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
194    snapshot_visitor = py_trees.visitors.SnapshotVisitor()
195    behaviour_tree.add_post_tick_handler(functools.partial(post_tick_handler, snapshot_visitor))
196    behaviour_tree.visitors.append(snapshot_visitor)
197    behaviour_tree.setup(timeout=15)
198
199    ####################
200    # Tick Tock
201    ####################
202    if args.interactive:
203        py_trees.console.read_single_keypress()
204    for _unused_i in range(1, 11):
205        try:
206            behaviour_tree.tick()
207            if args.interactive:
208                py_trees.console.read_single_keypress()
209            else:
210                time.sleep(0.5)
211        except KeyboardInterrupt:
212            break
213    print("\n")