py_trees 2.5.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_trees/__init__.py +39 -0
- py_trees/behaviour.py +428 -0
- py_trees/behaviours.py +808 -0
- py_trees/blackboard.py +1397 -0
- py_trees/common.py +302 -0
- py_trees/composites.py +778 -0
- py_trees/console.py +432 -0
- py_trees/decorators.py +936 -0
- py_trees/demos/README.md +8 -0
- py_trees/demos/__init__.py +31 -0
- py_trees/demos/action.py +200 -0
- py_trees/demos/blackboard.py +278 -0
- py_trees/demos/blackboard_namespaces.py +146 -0
- py_trees/demos/blackboard_remappings.py +141 -0
- py_trees/demos/context_switching.py +194 -0
- py_trees/demos/display_modes.py +151 -0
- py_trees/demos/dot_graphs.py +160 -0
- py_trees/demos/either_or.py +254 -0
- py_trees/demos/eternal_guard.py +214 -0
- py_trees/demos/lifecycle.py +149 -0
- py_trees/demos/logging.py +234 -0
- py_trees/demos/pick_up_where_you_left_off.py +213 -0
- py_trees/demos/ports/__init__.py +9 -0
- py_trees/demos/ports/basic.py +89 -0
- py_trees/demos/ports/nested_subtrees.py +120 -0
- py_trees/demos/ports/nested_subtrees.xml +65 -0
- py_trees/demos/ports/remapping.py +165 -0
- py_trees/demos/ports/xml_tree.py +113 -0
- py_trees/demos/ports/xml_tree.xml +15 -0
- py_trees/demos/selector.py +149 -0
- py_trees/demos/sequence.py +147 -0
- py_trees/demos/stewardship.py +261 -0
- py_trees/display.py +1075 -0
- py_trees/idioms.py +259 -0
- py_trees/logging.py +122 -0
- py_trees/meta.py +74 -0
- py_trees/parsers/__init__.py +21 -0
- py_trees/parsers/behaviour_tree_xml.py +1066 -0
- py_trees/ports.py +808 -0
- py_trees/ports_utils.py +464 -0
- py_trees/programs/__init__.py +15 -0
- py_trees/programs/render.py +205 -0
- py_trees/py.typed +0 -0
- py_trees/syntax_highlighting.py +73 -0
- py_trees/tests.py +121 -0
- py_trees/timers.py +87 -0
- py_trees/trees.py +481 -0
- py_trees/utilities.py +196 -0
- py_trees/version.py +17 -0
- py_trees/visitors.py +226 -0
- py_trees-2.5.0.dist-info/METADATA +186 -0
- py_trees-2.5.0.dist-info/RECORD +55 -0
- py_trees-2.5.0.dist-info/WHEEL +5 -0
- py_trees-2.5.0.dist-info/entry_points.txt +21 -0
- py_trees-2.5.0.dist-info/licenses/LICENSE +32 -0
py_trees/__init__.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#
|
|
2
|
+
# License: BSD
|
|
3
|
+
# https://raw.githubusercontent.com/splintered-reality/py_trees/devel/LICENSE
|
|
4
|
+
#
|
|
5
|
+
##############################################################################
|
|
6
|
+
# Documentation
|
|
7
|
+
##############################################################################
|
|
8
|
+
|
|
9
|
+
"""This is the top-level namespace of the py_trees package."""
|
|
10
|
+
|
|
11
|
+
##############################################################################
|
|
12
|
+
# Imports
|
|
13
|
+
##############################################################################
|
|
14
|
+
|
|
15
|
+
# Preserve import order: demos & programs depend on the modules above them.
|
|
16
|
+
# isort: off
|
|
17
|
+
from . import behaviour
|
|
18
|
+
from . import behaviours
|
|
19
|
+
from . import blackboard
|
|
20
|
+
from . import common
|
|
21
|
+
from . import composites
|
|
22
|
+
from . import console
|
|
23
|
+
from . import decorators
|
|
24
|
+
from . import display
|
|
25
|
+
from . import idioms
|
|
26
|
+
from . import logging
|
|
27
|
+
from . import meta
|
|
28
|
+
from . import ports
|
|
29
|
+
from . import syntax_highlighting
|
|
30
|
+
from . import tests
|
|
31
|
+
from . import timers
|
|
32
|
+
from . import trees
|
|
33
|
+
from . import utilities
|
|
34
|
+
from . import version
|
|
35
|
+
from . import visitors
|
|
36
|
+
|
|
37
|
+
from . import demos
|
|
38
|
+
from . import programs
|
|
39
|
+
# isort: on
|
py_trees/behaviour.py
ADDED
|
@@ -0,0 +1,428 @@
|
|
|
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
|
+
"""The core behaviour template for all py_tree behaviours."""
|
|
11
|
+
|
|
12
|
+
##############################################################################
|
|
13
|
+
# Imports
|
|
14
|
+
##############################################################################
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import abc
|
|
19
|
+
import re
|
|
20
|
+
import typing
|
|
21
|
+
import uuid
|
|
22
|
+
|
|
23
|
+
from . import blackboard, common, logging
|
|
24
|
+
|
|
25
|
+
##############################################################################
|
|
26
|
+
# Behaviour BluePrint
|
|
27
|
+
##############################################################################
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class Behaviour(abc.ABC):
|
|
31
|
+
"""A parent class for all user definable tree behaviours.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
name: the behaviour name, defaults to auto-generating from the class name
|
|
35
|
+
|
|
36
|
+
Raises:
|
|
37
|
+
TypeError: if the provided name is not a string
|
|
38
|
+
|
|
39
|
+
Attributes:
|
|
40
|
+
~py_trees.behaviours.Behaviour.id (:class:`uuid.UUID`): automagically generated unique identifier
|
|
41
|
+
for the behaviour
|
|
42
|
+
~py_trees.behaviours.Behaviour.name (:obj:`str`): the behaviour name
|
|
43
|
+
~py_trees.behaviours.Behaviour.blackboards (typing.List[py_trees.blackboard.Client]): collection of attached
|
|
44
|
+
blackboard clients
|
|
45
|
+
~py_trees.behaviours.Behaviour.status (:class:`~py_trees.common.Status`): the behaviour status
|
|
46
|
+
(:data:`~py_trees.common.Status.INVALID`,
|
|
47
|
+
:data:`~py_trees.common.Status.RUNNING`,
|
|
48
|
+
:data:`~py_trees.common.Status.FAILURE`,
|
|
49
|
+
:data:`~py_trees.common.Status.SUCCESS`)
|
|
50
|
+
~py_trees.behaviours.Behaviour.parent (:class:`~py_trees.behaviour.Behaviour`): a
|
|
51
|
+
:class:`~py_trees.composites.Composite` instance if nested in a tree, otherwise None
|
|
52
|
+
~py_trees.behaviours.Behaviour.children ([:class:`~py_trees.behaviour.Behaviour`]): empty for regular
|
|
53
|
+
behaviours, populated for composites
|
|
54
|
+
~py_trees.behaviours.Behaviour.logger (:class:`logging.Logger`): a simple logging mechanism
|
|
55
|
+
~py_trees.behaviours.Behaviour.feedback_message(:obj:`str`): improve debugging with a simple message
|
|
56
|
+
~py_trees.behaviours.Behaviour.blackbox_level (:class:`~py_trees.common.BlackBoxLevel`): a helper variable
|
|
57
|
+
for dot graphs and runtime gui's to collapse/explode entire subtrees dependent upon the blackbox level.
|
|
58
|
+
|
|
59
|
+
.. seealso::
|
|
60
|
+
* :ref:`Skeleton Behaviour Template <skeleton-behaviour-include>`
|
|
61
|
+
* :ref:`The Lifecycle Demo <py-trees-demo-behaviour-lifecycle-program>`
|
|
62
|
+
* :ref:`The Action Behaviour Demo <py-trees-demo-action-behaviour-program>`
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
def __init__(self, name: str):
|
|
66
|
+
if not isinstance(name, str):
|
|
67
|
+
raise TypeError(f"a behaviour name should be a string, but you passed in {type(name)}")
|
|
68
|
+
self.id = uuid.uuid4() # used to uniquely identify this node (helps with removing children from a tree)
|
|
69
|
+
self.name: str = name
|
|
70
|
+
self.blackboards: list[blackboard.Client] = []
|
|
71
|
+
self.qualified_name = f"{self.__class__.__qualname__}/{self.name}" # convenience
|
|
72
|
+
self.status = common.Status.INVALID
|
|
73
|
+
self.iterator = self.tick()
|
|
74
|
+
self.parent: Behaviour | None = None # will get set if a behaviour is added to a composite
|
|
75
|
+
self.children: list[Behaviour] = [] # only set by composite behaviours
|
|
76
|
+
self.logger = logging.Logger(name)
|
|
77
|
+
self.feedback_message = "" # useful for debugging, or human readable updates, but not necessary to implement
|
|
78
|
+
self.blackbox_level = common.BlackBoxLevel.NOT_A_BLACKBOX
|
|
79
|
+
|
|
80
|
+
############################################
|
|
81
|
+
# User Customisable Callbacks
|
|
82
|
+
############################################
|
|
83
|
+
|
|
84
|
+
def setup(self, **kwargs: typing.Any) -> None: # noqa: B027
|
|
85
|
+
"""
|
|
86
|
+
Set up and verify infrastructure (middleware connections, etc) is available.
|
|
87
|
+
|
|
88
|
+
Users should override this method for any configuration and/or validation
|
|
89
|
+
that is necessary prior to ticking the tree. Such construction is best
|
|
90
|
+
done here rather than in __init__ since there is no guarantee at __init__
|
|
91
|
+
that the infrastructure is ready or even available (e.g. you may be just
|
|
92
|
+
rendering dot graphs of the trees, no robot around).
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
* establishing a middleware connection to a sensor or driver
|
|
96
|
+
* ensuring a sensor or driver is in a 'ready' state
|
|
97
|
+
|
|
98
|
+
This method will typically be called before a tree's first tick as this gives
|
|
99
|
+
the application time to check and verify that everything is in a ready state before
|
|
100
|
+
executing. This is especially important given that a tree does not always tick
|
|
101
|
+
every behaviour and if not checked up-front, it may be some time before
|
|
102
|
+
discovering a behaviour was in a broken state.
|
|
103
|
+
|
|
104
|
+
.. tip::
|
|
105
|
+
When to use :meth:`~py_trees.behaviour.Behaviour.__init__`,
|
|
106
|
+
:meth:`~py_trees.behaviour.Behaviour.setup` and when to use
|
|
107
|
+
:meth:`~py_trees.behaviour.Behaviour.initialise`?
|
|
108
|
+
|
|
109
|
+
Use :meth:`~py_trees.behaviour.Behaviour.__init__` for configuration of
|
|
110
|
+
non-runtime dependencies (e.g. no middleware).
|
|
111
|
+
|
|
112
|
+
Use :meth:`~py_trees.behaviour.Behaviour.setup` for one-offs or to get
|
|
113
|
+
early signal that everything (e.g. middleware) is ready to go.
|
|
114
|
+
|
|
115
|
+
Use :meth:`~py_trees.behaviour.Behaviour.initialise` for just-in-time
|
|
116
|
+
configurations and/or checks.
|
|
117
|
+
|
|
118
|
+
There are times when it makes sense to do all three. For example,
|
|
119
|
+
pythonic variable configuration in :meth:`~py_trees.behaviour.Behaviour.__init__`,
|
|
120
|
+
middleware service client creation / server existence checks in
|
|
121
|
+
:meth:`~py_trees.behaviour.Behaviour.setup` and a just-in-time check
|
|
122
|
+
to ensure the server is still available in :meth:`~py_trees.behaviour.Behaviour.initialise`.
|
|
123
|
+
|
|
124
|
+
.. tip::
|
|
125
|
+
|
|
126
|
+
Faults are notified to the user of the behaviour via exceptions.
|
|
127
|
+
Choice of exception to use is left to the user.
|
|
128
|
+
|
|
129
|
+
.. warning::
|
|
130
|
+
|
|
131
|
+
The kwargs argument is for distributing objects at runtime to behaviours
|
|
132
|
+
before ticking. For example, a simulator instance with which behaviours can
|
|
133
|
+
interact with the simulator's python api, a ros2 node for setting up
|
|
134
|
+
communications. Use sparingly, as this is not proof against keyword conflicts
|
|
135
|
+
amongst disparate libraries of behaviours.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
**kwargs: distribute arguments to this
|
|
139
|
+
behaviour and in turn, all of its children
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
Exception: if this behaviour has a fault in construction or configuration
|
|
143
|
+
|
|
144
|
+
.. seealso:: :meth:`py_trees.behaviour.Behaviour.shutdown`
|
|
145
|
+
"""
|
|
146
|
+
pass
|
|
147
|
+
|
|
148
|
+
def initialise(self) -> None: # noqa: B027
|
|
149
|
+
"""
|
|
150
|
+
Execute user specified instructions prior to commencement of a new round of activity.
|
|
151
|
+
|
|
152
|
+
Users should override this method to perform any necessary initialising/clearing/resetting
|
|
153
|
+
of variables prior to a new round of activity for the behaviour.
|
|
154
|
+
|
|
155
|
+
This method is automatically called via the :meth:`py_trees.behaviour.Behaviour.tick` method
|
|
156
|
+
whenever the behaviour is not :data:`~py_trees.common.Status.RUNNING`.
|
|
157
|
+
|
|
158
|
+
... note:: This method can be called more than once in the lifetime of a tree!
|
|
159
|
+
"""
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
def terminate(self, new_status: common.Status) -> None: # noqa: B027
|
|
163
|
+
"""
|
|
164
|
+
Execute user specified instructions when the behaviour is stopped.
|
|
165
|
+
|
|
166
|
+
Users should override this method to clean up.
|
|
167
|
+
It will be triggered when a behaviour either
|
|
168
|
+
finishes execution (switching from :data:`~py_trees.common.Status.RUNNING`
|
|
169
|
+
to :data:`~py_trees.common.Status.FAILURE` || :data:`~py_trees.common.Status.SUCCESS`)
|
|
170
|
+
or it got interrupted by a higher priority branch (switching to
|
|
171
|
+
:data:`~py_trees.common.Status.INVALID`). Remember that
|
|
172
|
+
the :meth:`~py_trees.behaviour.Behaviour.initialise` method
|
|
173
|
+
will handle resetting of variables before re-entry, so this method is about
|
|
174
|
+
disabling resources until this behaviour's next tick. This could be a indeterminably
|
|
175
|
+
long time. e.g.
|
|
176
|
+
|
|
177
|
+
* cancel an external action that got started
|
|
178
|
+
* shut down any temporary communication handles
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
new_status (:class:`~py_trees.common.Status`): the behaviour is transitioning to this new status
|
|
182
|
+
|
|
183
|
+
.. warning:: Do not set `self.status = new_status` here, that is automatically handled
|
|
184
|
+
by the :meth:`~py_trees.behaviour.Behaviour.stop` method.
|
|
185
|
+
Use the argument purely for introspection purposes (e.g.
|
|
186
|
+
comparing the current state in `self.status` with the state it will transition to in
|
|
187
|
+
`new_status`.
|
|
188
|
+
|
|
189
|
+
.. seealso:: :meth:`py_trees.behaviour.Behaviour.stop`
|
|
190
|
+
"""
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
@abc.abstractmethod
|
|
194
|
+
def update(self) -> common.Status:
|
|
195
|
+
"""
|
|
196
|
+
Execute user specified instructions when the behaviour is ticked.
|
|
197
|
+
|
|
198
|
+
Users should override this method to perform any logic required to
|
|
199
|
+
arrive at a decision on the behaviour's new status. It is the primary worker function called
|
|
200
|
+
by the :meth:`~py_trees.behaviour.Behaviour.tick` mechanism.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
the behaviour's new status :class:`~py_trees.common.Status`
|
|
204
|
+
|
|
205
|
+
.. tip:: This method should be almost instantaneous and non-blocking
|
|
206
|
+
|
|
207
|
+
.. seealso:: :meth:`py_trees.behaviour.Behaviour.tick`
|
|
208
|
+
"""
|
|
209
|
+
return common.Status.INVALID
|
|
210
|
+
|
|
211
|
+
def shutdown(self) -> None: # noqa: B027
|
|
212
|
+
"""
|
|
213
|
+
Destroy setup infrastructure (the antithesis of setup).
|
|
214
|
+
|
|
215
|
+
Users should override this method for any custom destruction of infrastructure
|
|
216
|
+
usually brought into being in :meth:`~py_trees.behaviour.Behaviour.setup`.
|
|
217
|
+
|
|
218
|
+
Raises:
|
|
219
|
+
Exception: of whatever flavour the child raises when errors occur on destruction
|
|
220
|
+
|
|
221
|
+
.. seealso:: :meth:`py_trees.behaviour.Behaviour.setup`
|
|
222
|
+
"""
|
|
223
|
+
pass
|
|
224
|
+
|
|
225
|
+
############################################
|
|
226
|
+
# Private Methods - use inside a behaviour
|
|
227
|
+
############################################
|
|
228
|
+
|
|
229
|
+
def attach_blackboard_client(self, name: str | None = None, namespace: str | None = None) -> blackboard.Client:
|
|
230
|
+
"""
|
|
231
|
+
Create and attach a blackboard to this behaviour.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
name: human-readable (not necessarily unique) name for the client
|
|
235
|
+
namespace: sandbox the client to variables behind this namespace
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
a handle to the attached blackboard client
|
|
239
|
+
"""
|
|
240
|
+
if name is None:
|
|
241
|
+
count = len(self.blackboards)
|
|
242
|
+
name = self.name if (count == 0) else self.name + f"-{count}"
|
|
243
|
+
new_blackboard = blackboard.Client(name=name, namespace=namespace)
|
|
244
|
+
self.blackboards.append(new_blackboard)
|
|
245
|
+
return new_blackboard
|
|
246
|
+
|
|
247
|
+
############################################
|
|
248
|
+
# Public - lifecycle API
|
|
249
|
+
############################################
|
|
250
|
+
|
|
251
|
+
def setup_with_descendants(self) -> None:
|
|
252
|
+
"""Call setup on this child, its children (its children's children, )."""
|
|
253
|
+
for child in self.children:
|
|
254
|
+
for node in child.iterate():
|
|
255
|
+
node.setup()
|
|
256
|
+
self.setup()
|
|
257
|
+
|
|
258
|
+
def tick_once(self) -> None:
|
|
259
|
+
"""Tick the object without iterating step-by-step over the children (i.e. without generators)."""
|
|
260
|
+
# no logger necessary here...it directly relays to tick
|
|
261
|
+
for _unused in self.tick():
|
|
262
|
+
pass
|
|
263
|
+
|
|
264
|
+
def tick(self) -> typing.Iterator[Behaviour]:
|
|
265
|
+
"""
|
|
266
|
+
Tick the behaviour.
|
|
267
|
+
|
|
268
|
+
This function is a generator that can be used by an iterator on
|
|
269
|
+
an entire behaviour tree. It handles the logic for deciding when to
|
|
270
|
+
call the user's :meth:`~py_trees.behaviour.Behaviour.initialise`
|
|
271
|
+
and :meth:`~py_trees.behaviour.Behaviour.terminate` methods as well as making the
|
|
272
|
+
actual call to the user's :meth:`~py_trees.behaviour.Behaviour.update` method that determines the
|
|
273
|
+
behaviour's new status once the tick has finished. Once done, it will
|
|
274
|
+
then yield itself (generator mechanism) so that it can be used as part of
|
|
275
|
+
an iterator for the entire tree.
|
|
276
|
+
|
|
277
|
+
.. code-block:: python
|
|
278
|
+
|
|
279
|
+
for node in my_behaviour.tick():
|
|
280
|
+
print("Do something")
|
|
281
|
+
|
|
282
|
+
.. note::
|
|
283
|
+
|
|
284
|
+
This is a generator function, you must use this with *yield*. If you need a direct call,
|
|
285
|
+
prefer :meth:`~py_trees.behaviour.Behaviour.tick_once` instead.
|
|
286
|
+
|
|
287
|
+
Yields:
|
|
288
|
+
a reference to itself
|
|
289
|
+
|
|
290
|
+
.. warning::
|
|
291
|
+
Users should not override this method to provide custom tick behaviour. The
|
|
292
|
+
:meth:`~py_trees.behaviour.Behaviour.update` method has been provided for that purpose.
|
|
293
|
+
"""
|
|
294
|
+
self.logger.debug(f"{self.__class__.__name__}.tick()")
|
|
295
|
+
if self.status != common.Status.RUNNING:
|
|
296
|
+
self.initialise()
|
|
297
|
+
# don't set self.status yet, terminate() may need to check what the current state is first
|
|
298
|
+
new_status = self.update()
|
|
299
|
+
if new_status not in list(common.Status):
|
|
300
|
+
self.logger.error(f"A behaviour returned an invalid status, setting to INVALID [{new_status}][{self.name}]")
|
|
301
|
+
new_status = common.Status.INVALID
|
|
302
|
+
if new_status != common.Status.RUNNING:
|
|
303
|
+
self.stop(new_status)
|
|
304
|
+
self.status = new_status
|
|
305
|
+
yield self
|
|
306
|
+
|
|
307
|
+
def iterate(self, direct_descendants: bool = False) -> typing.Iterator[Behaviour]:
|
|
308
|
+
"""
|
|
309
|
+
Iterate over this child and its children.
|
|
310
|
+
|
|
311
|
+
This utilises python generators for looping. To traverse the entire tree:
|
|
312
|
+
|
|
313
|
+
.. code-block:: python
|
|
314
|
+
|
|
315
|
+
for node in my_behaviour.iterate():
|
|
316
|
+
print("Name: {0}".format(node.name))
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
direct_descendants (:obj:`bool`): only yield children one step away from this behaviour.
|
|
320
|
+
|
|
321
|
+
Yields:
|
|
322
|
+
:class:`~py_trees.behaviour.Behaviour`: one of its children
|
|
323
|
+
"""
|
|
324
|
+
for child in self.children:
|
|
325
|
+
if not direct_descendants:
|
|
326
|
+
yield from child.iterate()
|
|
327
|
+
else:
|
|
328
|
+
yield child
|
|
329
|
+
yield self
|
|
330
|
+
|
|
331
|
+
# TODO: better type refinement of 'viso=itor'
|
|
332
|
+
def visit(self, visitor: typing.Any) -> None:
|
|
333
|
+
"""
|
|
334
|
+
Introspect on this behaviour with a visitor.
|
|
335
|
+
|
|
336
|
+
This is functionality that enables external introspection into the behaviour. It gets used
|
|
337
|
+
by the tree manager classes to collect information as ticking traverses a tree.
|
|
338
|
+
|
|
339
|
+
Args:
|
|
340
|
+
visitor: the visiting class, must have a run(:class:`~py_trees.behaviour.Behaviour`) method.
|
|
341
|
+
"""
|
|
342
|
+
visitor.run(self)
|
|
343
|
+
|
|
344
|
+
def stop(self, new_status: common.Status) -> None:
|
|
345
|
+
"""
|
|
346
|
+
Stop the behaviour with the specified status.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
new_status: the behaviour is transitioning to this new status
|
|
350
|
+
|
|
351
|
+
This is called to bring the current round of activity for the behaviour to completion, typically
|
|
352
|
+
resulting in a final status of :data:`~py_trees.common.Status.SUCCESS`,
|
|
353
|
+
:data:`~py_trees.common.Status.FAILURE` or :data:`~py_trees.common.Status.INVALID`.
|
|
354
|
+
|
|
355
|
+
.. warning::
|
|
356
|
+
Users should not override this method to provide custom termination behaviour. The
|
|
357
|
+
:meth:`~py_trees.behaviour.Behaviour.terminate` method has been provided for that purpose.
|
|
358
|
+
"""
|
|
359
|
+
self.logger.debug(
|
|
360
|
+
"{}.stop({})".format(
|
|
361
|
+
self.__class__.__name__,
|
|
362
|
+
(f"{self.status}->{new_status}" if self.status != new_status else f"{new_status}"),
|
|
363
|
+
)
|
|
364
|
+
)
|
|
365
|
+
self.terminate(new_status)
|
|
366
|
+
self.status = new_status
|
|
367
|
+
self.iterator = self.tick()
|
|
368
|
+
|
|
369
|
+
############################################
|
|
370
|
+
# Public - introspection API
|
|
371
|
+
############################################
|
|
372
|
+
def has_parent_with_name(self, name: str) -> bool:
|
|
373
|
+
"""
|
|
374
|
+
Search this behaviour's ancestors for one with the specified name.
|
|
375
|
+
|
|
376
|
+
Args:
|
|
377
|
+
name: name of the parent to match, can be a regular expression
|
|
378
|
+
|
|
379
|
+
Returns:
|
|
380
|
+
whether a parent was found or not
|
|
381
|
+
"""
|
|
382
|
+
pattern = re.compile(name)
|
|
383
|
+
b = self
|
|
384
|
+
while b.parent is not None:
|
|
385
|
+
if pattern.match(b.parent.name) is not None:
|
|
386
|
+
return True
|
|
387
|
+
b = b.parent
|
|
388
|
+
return False
|
|
389
|
+
|
|
390
|
+
def has_parent_with_instance_type(self, instance_type: type[Behaviour]) -> bool:
|
|
391
|
+
"""
|
|
392
|
+
Search this behaviour's ancestors for one of the specified type.
|
|
393
|
+
|
|
394
|
+
Args:
|
|
395
|
+
instance type of the parent to match
|
|
396
|
+
|
|
397
|
+
Returns:
|
|
398
|
+
whether a parent was found or not
|
|
399
|
+
"""
|
|
400
|
+
b = self
|
|
401
|
+
while b.parent is not None:
|
|
402
|
+
if isinstance(b.parent, instance_type):
|
|
403
|
+
return True
|
|
404
|
+
b = b.parent
|
|
405
|
+
return False
|
|
406
|
+
|
|
407
|
+
def tip(self) -> Behaviour | None:
|
|
408
|
+
"""
|
|
409
|
+
Get the *tip* of this behaviour's subtree (if it has one).
|
|
410
|
+
|
|
411
|
+
This corresponds to the the deepest node that was running before the
|
|
412
|
+
subtree traversal reversed direction and headed back to this node.
|
|
413
|
+
|
|
414
|
+
Returns:
|
|
415
|
+
The deepest node (behaviour) that was running before subtree traversal
|
|
416
|
+
reversed direction, or None if this behaviour's status is
|
|
417
|
+
:data:`~py_trees.common.Status.INVALID`.
|
|
418
|
+
"""
|
|
419
|
+
return self if self.status != common.Status.INVALID else None
|
|
420
|
+
|
|
421
|
+
|
|
422
|
+
##############################################################################
|
|
423
|
+
# Mypy Convenience Types
|
|
424
|
+
##############################################################################
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
BehaviourSubClass = typing.TypeVar("BehaviourSubClass", bound=Behaviour)
|
|
428
|
+
# BehaviourUpdateMethod = typing.Callable[[BehaviourSubClass], common.Status]
|