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]
- 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) –
- setup(**kwargs: int) 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 (int) –
- Return type
None
- py_trees.demos.action.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.action.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
- 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
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 += (
51 "* Kickstarts new goals with the external process in the initialise() method\n"
52 )
53 content += "* Monitors the ongoing goal status in the update() method\n"
54 content += (
55 "* Determines RUNNING/SUCCESS pending feedback from the external process\n"
56 )
57
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 + "Action Behaviour".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() -> typing.Optional[str]:
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 (
81 console.cyan
82 + "And his noodly appendage reached forth to tickle the blessed...\n"
83 + console.reset
84 )
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 return argparse.ArgumentParser(
97 description=description(),
98 epilog=epilog(),
99 formatter_class=argparse.RawDescriptionHelpFormatter,
100 )
101
102
103def planning(pipe_connection: multiprocessing.connection.Connection) -> None:
104 """Emulate a (potentially) long running external process.
105
106 Args:
107 pipe_connection: connection to the planning process
108 """
109 idle = True
110 percentage_complete = 0
111 try:
112 while True:
113 if pipe_connection.poll():
114 pipe_connection.recv()
115 percentage_complete = 0
116 idle = False
117 if not idle:
118 percentage_complete += 10
119 pipe_connection.send([percentage_complete])
120 if percentage_complete == 100:
121 idle = True
122 time.sleep(0.5)
123 except KeyboardInterrupt:
124 pass
125
126
127class Action(py_trees.behaviour.Behaviour):
128 """Demonstrates the at-a-distance style action behaviour.
129
130 This behaviour connects to a separately running process
131 (initiated in setup()) and proceeeds to work with that subprocess to
132 initiate a task and monitor the progress of that task at each tick
133 until completed. While the task is running the behaviour returns
134 :data:`~py_trees.common.Status.RUNNING`.
135
136 On completion, the the behaviour returns with success or failure
137 (depending on success or failure of the task itself).
138
139 Key point - this behaviour itself should not be doing any work!
140 """
141
142 def __init__(self, name: str):
143 """Configure the name of the behaviour."""
144 super(Action, self).__init__(name)
145 self.logger.debug("%s.__init__()" % (self.__class__.__name__))
146
147 def setup(self, **kwargs: int) -> None:
148 """Kickstart the separate process this behaviour will work with.
149
150 Ordinarily this process will be already running. In this case,
151 setup is usually just responsible for verifying it exists.
152 """
153 self.logger.debug(
154 "%s.setup()->connections to an external process" % (self.__class__.__name__)
155 )
156 self.parent_connection, self.child_connection = multiprocessing.Pipe()
157 self.planning = multiprocessing.Process(
158 target=planning, args=(self.child_connection,)
159 )
160 atexit.register(self.planning.terminate)
161 self.planning.start()
162
163 def initialise(self) -> None:
164 """Reset a counter variable."""
165 self.logger.debug(
166 "%s.initialise()->sending new goal" % (self.__class__.__name__)
167 )
168 self.parent_connection.send(["new goal"])
169 self.percentage_completion = 0
170
171 def update(self) -> py_trees.common.Status:
172 """Increment the counter, monitor and decide on a new status."""
173 new_status = py_trees.common.Status.RUNNING
174 if self.parent_connection.poll():
175 self.percentage_completion = self.parent_connection.recv().pop()
176 if self.percentage_completion == 100:
177 new_status = py_trees.common.Status.SUCCESS
178 if new_status == py_trees.common.Status.SUCCESS:
179 self.feedback_message = "Processing finished"
180 self.logger.debug(
181 "%s.update()[%s->%s][%s]"
182 % (
183 self.__class__.__name__,
184 self.status,
185 new_status,
186 self.feedback_message,
187 )
188 )
189 else:
190 self.feedback_message = "{0}%".format(self.percentage_completion)
191 self.logger.debug(
192 "%s.update()[%s][%s]"
193 % (self.__class__.__name__, self.status, self.feedback_message)
194 )
195 return new_status
196
197 def terminate(self, new_status: py_trees.common.Status) -> None:
198 """Nothing to clean up in this example."""
199 self.logger.debug(
200 "%s.terminate()[%s->%s]"
201 % (self.__class__.__name__, self.status, new_status)
202 )
203
204
205##############################################################################
206# Main
207##############################################################################
208
209
210def main() -> None:
211 """Entry point for the demo script."""
212 command_line_argument_parser().parse_args()
213
214 print(description())
215
216 py_trees.logging.level = py_trees.logging.Level.DEBUG
217
218 action = Action(name="Action")
219 action.setup()
220 try:
221 for _unused_i in range(0, 12):
222 action.tick_once()
223 time.sleep(0.5)
224 print("\n")
225 except KeyboardInterrupt:
226 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
- 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) –
- setup(**kwargs: int) None [source]
No delayed initialisation required for this example.
- Parameters
kwargs (int) –
- Return type
None
- py_trees.demos.lifecycle.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.lifecycle.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
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 += (
46 "This behaviour will count from 1 to 3 and then reset and repeat. As it does\n"
47 )
48 content += "so, it logs and displays the methods as they are called - construction, setup,\n"
49 content += "initialisation, ticking and termination.\n"
50 if py_trees.console.has_colours:
51 banner_line = console.green + "*" * 79 + "\n" + console.reset
52 s = banner_line
53 s += (
54 console.bold_white + "Behaviour Lifecycle".center(79) + "\n" + console.reset
55 )
56 s += banner_line
57 s += "\n"
58 s += content
59 s += "\n"
60 s += banner_line
61 else:
62 s = content
63 return s
64
65
66def epilog() -> typing.Optional[str]:
67 """
68 Print a noodly epilog for --help.
69
70 Returns:
71 the noodly message
72 """
73 if py_trees.console.has_colours:
74 return (
75 console.cyan
76 + "And his noodly appendage reached forth to tickle the blessed...\n"
77 + console.reset
78 )
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 return argparse.ArgumentParser(
91 description=description(),
92 epilog=epilog(),
93 formatter_class=argparse.RawDescriptionHelpFormatter,
94 )
95
96
97class Counter(py_trees.behaviour.Behaviour):
98 """Simple counting behaviour.
99
100 * Increments a counter from zero at each tick
101 * Finishes with success if the counter reaches three
102 * Resets the counter in the initialise() method.
103 """
104
105 def __init__(self, name: str = "Counter"):
106 """Configure the name of the behaviour."""
107 super(Counter, self).__init__(name)
108 self.logger.debug("%s.__init__()" % (self.__class__.__name__))
109
110 def setup(self, **kwargs: int) -> None:
111 """No delayed initialisation required for this example."""
112 self.logger.debug("%s.setup()" % (self.__class__.__name__))
113
114 def initialise(self) -> None:
115 """Reset a counter variable."""
116 self.logger.debug("%s.initialise()" % (self.__class__.__name__))
117 self.counter = 0
118
119 def update(self) -> py_trees.common.Status:
120 """Increment the counter and decide on a new status."""
121 self.counter += 1
122 new_status = (
123 py_trees.common.Status.SUCCESS
124 if self.counter == 3
125 else py_trees.common.Status.RUNNING
126 )
127 if new_status == py_trees.common.Status.SUCCESS:
128 self.feedback_message = (
129 "counting...{0} - phew, thats enough for today".format(self.counter)
130 )
131 else:
132 self.feedback_message = "still counting"
133 self.logger.debug(
134 "%s.update()[%s->%s][%s]"
135 % (self.__class__.__name__, self.status, new_status, self.feedback_message)
136 )
137 return new_status
138
139 def terminate(self, new_status: py_trees.common.Status) -> None:
140 """Nothing to clean up in this example."""
141 self.logger.debug(
142 "%s.terminate()[%s->%s]"
143 % (self.__class__.__name__, self.status, new_status)
144 )
145
146
147##############################################################################
148# Main
149##############################################################################
150
151
152def main() -> None:
153 """Entry point for the demo script."""
154 command_line_argument_parser().parse_args()
155
156 print(description())
157
158 py_trees.logging.level = py_trees.logging.Level.DEBUG
159
160 counter = Counter()
161 counter.setup()
162 try:
163 for _unused_i in range(0, 7):
164 counter.tick_once()
165 time.sleep(0.5)
166 print("\n")
167 except KeyboardInterrupt:
168 print("")
169 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
- class py_trees.demos.blackboard.BlackboardWriter(name: str)[source]
Bases:
Behaviour
Write some more interesting / complex types to the blacbkoard.
- Parameters
name (str) –
- class py_trees.demos.blackboard.Nested[source]
Bases:
object
A more complex object to interact with on the blackboard.
- __weakref__
list of weak references to the object (if defined)
- 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
- py_trees.demos.blackboard.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.blackboard.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.blackboard.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
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
35import typing
36
37import py_trees
38import py_trees.console as console
39
40##############################################################################
41# Classes
42##############################################################################
43
44
45def description() -> str:
46 """
47 Print description and usage information about the program.
48
49 Returns:
50 the program description string
51 """
52 content = "Demonstrates usage of the blackboard and related behaviours.\n"
53 content += "\n"
54 content += "A sequence is populated with a few behaviours that exercise\n"
55 content += "reading and writing on the Blackboard in interesting ways.\n"
56
57 if py_trees.console.has_colours:
58 banner_line = console.green + "*" * 79 + "\n" + console.reset
59 s = banner_line
60 s += console.bold_white + "Blackboard".center(79) + "\n" + console.reset
61 s += banner_line
62 s += "\n"
63 s += content
64 s += "\n"
65 s += banner_line
66 else:
67 s = content
68 return s
69
70
71def epilog() -> typing.Optional[str]:
72 """
73 Print a noodly epilog for --help.
74
75 Returns:
76 the noodly message
77 """
78 if py_trees.console.has_colours:
79 return (
80 console.cyan
81 + "And his noodly appendage reached forth to tickle the blessed...\n"
82 + console.reset
83 )
84 else:
85 return None
86
87
88def command_line_argument_parser() -> argparse.ArgumentParser:
89 """
90 Process command line arguments.
91
92 Returns:
93 the argument parser
94 """
95 parser = argparse.ArgumentParser(
96 description=description(),
97 epilog=epilog(),
98 formatter_class=argparse.RawDescriptionHelpFormatter,
99 )
100 render_group = parser.add_mutually_exclusive_group()
101 render_group.add_argument(
102 "-r", "--render", action="store_true", help="render dot tree to file"
103 )
104 render_group.add_argument(
105 "--render-with-blackboard-variables",
106 action="store_true",
107 help="render dot tree to file with blackboard variables",
108 )
109 return parser
110
111
112class Nested(object):
113 """A more complex object to interact with on the blackboard."""
114
115 def __init__(self) -> None:
116 """Initialise variables to some arbitrary defaults."""
117 self.foo = "bar"
118
119 def __str__(self) -> str:
120 return str({"foo": self.foo})
121
122
123class BlackboardWriter(py_trees.behaviour.Behaviour):
124 """Write some more interesting / complex types to the blacbkoard."""
125
126 def __init__(self, name: str):
127 """Set up the blackboard.
128
129 Args:
130 name: behaviour name
131 """
132 super().__init__(name=name)
133 self.blackboard = self.attach_blackboard_client()
134 self.blackboard.register_key(key="dude", access=py_trees.common.Access.READ)
135 self.blackboard.register_key(
136 key="spaghetti", access=py_trees.common.Access.WRITE
137 )
138
139 self.logger.debug("%s.__init__()" % (self.__class__.__name__))
140
141 def update(self) -> py_trees.common.Status:
142 """Write a dictionary to the blackboard.
143
144 This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
145 """
146 self.logger.debug("%s.update()" % (self.__class__.__name__))
147 try:
148 _ = self.blackboard.dude
149 except KeyError:
150 pass
151 try:
152 _ = self.blackboard.dudette
153 except AttributeError:
154 pass
155 try:
156 self.blackboard.dudette = "Jane"
157 except AttributeError:
158 pass
159 self.blackboard.spaghetti = {"type": "Carbonara", "quantity": 1}
160 self.blackboard.spaghetti = {"type": "Gnocchi", "quantity": 2}
161 try:
162 self.blackboard.set(
163 "spaghetti", {"type": "Bolognese", "quantity": 3}, overwrite=False
164 )
165 except AttributeError:
166 pass
167 return py_trees.common.Status.SUCCESS
168
169
170class ParamsAndState(py_trees.behaviour.Behaviour):
171 """Parameter and state storage on the blackboard.
172
173 This behaviour demonstrates the usage of namespaces and
174 multiple clients to perform getting and setting of
175 parameters and state in a concise and convenient manner.
176 """
177
178 def __init__(self, name: str):
179 """Set up separate blackboard clients for parameters and state.
180
181 Args:
182 name: behaviour name
183 """
184 super().__init__(name=name)
185 # namespaces can include the separator or may leave it out
186 # they can also be nested, e.g. /agent/state, /agent/parameters
187 self.parameters = self.attach_blackboard_client("Params", "parameters")
188 self.state = self.attach_blackboard_client("State", "state")
189 self.parameters.register_key(
190 key="default_speed", access=py_trees.common.Access.READ
191 )
192 self.state.register_key(
193 key="current_speed", access=py_trees.common.Access.WRITE
194 )
195
196 def initialise(self) -> None:
197 """Initialise speed from the stored parameter variable on the blackboard."""
198 try:
199 self.state.current_speed = self.parameters.default_speed
200 except KeyError as e:
201 raise RuntimeError(
202 "parameter 'default_speed' not found [{}]".format(str(e))
203 )
204
205 def update(self) -> py_trees.common.Status:
206 """
207 Check speed and either increment, or complete if it has reached a threshold.
208
209 Returns:
210 :data:`~py_trees.common.Status.RUNNING` if incrementing, :data:`~py_trees.common.Status.SUCCESS` otherwise.
211 """
212 if self.state.current_speed > 40.0:
213 return py_trees.common.Status.SUCCESS
214 else:
215 self.state.current_speed += 1.0
216 return py_trees.common.Status.RUNNING
217
218
219def create_root() -> py_trees.behaviour.Behaviour:
220 """
221 Create the root behaviour and it's subtree.
222
223 Returns:
224 the root behaviour
225 """
226 root = py_trees.composites.Sequence(name="Blackboard Demo", memory=True)
227 set_blackboard_variable = py_trees.behaviours.SetBlackboardVariable(
228 name="Set Nested",
229 variable_name="nested",
230 variable_value=Nested(),
231 overwrite=True,
232 )
233 write_blackboard_variable = BlackboardWriter(name="Writer")
234 check_blackboard_variable = py_trees.behaviours.CheckBlackboardVariableValue(
235 name="Check Nested Foo",
236 check=py_trees.common.ComparisonExpression(
237 variable="nested.foo", value="bar", operator=operator.eq
238 ),
239 )
240 params_and_state = ParamsAndState(name="ParamsAndState")
241 root.add_children(
242 [
243 set_blackboard_variable,
244 write_blackboard_variable,
245 check_blackboard_variable,
246 params_and_state,
247 ]
248 )
249 return root
250
251
252##############################################################################
253# Main
254##############################################################################
255
256
257def main() -> None:
258 """Entry point for the demo script."""
259 args = command_line_argument_parser().parse_args()
260 print(description())
261 py_trees.logging.level = py_trees.logging.Level.DEBUG
262 py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
263 blackboard = py_trees.blackboard.Client(name="Configuration")
264 blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
265 blackboard.register_key(
266 key="/parameters/default_speed", access=py_trees.common.Access.EXCLUSIVE_WRITE
267 )
268 blackboard.dude = "Bob"
269 blackboard.parameters.default_speed = 30.0
270
271 root = create_root()
272
273 ####################
274 # Rendering
275 ####################
276 if args.render:
277 py_trees.display.render_dot_tree(root, with_blackboard_variables=False)
278 sys.exit()
279 if args.render_with_blackboard_variables:
280 py_trees.display.render_dot_tree(root, with_blackboard_variables=True)
281 sys.exit()
282
283 ####################
284 # Execute
285 ####################
286 root.setup_with_descendants()
287 unset_blackboard = py_trees.blackboard.Client(name="Unsetter")
288 unset_blackboard.register_key(key="foo", access=py_trees.common.Access.WRITE)
289 print("\n--------- Tick 0 ---------\n")
290 root.tick_once()
291 print("\n")
292 print(py_trees.display.unicode_tree(root, show_status=True))
293 print("--------------------------\n")
294 print(py_trees.display.unicode_blackboard())
295 print("--------------------------\n")
296 print(py_trees.display.unicode_blackboard(display_only_key_metadata=True))
297 print("--------------------------\n")
298 unset_blackboard.unset("foo")
299 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]
- py_trees.demos.blackboard_namespaces.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.blackboard_namespaces.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
- py_trees.demos.blackboard_namespaces.epilog() Optional[str] [source]
Print a noodly epilog for –help.
- py_trees.demos.blackboard_namespaces.main() None [source]
Entry point for the demo script.
- Return type
None
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
29import typing
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 usage of blackboard namespaces.\n"
47 content += "\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() -> typing.Optional[str]:
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 (
72 console.cyan
73 + "And his noodly appendage reached forth to tickle the blessed...\n"
74 + console.reset
75 )
76 else:
77 return None
78
79
80def command_line_argument_parser() -> argparse.ArgumentParser:
81 """
82 Process command line arguments.
83
84 Returns:
85 the argument parser
86 """
87 parser = argparse.ArgumentParser(
88 description=description(),
89 epilog=epilog(),
90 formatter_class=argparse.RawDescriptionHelpFormatter,
91 )
92 return parser
93
94
95##############################################################################
96# Main
97##############################################################################
98
99
100def main() -> None:
101 """Entry point for the demo script."""
102 _ = (
103 command_line_argument_parser().parse_args()
104 ) # configuration only, no args to process
105 print(description())
106 print(
107 "-------------------------------------------------------------------------------"
108 )
109 print("$ py_trees.blackboard.Client(name='Blackboard')")
110 print("$ foo.register_key(key='dude', access=py_trees.common.Access.WRITE)")
111 print("$ foo.register_key(key='/dudette', access=py_trees.common.Access.WRITE)")
112 print("$ foo.register_key(key='/foo/bar/wow', access=py_trees.common.Access.WRITE)")
113 print(
114 "-------------------------------------------------------------------------------"
115 )
116 blackboard = py_trees.blackboard.Client(name="Blackboard")
117 blackboard.register_key(key="dude", access=py_trees.common.Access.WRITE)
118 blackboard.register_key(key="/dudette", access=py_trees.common.Access.WRITE)
119 blackboard.register_key(key="/foo/bar/wow", access=py_trees.common.Access.WRITE)
120 print(blackboard)
121 print(
122 "-------------------------------------------------------------------------------"
123 )
124 print("$ blackboard.dude = 'Bob'")
125 print("$ blackboard.dudette = 'Jade'")
126 print(
127 "-------------------------------------------------------------------------------"
128 )
129 blackboard.dude = "Bob"
130 blackboard.dudette = "Jade"
131 print(py_trees.display.unicode_blackboard())
132 print(
133 "-------------------------------------------------------------------------------"
134 )
135 print("$ blackboard.foo.bar.wow = 'foobar'")
136 print(
137 "-------------------------------------------------------------------------------"
138 )
139 blackboard.foo.bar.wow = "foobar"
140 print(py_trees.display.unicode_blackboard())
141 print(
142 "-------------------------------------------------------------------------------"
143 )
144 print("$ py_trees.blackboard.Client(name='Foo', namespace='foo')")
145 print("$ foo.register_key(key='awesome', access=py_trees.common.Access.WRITE)")
146 print("$ foo.register_key(key='/brilliant', access=py_trees.common.Access.WRITE)")
147 print("$ foo.register_key(key='/foo/clever', access=py_trees.common.Access.WRITE)")
148 print(
149 "-------------------------------------------------------------------------------"
150 )
151 foo = py_trees.blackboard.Client(name="Foo", namespace="foo")
152 foo.register_key(key="awesome", access=py_trees.common.Access.WRITE)
153 # TODO: should /brilliant be namespaced or go directly to root?
154 foo.register_key(key="/brilliant", access=py_trees.common.Access.WRITE)
155 # absolute names are ok, so long as they include the namespace
156 foo.register_key(key="/foo/clever", access=py_trees.common.Access.WRITE)
157 print(foo)
158 print(
159 "-------------------------------------------------------------------------------"
160 )
161 print("$ foo.awesome = True")
162 print("$ foo.set('/brilliant', False)")
163 print("$ foo.clever = True")
164 print(
165 "-------------------------------------------------------------------------------"
166 )
167 foo.awesome = True
168 # Only accessable via set since it's not in the namespace
169 foo.set("/brilliant", False)
170 # This will fail since it looks for the namespaced /foo/brilliant key
171 # foo.brilliant = False
172 foo.clever = True
173 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]
- 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.
- py_trees.demos.blackboard_remappings.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.blackboard_remappings.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
- py_trees.demos.blackboard_remappings.epilog() Optional[str] [source]
Print a noodly epilog for –help.
- py_trees.demos.blackboard_remappings.main() None [source]
Entry point for the demo script.
- Return type
None
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
29import typing
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 usage of blackbord remappings.\n"
47 content += "\n"
48 content += "Demonstration is via an exemplar behaviour making use of remappings..\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 + "Blackboard".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() -> typing.Optional[str]:
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 (
73 console.cyan
74 + "And his noodly appendage reached forth to tickle the blessed...\n"
75 + console.reset
76 )
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(),
90 epilog=epilog(),
91 formatter_class=argparse.RawDescriptionHelpFormatter,
92 )
93 return parser
94
95
96class Remap(py_trees.behaviour.Behaviour):
97 """Custom writer that submits a more complicated variable to the blackboard."""
98
99 def __init__(self, name: str, remap_to: typing.Dict[str, str]):
100 """
101 Set up the blackboard and remap variables.
102
103 Args:
104 name: behaviour name
105 remap_to: remappings (from variable name to variable name)
106 """
107 super().__init__(name=name)
108 self.logger.debug("%s.__init__()" % (self.__class__.__name__))
109 self.blackboard = self.attach_blackboard_client()
110 self.blackboard.register_key(
111 key="/foo/bar/wow",
112 access=py_trees.common.Access.WRITE,
113 remap_to=remap_to["/foo/bar/wow"],
114 )
115
116 def update(self) -> py_trees.common.Status:
117 """Write a dictionary to the blackboard.
118
119 This beaviour always returns :data:`~py_trees.common.Status.SUCCESS`.
120 """
121 self.logger.debug("%s.update()" % (self.__class__.__name__))
122 self.blackboard.foo.bar.wow = "colander"
123
124 return py_trees.common.Status.SUCCESS
125
126
127##############################################################################
128# Main
129##############################################################################
130
131
132def main() -> None:
133 """Entry point for the demo script."""
134 _ = (
135 command_line_argument_parser().parse_args()
136 ) # configuration only, no arg processing
137 print(description())
138 py_trees.logging.level = py_trees.logging.Level.DEBUG
139 py_trees.blackboard.Blackboard.enable_activity_stream(maximum_size=100)
140 root = Remap(name="Remap", remap_to={"/foo/bar/wow": "/parameters/wow"})
141
142 ####################
143 # Execute
144 ####################
145 root.tick_once()
146 print(root.blackboard)
147 print(py_trees.display.unicode_blackboard())
148 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
- 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 (interminate()
). 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) –
- py_trees.demos.context_switching.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.context_switching.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.context_switching.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
- py_trees.demos.context_switching.main() None [source]
Entry point for the demo script.
- Return type
None
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
30import typing
31
32import py_trees
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 context switching with parallels and sequences.\n"
48 content += "\n"
49 content += (
50 "A context switching behaviour is run in parallel with a work sequence.\n"
51 )
52 content += (
53 "Switching the context occurs in the initialise() and terminate() methods\n"
54 )
55 content += (
56 "of the context switching behaviour. Note that whether the sequence results\n"
57 )
58 content += (
59 "in failure or success, the context switch behaviour will always call the\n"
60 )
61 content += (
62 "terminate() method to restore the context. It will also call terminate()\n"
63 )
64 content += (
65 "to restore the context in the event of a higher priority parent cancelling\n"
66 )
67 content += "this parallel subtree.\n"
68 if py_trees.console.has_colours:
69 banner_line = console.green + "*" * 79 + "\n" + console.reset
70 s = banner_line
71 s += console.bold_white + "Context Switching".center(79) + "\n" + console.reset
72 s += banner_line
73 s += "\n"
74 s += content
75 s += "\n"
76 s += banner_line
77 else:
78 s = content
79 return s
80
81
82def epilog() -> typing.Optional[str]:
83 """
84 Print a noodly epilog for --help.
85
86 Returns:
87 the noodly message
88 """
89 if py_trees.console.has_colours:
90 return (
91 console.cyan
92 + "And his noodly appendage reached forth to tickle the blessed...\n"
93 + console.reset
94 )
95 else:
96 return None
97
98
99def command_line_argument_parser() -> argparse.ArgumentParser:
100 """
101 Process command line arguments.
102
103 Returns:
104 the argument parser
105 """
106 parser = argparse.ArgumentParser(
107 description=description(),
108 epilog=epilog(),
109 formatter_class=argparse.RawDescriptionHelpFormatter,
110 )
111 parser.add_argument(
112 "-r", "--render", action="store_true", help="render dot tree to file"
113 )
114 return parser
115
116
117class ContextSwitch(py_trees.behaviour.Behaviour):
118 """
119 An example of a context switching class.
120
121 This class sets (in ``initialise()``)
122 and restores a context (in ``terminate()``). Use in parallel with a
123 sequence/subtree that does the work while in this context.
124
125 .. attention:: Simply setting a pair of behaviours (set and reset context) on
126 either end of a sequence will not suffice for context switching. In the case
127 that one of the work behaviours in the sequence fails, the final reset context
128 switch will never trigger.
129 """
130
131 def __init__(self, name: str = "ContextSwitch"):
132 """Initialise with a behaviour name."""
133 super(ContextSwitch, self).__init__(name)
134 self.feedback_message = "no context"
135
136 def initialise(self) -> None:
137 """Backup and set a new context."""
138 self.logger.debug("%s.initialise()[switch context]" % (self.__class__.__name__))
139 # Some actions that:
140 # 1. retrieve the current context from somewhere
141 # 2. cache the context internally
142 # 3. apply a new context
143 self.feedback_message = "new context"
144
145 def update(self) -> py_trees.common.Status:
146 """Just returns RUNNING while it waits for other activities to finish."""
147 self.logger.debug(
148 "%s.update()[RUNNING][%s]"
149 % (self.__class__.__name__, self.feedback_message)
150 )
151 return py_trees.common.Status.RUNNING
152
153 def terminate(self, new_status: py_trees.common.Status) -> None:
154 """Restore the context with the previously backed up context."""
155 self.logger.debug(
156 "%s.terminate()[%s->%s][restore context]"
157 % (self.__class__.__name__, self.status, new_status)
158 )
159 # Some actions that:
160 # 1. restore the cached context
161 self.feedback_message = "restored context"
162
163
164def create_root() -> py_trees.behaviour.Behaviour:
165 """
166 Create the root behaviour and it's subtree.
167
168 Returns:
169 the root behaviour
170 """
171 root = py_trees.composites.Parallel(
172 name="Parallel", policy=py_trees.common.ParallelPolicy.SuccessOnOne()
173 )
174 context_switch = ContextSwitch(name="Context")
175 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
176 for job in ["Action 1", "Action 2"]:
177 success_after_two = py_trees.behaviours.StatusQueue(
178 name=job,
179 queue=[py_trees.common.Status.RUNNING, py_trees.common.Status.RUNNING],
180 eventually=py_trees.common.Status.SUCCESS,
181 )
182 sequence.add_child(success_after_two)
183 root.add_child(context_switch)
184 root.add_child(sequence)
185 return root
186
187
188##############################################################################
189# Main
190##############################################################################
191
192
193def main() -> None:
194 """Entry point for the demo script."""
195 args = command_line_argument_parser().parse_args()
196 print(description())
197 py_trees.logging.level = py_trees.logging.Level.DEBUG
198
199 root = create_root()
200
201 ####################
202 # Rendering
203 ####################
204 if args.render:
205 py_trees.display.render_dot_tree(root)
206 sys.exit()
207
208 ####################
209 # Execute
210 ####################
211 root.setup_with_descendants()
212 for i in range(1, 6):
213 try:
214 print("\n--------- Tick {0} ---------\n".format(i))
215 root.tick_once()
216 print("\n")
217 print("{}".format(py_trees.display.unicode_tree(root, show_status=True)))
218 time.sleep(1.0)
219 except KeyboardInterrupt:
220 break
221 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”
- py_trees.demos.dot_graphs.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.dot_graphs.create_tree(level: str) Behaviour [source]
Create the root behaviour and it’s subtree.
- py_trees.demos.dot_graphs.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
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
28import typing
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 name = "py-trees-demo-dot-graphs"
46 content = "Renders a dot graph for a simple tree, with blackboxes.\n"
47 if py_trees.console.has_colours:
48 banner_line = console.green + "*" * 79 + "\n" + console.reset
49 s = banner_line
50 s += console.bold_white + "Dot Graphs".center(79) + "\n" + console.reset
51 s += banner_line
52 s += "\n"
53 s += content
54 s += "\n"
55 s += console.white
56 s += console.bold + " Generate Full Dot Graph" + console.reset + "\n"
57 s += "\n"
58 s += console.cyan + " {0}".format(name) + console.reset + "\n"
59 s += "\n"
60 s += console.bold + " With Varying Visibility Levels" + console.reset + "\n"
61 s += "\n"
62 s += (
63 console.cyan
64 + " {0}".format(name)
65 + console.yellow
66 + " --level=all"
67 + console.reset
68 + "\n"
69 )
70 s += (
71 console.cyan
72 + " {0}".format(name)
73 + console.yellow
74 + " --level=detail"
75 + console.reset
76 + "\n"
77 )
78 s += (
79 console.cyan
80 + " {0}".format(name)
81 + console.yellow
82 + " --level=component"
83 + console.reset
84 + "\n"
85 )
86 s += (
87 console.cyan
88 + " {0}".format(name)
89 + console.yellow
90 + " --level=big_picture"
91 + console.reset
92 + "\n"
93 )
94 s += "\n"
95 s += banner_line
96 else:
97 s = content
98 return s
99
100
101def epilog() -> typing.Optional[str]:
102 """
103 Print a noodly epilog for --help.
104
105 Returns:
106 the noodly message
107 """
108 if py_trees.console.has_colours:
109 return (
110 console.cyan
111 + "And his noodly appendage reached forth to tickle the blessed...\n"
112 + console.reset
113 )
114 else:
115 return None
116
117
118def command_line_argument_parser() -> argparse.ArgumentParser:
119 """
120 Process command line arguments.
121
122 Returns:
123 the argument parser
124 """
125 parser = argparse.ArgumentParser(
126 description=description(),
127 epilog=epilog(),
128 formatter_class=argparse.RawDescriptionHelpFormatter,
129 )
130 parser.add_argument(
131 "-l",
132 "--level",
133 action="store",
134 default="fine_detail",
135 choices=["all", "fine_detail", "detail", "component", "big_picture"],
136 help="visibility level",
137 )
138 return parser
139
140
141def create_tree(level: str) -> py_trees.behaviour.Behaviour:
142 """
143 Create the root behaviour and it's subtree.
144
145 Returns:
146 the root behaviour
147 """
148 root = py_trees.composites.Selector(name="Demo Dot Graphs %s" % level, memory=False)
149 first_blackbox = py_trees.composites.Sequence(name="BlackBox 1", memory=True)
150 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
151 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
152 first_blackbox.add_child(py_trees.behaviours.Running("Worker"))
153 first_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.BIG_PICTURE
154 second_blackbox = py_trees.composites.Sequence(name="Blackbox 2", memory=True)
155 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
156 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
157 second_blackbox.add_child(py_trees.behaviours.Running("Worker"))
158 second_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
159 third_blackbox = py_trees.composites.Sequence(name="Blackbox 3", memory=True)
160 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
161 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
162 third_blackbox.add_child(py_trees.behaviours.Running("Worker"))
163 third_blackbox.blackbox_level = py_trees.common.BlackBoxLevel.DETAIL
164 root.add_child(first_blackbox)
165 root.add_child(second_blackbox)
166 first_blackbox.add_child(third_blackbox)
167 return root
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 args.enum_level = py_trees.common.string_to_visibility_level(args.level)
179 print(description())
180 py_trees.logging.level = py_trees.logging.Level.DEBUG
181
182 root = create_tree(args.level)
183 py_trees.display.render_dot_tree(root, args.enum_level)
184
185 if py_trees.utilities.which("xdot"):
186 try:
187 subprocess.call(["xdot", "demo_dot_graphs_%s.dot" % args.level])
188 except KeyboardInterrupt:
189 pass
190 else:
191 print("")
192 console.logerror(
193 "No xdot viewer found, skipping display [hint: sudo apt install xdot]"
194 )
195 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
- py_trees.demos.either_or.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.either_or.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.either_or.description(root: Behaviour) str [source]
Print description and usage information about the program.
- 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
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
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 the 'either_or' idiom.\n\n"
50 content += "This behaviour tree pattern enables triggering of subtrees\n"
51 content += "with equal priority (first in, first served).\n"
52 content += "\n"
53 content += "EVENTS\n"
54 content += "\n"
55 content += " - 3 : joystick one enabled, task one starts\n"
56 content += " - 5 : task one finishes\n"
57 content += " - 6 : joystick two enabled, task two starts\n"
58 content += " - 7 : joystick one enabled, task one ignored, task two continues\n"
59 content += " - 8 : task two finishes\n"
60 content += "\n"
61 if py_trees.console.has_colours:
62 banner_line = console.green + "*" * 79 + "\n" + console.reset
63 s = banner_line
64 s += console.bold_white + "Either Or".center(79) + "\n" + console.reset
65 s += banner_line
66 s += "\n"
67 s += content
68 s += "\n"
69 s += banner_line
70 else:
71 s = content
72 return s
73
74
75def epilog() -> typing.Optional[str]:
76 """
77 Print a noodly epilog for --help.
78
79 Returns:
80 the noodly message
81 """
82 if py_trees.console.has_colours:
83 return (
84 console.cyan
85 + "And his noodly appendage reached forth to tickle the blessed...\n"
86 + console.reset
87 )
88 else:
89 return None
90
91
92def command_line_argument_parser() -> argparse.ArgumentParser:
93 """
94 Process command line arguments.
95
96 Returns:
97 the argument parser
98 """
99 parser = argparse.ArgumentParser(
100 description=description(create_root()),
101 epilog=epilog(),
102 formatter_class=argparse.RawDescriptionHelpFormatter,
103 )
104 group = parser.add_mutually_exclusive_group()
105 group.add_argument(
106 "-r", "--render", action="store_true", help="render dot tree to file"
107 )
108 group.add_argument(
109 "-i",
110 "--interactive",
111 action="store_true",
112 help="pause and wait for keypress at each tick",
113 )
114 return parser
115
116
117def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
118 """Print a banner with current tick count prior to ticking the tree.
119
120 Args:
121 behaviour_tree: the tree to tick (used to fetch the count number)
122 """
123 print("\n--------- Run %s ---------\n" % behaviour_tree.count)
124
125
126def post_tick_handler(
127 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
128 behaviour_tree: py_trees.trees.BehaviourTree,
129) -> None:
130 """
131 Print data about the part of the tree visited.
132
133 Args:
134 snapshot_handler: gather data about the part of the tree visited
135 behaviour_tree: tree to gather data from
136 """
137 print(
138 "\n"
139 + py_trees.display.unicode_tree(
140 root=behaviour_tree.root,
141 visited=snapshot_visitor.visited,
142 previously_visited=snapshot_visitor.previously_visited,
143 )
144 )
145 print(py_trees.display.unicode_blackboard())
146
147
148def create_root() -> py_trees.behaviour.Behaviour:
149 """
150 Create the root behaviour and it's subtree.
151
152 Returns:
153 the root behaviour
154 """
155 trigger_one = py_trees.decorators.FailureIsRunning(
156 name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 1", n=4)
157 )
158 trigger_two = py_trees.decorators.FailureIsRunning(
159 name="FisR", child=py_trees.behaviours.SuccessEveryN(name="Joystick 2", n=7)
160 )
161 enable_joystick_one = py_trees.behaviours.SetBlackboardVariable(
162 name="Joy1 - Enabled",
163 variable_name="joystick_one",
164 variable_value="enabled",
165 overwrite=True,
166 )
167 enable_joystick_two = py_trees.behaviours.SetBlackboardVariable(
168 name="Joy2 - Enabled",
169 variable_name="joystick_two",
170 variable_value="enabled",
171 overwrite=True,
172 )
173 reset_joystick_one = py_trees.behaviours.SetBlackboardVariable(
174 name="Joy1 - Disabled",
175 variable_name="joystick_one",
176 variable_value="disabled",
177 overwrite=True,
178 )
179 reset_joystick_two = py_trees.behaviours.SetBlackboardVariable(
180 name="Joy2 - Disabled",
181 variable_name="joystick_two",
182 variable_value="disabled",
183 overwrite=True,
184 )
185 task_one = py_trees.behaviours.TickCounter(
186 name="Task 1", duration=2, completion_status=py_trees.common.Status.SUCCESS
187 )
188 task_two = py_trees.behaviours.TickCounter(
189 name="Task 2", duration=2, completion_status=py_trees.common.Status.SUCCESS
190 )
191 idle = py_trees.behaviours.Running(name="Idle")
192 either_or = py_trees.idioms.either_or(
193 name="Either Or",
194 conditions=[
195 py_trees.common.ComparisonExpression(
196 "joystick_one", "enabled", operator.eq
197 ),
198 py_trees.common.ComparisonExpression(
199 "joystick_two", "enabled", operator.eq
200 ),
201 ],
202 subtrees=[task_one, task_two],
203 namespace="either_or",
204 )
205 root = py_trees.composites.Parallel(
206 name="Root",
207 policy=py_trees.common.ParallelPolicy.SuccessOnAll(synchronise=False),
208 )
209 reset = py_trees.composites.Sequence(name="Reset", memory=True)
210 reset.add_children([reset_joystick_one, reset_joystick_two])
211 joystick_one_events = py_trees.composites.Sequence(name="Joy1 Events", memory=True)
212 joystick_one_events.add_children([trigger_one, enable_joystick_one])
213 joystick_two_events = py_trees.composites.Sequence(name="Joy2 Events", memory=True)
214 joystick_two_events.add_children([trigger_two, enable_joystick_two])
215 tasks = py_trees.composites.Selector(name="Tasks", memory=False)
216 tasks.add_children([either_or, idle])
217 root.add_children([reset, joystick_one_events, joystick_two_events, tasks])
218 return root
219
220
221##############################################################################
222# Main
223##############################################################################
224
225
226def main() -> None:
227 """Entry point for the demo script."""
228 args = command_line_argument_parser().parse_args()
229 # py_trees.logging.level = py_trees.logging.Level.DEBUG
230 root = create_root()
231 print(description(root))
232
233 ####################
234 # Rendering
235 ####################
236 if args.render:
237 py_trees.display.render_dot_tree(root)
238 sys.exit()
239
240 ####################
241 # Tree Stewardship
242 ####################
243 behaviour_tree = py_trees.trees.BehaviourTree(root)
244 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
245 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
246 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
247 behaviour_tree.add_post_tick_handler(
248 functools.partial(post_tick_handler, snapshot_visitor)
249 )
250 behaviour_tree.visitors.append(snapshot_visitor)
251 behaviour_tree.setup(timeout=15)
252
253 ####################
254 # Tick Tock
255 ####################
256 if args.interactive:
257 py_trees.console.read_single_keypress()
258 for _unused_i in range(1, 11):
259 try:
260 behaviour_tree.tick()
261 if args.interactive:
262 py_trees.console.read_single_keypress()
263 else:
264 time.sleep(0.5)
265 except KeyboardInterrupt:
266 break
267 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
- py_trees.demos.eternal_guard.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.eternal_guard.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.eternal_guard.description(root: Behaviour) str [source]
Print description and usage information about the program.
- 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
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
31import typing
32
33import py_trees
34
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 the 'eternal guard' concept.\n\n"
50 content += "Two binary (F|S) conditional checks will fire every\n"
51 content += "tick, thus providing a fail-fast mechanism for the\n"
52 content += "long running sequence of tasks behind them.\n"
53 content += "\n"
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 + "Eternal Guard".center(79) + "\n" + console.reset
58 s += banner_line
59 s += "\n"
60 s += content
61 s += "\n"
62 s += py_trees.display.unicode_tree(root)
63 s += "\n"
64 s += banner_line
65 else:
66 s = content
67 return s
68
69
70def epilog() -> typing.Optional[str]:
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 (
79 console.cyan
80 + "And his noodly appendage reached forth to tickle the blessed...\n"
81 + console.reset
82 )
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(
101 "-r", "--render", action="store_true", help="render dot tree to file"
102 )
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 pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
113 """Print a banner with current tick count prior to ticking the tree.
114
115 Args:
116 behaviour_tree: the tree to tick (used to fetch the count number)
117 """
118 print("\n--------- Run %s ---------\n" % behaviour_tree.count)
119
120
121def post_tick_handler(
122 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
123 behaviour_tree: py_trees.trees.BehaviourTree,
124) -> None:
125 """
126 Print data about the part of the tree visited.
127
128 Args:
129 snapshot_handler: gather data about the part of the tree visited
130 behaviour_tree: tree to gather data from
131 """
132 print(
133 "\n"
134 + py_trees.display.unicode_tree(
135 root=behaviour_tree.root,
136 visited=snapshot_visitor.visited,
137 previously_visited=snapshot_visitor.previously_visited,
138 )
139 )
140 print(py_trees.display.unicode_blackboard())
141
142
143def create_root() -> py_trees.behaviour.Behaviour:
144 """
145 Create the root behaviour and it's subtree.
146
147 Returns:
148 the root behaviour
149 """
150 eternal_guard = py_trees.composites.Sequence(name="Eternal Guard", memory=False)
151 condition_one = py_trees.behaviours.StatusQueue(
152 name="Condition 1",
153 queue=[
154 py_trees.common.Status.SUCCESS,
155 py_trees.common.Status.FAILURE,
156 py_trees.common.Status.SUCCESS,
157 ],
158 eventually=py_trees.common.Status.SUCCESS,
159 )
160 condition_two = py_trees.behaviours.StatusQueue(
161 name="Condition 2",
162 queue=[
163 py_trees.common.Status.SUCCESS,
164 py_trees.common.Status.SUCCESS,
165 py_trees.common.Status.FAILURE,
166 ],
167 eventually=py_trees.common.Status.SUCCESS,
168 )
169 task_sequence = py_trees.composites.Sequence(name="Task Sequence", memory=True)
170 task_one = py_trees.behaviours.Success(name="Worker 1")
171 task_two = py_trees.behaviours.Running(name="Worker 2")
172
173 eternal_guard.add_children([condition_one, condition_two, task_sequence])
174 task_sequence.add_children([task_one, task_two])
175 return eternal_guard
176
177
178##############################################################################
179# Main
180##############################################################################
181
182
183def main() -> None:
184 """Entry point for the demo script."""
185 args = command_line_argument_parser().parse_args()
186 # py_trees.logging.level = py_trees.logging.Level.DEBUG
187 root = create_root()
188 print(description(root))
189
190 ####################
191 # Rendering
192 ####################
193 if args.render:
194 py_trees.display.render_dot_tree(root)
195 sys.exit()
196
197 ####################
198 # Tree Stewardship
199 ####################
200 behaviour_tree = py_trees.trees.BehaviourTree(root)
201 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
202 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
203 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
204 behaviour_tree.add_post_tick_handler(
205 functools.partial(post_tick_handler, snapshot_visitor)
206 )
207 behaviour_tree.visitors.append(snapshot_visitor)
208 behaviour_tree.setup(timeout=15)
209
210 ####################
211 # Tick Tock
212 ####################
213 if args.interactive:
214 py_trees.console.read_single_keypress()
215 for _unused_i in range(1, 11):
216 try:
217 behaviour_tree.tick()
218 if args.interactive:
219 py_trees.console.read_single_keypress()
220 else:
221 time.sleep(0.5)
222 except KeyboardInterrupt:
223 break
224 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
- py_trees.demos.logging.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.logging.create_tree() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.logging.description(root: Behaviour) str [source]
Print description and usage information about the program.
- 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
snapshot_visitor (DisplaySnapshotVisitor) –
behaviour_tree (BehaviourTree) –
- Return type
None
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() -> typing.Optional[str]:
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 (
85 console.cyan
86 + "And his noodly appendage reached forth to tickle the blessed...\n"
87 + console.reset
88 )
89 else:
90 return None
91
92
93def command_line_argument_parser() -> argparse.ArgumentParser:
94 """
95 Process command line arguments.
96
97 Returns:
98 the argument parser
99 """
100 parser = argparse.ArgumentParser(
101 description=description(create_tree()),
102 epilog=epilog(),
103 formatter_class=argparse.RawDescriptionHelpFormatter,
104 )
105 group = parser.add_mutually_exclusive_group()
106 group.add_argument(
107 "-r", "--render", action="store_true", help="render dot tree to file"
108 )
109 group.add_argument(
110 "-i",
111 "--interactive",
112 action="store_true",
113 help="pause and wait for keypress at each tick",
114 )
115 return parser
116
117
118def logger(
119 snapshot_visitor: py_trees.visitors.DisplaySnapshotVisitor,
120 behaviour_tree: py_trees.trees.BehaviourTree,
121) -> None:
122 """Log the tree (relevant parts thereof) to a yaml file.
123
124 Use as a post-tick handler for a tree.
125 """
126 if snapshot_visitor.changed:
127 print(console.cyan + "Logging.......................yes\n" + console.reset)
128 tree_serialisation = {"tick": behaviour_tree.count, "nodes": []}
129 for node in behaviour_tree.root.iterate():
130 node_type_str = "Behaviour"
131 for behaviour_type in [
132 py_trees.composites.Sequence,
133 py_trees.composites.Selector,
134 py_trees.composites.Parallel,
135 py_trees.decorators.Decorator,
136 ]:
137 if isinstance(node, behaviour_type):
138 node_type_str = behaviour_type.__name__
139 if node.tip() is not None:
140 node_tip = node.tip()
141 assert node_tip is not None # help mypy
142 node_tip_id = str(node_tip.id)
143 else:
144 node_tip_id = "none"
145 node_snapshot = {
146 "name": node.name,
147 "id": str(node.id),
148 "parent_id": str(node.parent.id) if node.parent else "none",
149 "child_ids": [str(child.id) for child in node.children],
150 "tip_id": node_tip_id,
151 "class_name": str(node.__module__) + "." + str(type(node).__name__),
152 "type": node_type_str,
153 "status": node.status.value,
154 "message": node.feedback_message,
155 "is_active": True if node.id in snapshot_visitor.visited else False,
156 }
157 typing.cast(list, tree_serialisation["nodes"]).append(node_snapshot)
158 if behaviour_tree.count == 0:
159 with open("dump.json", "w+") as outfile:
160 json.dump(tree_serialisation, outfile, indent=4)
161 else:
162 with open("dump.json", "a") as outfile:
163 json.dump(tree_serialisation, outfile, indent=4)
164 else:
165 print(console.yellow + "Logging.......................no\n" + console.reset)
166
167
168def create_tree() -> py_trees.behaviour.Behaviour:
169 """
170 Create the root behaviour and it's subtree.
171
172 Returns:
173 the root behaviour
174 """
175 every_n_success = py_trees.behaviours.SuccessEveryN("EveryN", 5)
176 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
177 guard = py_trees.behaviours.Success("Guard")
178 periodic_success = py_trees.behaviours.Periodic("Periodic", 3)
179 finisher = py_trees.behaviours.Success("Finisher")
180 sequence.add_child(guard)
181 sequence.add_child(periodic_success)
182 sequence.add_child(finisher)
183 sequence.blackbox_level = py_trees.common.BlackBoxLevel.COMPONENT
184 idle = py_trees.behaviours.Success("Idle")
185 root = py_trees.composites.Selector(name="Logging", memory=False)
186 root.add_child(every_n_success)
187 root.add_child(sequence)
188 root.add_child(idle)
189 return root
190
191
192##############################################################################
193# Main
194##############################################################################
195
196
197def main() -> None:
198 """Entry point for the demo script."""
199 args = command_line_argument_parser().parse_args()
200 py_trees.logging.level = py_trees.logging.Level.DEBUG
201 tree = create_tree()
202 print(description(tree))
203
204 ####################
205 # Rendering
206 ####################
207 if args.render:
208 py_trees.display.render_dot_tree(tree)
209 sys.exit()
210
211 ####################
212 # Tree Stewardship
213 ####################
214 behaviour_tree = py_trees.trees.BehaviourTree(tree)
215
216 debug_visitor = py_trees.visitors.DebugVisitor()
217 snapshot_visitor = py_trees.visitors.DisplaySnapshotVisitor()
218
219 behaviour_tree.visitors.append(debug_visitor)
220 behaviour_tree.visitors.append(snapshot_visitor)
221
222 behaviour_tree.add_post_tick_handler(functools.partial(logger, snapshot_visitor))
223
224 behaviour_tree.setup(timeout=15)
225
226 ####################
227 # Tick Tock
228 ####################
229 if args.interactive:
230 py_trees.console.read_single_keypress()
231 while True:
232 try:
233 behaviour_tree.tick()
234 if args.interactive:
235 py_trees.console.read_single_keypress()
236 else:
237 time.sleep(0.5)
238 except KeyboardInterrupt:
239 break
240 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
- py_trees.demos.selector.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.selector.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.selector.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
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
30import typing
31
32import py_trees
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 = (
48 "Higher priority switching and interruption in the children of a selector.\n"
49 )
50 content += "\n"
51 content += "In this example the higher priority child is setup to fail initially,\n"
52 content += "falling back to the continually running second child. On the third\n"
53 content += (
54 "tick, the first child succeeds and cancels the hitherto running child.\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 + "Selectors".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() -> typing.Optional[str]:
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 (
79 console.cyan
80 + "And his noodly appendage reached forth to tickle the blessed...\n"
81 + console.reset
82 )
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(),
96 epilog=epilog(),
97 formatter_class=argparse.RawDescriptionHelpFormatter,
98 )
99 parser.add_argument(
100 "-r", "--render", action="store_true", help="render dot tree to file"
101 )
102 return parser
103
104
105def create_root() -> py_trees.behaviour.Behaviour:
106 """
107 Create the root behaviour and it's subtree.
108
109 Returns:
110 the root behaviour
111 """
112 root = py_trees.composites.Selector(name="Selector", memory=False)
113 ffs = py_trees.behaviours.StatusQueue(
114 name="FFS",
115 queue=[
116 py_trees.common.Status.FAILURE,
117 py_trees.common.Status.FAILURE,
118 py_trees.common.Status.SUCCESS,
119 ],
120 eventually=py_trees.common.Status.SUCCESS,
121 )
122 always_running = py_trees.behaviours.Running(name="Running")
123 root.add_children([ffs, always_running])
124 return root
125
126
127##############################################################################
128# Main
129##############################################################################
130
131
132def main() -> None:
133 """Entry point for the demo script."""
134 args = command_line_argument_parser().parse_args()
135 print(description())
136 py_trees.logging.level = py_trees.logging.Level.DEBUG
137
138 root = create_root()
139
140 ####################
141 # Rendering
142 ####################
143 if args.render:
144 py_trees.display.render_dot_tree(root)
145 sys.exit()
146
147 ####################
148 # Execute
149 ####################
150 root.setup_with_descendants()
151 for i in range(1, 4):
152 try:
153 print("\n--------- Tick {0} ---------\n".format(i))
154 root.tick_once()
155 print("\n")
156 print(py_trees.display.unicode_tree(root=root, show_status=True))
157 time.sleep(1.0)
158 except KeyboardInterrupt:
159 break
160 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
- py_trees.demos.sequence.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.sequence.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.sequence.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
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
30import typing
31
32import py_trees
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 sequences in action.\n\n"
48 content += (
49 "A sequence is populated with 2-tick jobs that are allowed to run through to\n"
50 )
51 content += "completion.\n"
52
53 if py_trees.console.has_colours:
54 banner_line = console.green + "*" * 79 + "\n" + console.reset
55 s = banner_line
56 s += console.bold_white + "Sequences".center(79) + "\n" + console.reset
57 s += banner_line
58 s += "\n"
59 s += content
60 s += "\n"
61 s += banner_line
62 else:
63 s = content
64 return s
65
66
67def epilog() -> typing.Optional[str]:
68 """
69 Print a noodly epilog for --help.
70
71 Returns:
72 the noodly message
73 """
74 if py_trees.console.has_colours:
75 return (
76 console.cyan
77 + "And his noodly appendage reached forth to tickle the blessed...\n"
78 + console.reset
79 )
80 else:
81 return None
82
83
84def command_line_argument_parser() -> argparse.ArgumentParser:
85 """
86 Process command line arguments.
87
88 Returns:
89 the argument parser
90 """
91 parser = argparse.ArgumentParser(
92 description=description(),
93 epilog=epilog(),
94 formatter_class=argparse.RawDescriptionHelpFormatter,
95 )
96 parser.add_argument(
97 "-r", "--render", action="store_true", help="render dot tree to file"
98 )
99 return parser
100
101
102def create_root() -> py_trees.behaviour.Behaviour:
103 """
104 Create the root behaviour and it's subtree.
105
106 Returns:
107 the root behaviour
108 """
109 root = py_trees.composites.Sequence(name="Sequence", memory=True)
110 for action in ["Action 1", "Action 2", "Action 3"]:
111 rssss = py_trees.behaviours.StatusQueue(
112 name=action,
113 queue=[
114 py_trees.common.Status.RUNNING,
115 py_trees.common.Status.SUCCESS,
116 ],
117 eventually=py_trees.common.Status.SUCCESS,
118 )
119 root.add_child(rssss)
120 return root
121
122
123##############################################################################
124# Main
125##############################################################################
126
127
128def main() -> None:
129 """Entry point for the demo script."""
130 args = command_line_argument_parser().parse_args()
131 print(description())
132 py_trees.logging.level = py_trees.logging.Level.DEBUG
133
134 root = create_root()
135
136 ####################
137 # Rendering
138 ####################
139 if args.render:
140 py_trees.display.render_dot_tree(root)
141 sys.exit()
142
143 ####################
144 # Execute
145 ####################
146 root.setup_with_descendants()
147 for i in range(1, 6):
148 try:
149 print("\n--------- Tick {0} ---------\n".format(i))
150 root.tick_once()
151 print("\n")
152 print(py_trees.display.unicode_tree(root=root, show_status=True))
153 time.sleep(1.0)
154 except KeyboardInterrupt:
155 break
156 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
- 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.
- class py_trees.demos.stewardship.PeriodicSuccess[source]
Bases:
Periodic
Write the period from
Periodic
as a variable on the blackboard.
- class py_trees.demos.stewardship.SuccessEveryN[source]
Bases:
SuccessEveryN
Add a blackboard counter to
SuccessEveryN
.- update() Status [source]
Run the
SuccessEveryN
update and write to blackboard.- Returns
Returns the
Status
ofSuccessEveryN
- Return type
- py_trees.demos.stewardship.command_line_argument_parser() ArgumentParser [source]
Process command line arguments.
- Returns
the argument parser
- Return type
- py_trees.demos.stewardship.create_tree() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.stewardship.description() str [source]
Print description and usage information about the program.
- Returns
the program description string
- Return type
- 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
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
30import typing
31
32import py_trees
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 = "A demonstration of tree stewardship.\n\n"
48 content += (
49 "A slightly less trivial tree that uses a simple stdout pre-tick handler\n"
50 )
51 content += "and both the debug and snapshot visitors for logging and displaying\n"
52 content += "the state of the tree.\n"
53 content += "\n"
54 content += "EVENTS\n"
55 content += "\n"
56 content += " - 3 : sequence switches from running to success\n"
57 content += " - 4 : selector's first child flicks to success once only\n"
58 content += " - 8 : the fallback idler kicks in as everything else fails\n"
59 content += " - 14 : the first child kicks in again, aborting a running sequence behind it\n"
60 content += "\n"
61 if py_trees.console.has_colours:
62 banner_line = console.green + "*" * 79 + "\n" + console.reset
63 s = banner_line
64 s += console.bold_white + "Trees".center(79) + "\n" + console.reset
65 s += banner_line
66 s += "\n"
67 s += content
68 s += "\n"
69 s += banner_line
70 else:
71 s = content
72 return s
73
74
75def epilog() -> typing.Optional[str]:
76 """
77 Print a noodly epilog for --help.
78
79 Returns:
80 the noodly message
81 """
82 if py_trees.console.has_colours:
83 return (
84 console.cyan
85 + "And his noodly appendage reached forth to tickle the blessed...\n"
86 + console.reset
87 )
88 else:
89 return None
90
91
92def command_line_argument_parser() -> argparse.ArgumentParser:
93 """
94 Process command line arguments.
95
96 Returns:
97 the argument parser
98 """
99 parser = argparse.ArgumentParser(
100 description=description(),
101 epilog=epilog(),
102 formatter_class=argparse.RawDescriptionHelpFormatter,
103 )
104 group = parser.add_mutually_exclusive_group()
105 group.add_argument(
106 "-r", "--render", action="store_true", help="render dot tree to file"
107 )
108 group.add_argument(
109 "--render-with-blackboard-variables",
110 action="store_true",
111 help="render dot tree to file with blackboard variables",
112 )
113 group.add_argument(
114 "-i",
115 "--interactive",
116 action="store_true",
117 help="pause and wait for keypress at each tick",
118 )
119 return parser
120
121
122def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
123 """Generate a simple pre-tick banner printing to stdout."""
124 print("\n--------- Run %s ---------\n" % behaviour_tree.count)
125
126
127class SuccessEveryN(py_trees.behaviours.SuccessEveryN):
128 """Add a blackboard counter to :class:`~py_trees.behaviours.SuccessEveryN`."""
129
130 def __init__(self) -> None:
131 """Set up the blackboard."""
132 super().__init__(name="EveryN", n=5)
133 self.blackboard = self.attach_blackboard_client(name=self.name)
134 self.blackboard.register_key("count", access=py_trees.common.Access.WRITE)
135
136 def update(self) -> py_trees.common.Status:
137 """
138 Run the :class:`~py_trees.behaviours.SuccessEveryN` update and write to blackboard.
139
140 Returns:
141 Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.SuccessEveryN`
142 """
143 status = super().update()
144 self.blackboard.count = self.count
145 return status
146
147
148class PeriodicSuccess(py_trees.behaviours.Periodic):
149 """Write the period from :class:`~py_trees.behaviours.Periodic` as a variable on the blackboard."""
150
151 def __init__(self) -> None:
152 """Initialise :class:`~py_trees.behaviours.Periodic` and set up the blackboard."""
153 super().__init__(name="Periodic", n=3)
154 self.blackboard = self.attach_blackboard_client(name=self.name)
155 self.blackboard.register_key("period", access=py_trees.common.Access.WRITE)
156
157 def update(self) -> py_trees.common.Status:
158 """
159 Run the :class:`~py_trees.behaviours.Periodic` update and write to blackboard.
160
161 Returns:
162 Returns the :class:`~py_trees.common.Status` of :class:`~py_trees.behaviours.Periodic`
163 """
164 status = super().update()
165 self.blackboard.period = self.period
166 return status
167
168
169class Finisher(py_trees.behaviour.Behaviour):
170 """
171 Gathers blackboard data from other behaviours and prints a summary.
172
173 To be used at the end of the tree run.
174 """
175
176 def __init__(self) -> None:
177 """Set up the blackboard."""
178 super().__init__(name="Finisher")
179 self.blackboard = self.attach_blackboard_client(name=self.name)
180 self.blackboard.register_key("count", access=py_trees.common.Access.READ)
181 self.blackboard.register_key("period", access=py_trees.common.Access.READ)
182
183 def update(self) -> py_trees.common.Status:
184 """
185 Fetch blackboard variables and print a summary.
186
187 Returns:
188 Always returns :data:`~py_trees.common.Status.SUCCESS`.
189 """
190 print(console.green + "---------------------------" + console.reset)
191 print(console.bold + " Finisher" + console.reset)
192 print(
193 console.green + " Count : {}".format(self.blackboard.count) + console.reset
194 )
195 print(
196 console.green
197 + " Period: {}".format(self.blackboard.period)
198 + console.reset
199 )
200 print(console.green + "---------------------------" + console.reset)
201 return py_trees.common.Status.SUCCESS
202
203
204def create_tree() -> py_trees.behaviour.Behaviour:
205 """
206 Create the root behaviour and it's subtree.
207
208 Returns:
209 the root behaviour
210 """
211 every_n_success = SuccessEveryN()
212 sequence = py_trees.composites.Sequence(name="Sequence", memory=True)
213 guard = py_trees.behaviours.Success("Guard")
214 periodic_success = PeriodicSuccess()
215 finisher = Finisher()
216 sequence.add_child(guard)
217 sequence.add_child(periodic_success)
218 sequence.add_child(finisher)
219 idle = py_trees.behaviours.Success("Idle")
220 root = py_trees.composites.Selector(name="Demo Tree", memory=False)
221 root.add_child(every_n_success)
222 root.add_child(sequence)
223 root.add_child(idle)
224 return root
225
226
227##############################################################################
228# Main
229##############################################################################
230
231
232def main() -> None:
233 """Entry point for the demo script."""
234 args = command_line_argument_parser().parse_args()
235 py_trees.logging.level = py_trees.logging.Level.DEBUG
236 tree = create_tree()
237 print(description())
238
239 ####################
240 # Rendering
241 ####################
242 if args.render:
243 py_trees.display.render_dot_tree(tree)
244 sys.exit()
245
246 if args.render_with_blackboard_variables:
247 py_trees.display.render_dot_tree(tree, with_blackboard_variables=True)
248 sys.exit()
249
250 ####################
251 # Tree Stewardship
252 ####################
253 py_trees.blackboard.Blackboard.enable_activity_stream(100)
254 behaviour_tree = py_trees.trees.BehaviourTree(tree)
255 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
256 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
257 behaviour_tree.visitors.append(
258 py_trees.visitors.DisplaySnapshotVisitor(
259 display_blackboard=True, display_activity_stream=True
260 )
261 )
262 behaviour_tree.setup(timeout=15)
263
264 ####################
265 # Tick Tock
266 ####################
267 if args.interactive:
268 py_trees.console.read_single_keypress()
269 while True:
270 try:
271 behaviour_tree.tick()
272 if args.interactive:
273 py_trees.console.read_single_keypress()
274 else:
275 time.sleep(0.5)
276 except KeyboardInterrupt:
277 break
278 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
- 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
- py_trees.demos.pick_up_where_you_left_off.create_root() Behaviour [source]
Create the root behaviour and it’s subtree.
- Returns
the root behaviour
- Return type
- py_trees.demos.pick_up_where_you_left_off.description(root: Behaviour) str [source]
Print description and usage information about the program.
- py_trees.demos.pick_up_where_you_left_off.epilog() Optional[str] [source]
Print a noodly epilog for –help.
- 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
snapshot_visitor (SnapshotVisitor) –
behaviour_tree (BehaviourTree) –
- 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
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
31import typing
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 'pick up where you left off' idiom.\n\n"
49 content += "A common behaviour tree pattern that allows you to resume\n"
50 content += "work after being interrupted by a high priority interrupt.\n"
51 content += "\n"
52 content += "EVENTS\n"
53 content += "\n"
54 content += " - 2 : task one done, task two running\n"
55 content += " - 3 : high priority interrupt\n"
56 content += " - 7 : task two restarts\n"
57 content += " - 9 : task two done\n"
58 content += "\n"
59 if py_trees.console.has_colours:
60 banner_line = console.green + "*" * 79 + "\n" + console.reset
61 s = banner_line
62 s += (
63 console.bold_white
64 + "Pick Up Where you Left Off".center(79)
65 + "\n"
66 + console.reset
67 )
68 s += banner_line
69 s += "\n"
70 s += content
71 s += "\n"
72 s += py_trees.display.unicode_tree(root)
73 s += "\n"
74 s += banner_line
75 else:
76 s = content
77 return s
78
79
80def epilog() -> typing.Optional[str]:
81 """
82 Print a noodly epilog for --help.
83
84 Returns:
85 the noodly message
86 """
87 if py_trees.console.has_colours:
88 return (
89 console.cyan
90 + "And his noodly appendage reached forth to tickle the blessed...\n"
91 + console.reset
92 )
93 else:
94 return None
95
96
97def command_line_argument_parser() -> argparse.ArgumentParser:
98 """
99 Process command line arguments.
100
101 Returns:
102 the argument parser
103 """
104 parser = argparse.ArgumentParser(
105 description=description(create_root()),
106 epilog=epilog(),
107 formatter_class=argparse.RawDescriptionHelpFormatter,
108 )
109 group = parser.add_mutually_exclusive_group()
110 group.add_argument(
111 "-r", "--render", action="store_true", help="render dot tree to file"
112 )
113 group.add_argument(
114 "-i",
115 "--interactive",
116 action="store_true",
117 help="pause and wait for keypress at each tick",
118 )
119 return parser
120
121
122def pre_tick_handler(behaviour_tree: py_trees.trees.BehaviourTree) -> None:
123 """Print a banner immediately before every tick of the tree.
124
125 Args:
126 behaviour_tree (:class:`~py_trees.trees.BehaviourTree`): the tree custodian
127
128 """
129 print("\n--------- Run %s ---------\n" % behaviour_tree.count)
130
131
132def post_tick_handler(
133 snapshot_visitor: py_trees.visitors.SnapshotVisitor,
134 behaviour_tree: py_trees.trees.BehaviourTree,
135) -> None:
136 """Print an ascii tree with the current snapshot status."""
137 print(
138 "\n"
139 + py_trees.display.unicode_tree(
140 root=behaviour_tree.root,
141 visited=snapshot_visitor.visited,
142 previously_visited=snapshot_visitor.previously_visited,
143 )
144 )
145
146
147def create_root() -> py_trees.behaviour.Behaviour:
148 """
149 Create the root behaviour and it's subtree.
150
151 Returns:
152 the root behaviour
153 """
154 task_one = py_trees.behaviours.StatusQueue(
155 name="Task 1",
156 queue=[
157 py_trees.common.Status.RUNNING,
158 py_trees.common.Status.RUNNING,
159 ],
160 eventually=py_trees.common.Status.SUCCESS,
161 )
162 task_two = py_trees.behaviours.StatusQueue(
163 name="Task 2",
164 queue=[
165 py_trees.common.Status.RUNNING,
166 py_trees.common.Status.RUNNING,
167 ],
168 eventually=py_trees.common.Status.SUCCESS,
169 )
170 high_priority_interrupt = py_trees.decorators.RunningIsFailure(
171 name="Running is Failure",
172 child=py_trees.behaviours.Periodic(name="High Priority", n=3),
173 )
174 piwylo = py_trees.idioms.pick_up_where_you_left_off(
175 name="Pick Up\nWhere You\nLeft Off", tasks=[task_one, task_two]
176 )
177 root = py_trees.composites.Selector(name="Root", memory=False)
178 root.add_children([high_priority_interrupt, piwylo])
179
180 return root
181
182
183##############################################################################
184# Main
185##############################################################################
186
187
188def main() -> None:
189 """Entry point for the demo script."""
190 args = command_line_argument_parser().parse_args()
191 py_trees.logging.level = py_trees.logging.Level.DEBUG
192 root = create_root()
193 print(description(root))
194
195 ####################
196 # Rendering
197 ####################
198 if args.render:
199 py_trees.display.render_dot_tree(root)
200 sys.exit()
201
202 ####################
203 # Tree Stewardship
204 ####################
205 behaviour_tree = py_trees.trees.BehaviourTree(root)
206 behaviour_tree.add_pre_tick_handler(pre_tick_handler)
207 behaviour_tree.visitors.append(py_trees.visitors.DebugVisitor())
208 snapshot_visitor = py_trees.visitors.SnapshotVisitor()
209 behaviour_tree.add_post_tick_handler(
210 functools.partial(post_tick_handler, snapshot_visitor)
211 )
212 behaviour_tree.visitors.append(snapshot_visitor)
213 behaviour_tree.setup(timeout=15)
214
215 ####################
216 # Tick Tock
217 ####################
218 if args.interactive:
219 py_trees.console.read_single_keypress()
220 for _unused_i in range(1, 11):
221 try:
222 behaviour_tree.tick()
223 if args.interactive:
224 py_trees.console.read_single_keypress()
225 else:
226 time.sleep(0.5)
227 except KeyboardInterrupt:
228 break
229 print("\n")