Decorators

Decorators are behaviours that manage a single child and provide common modifications to their underlying child behaviour (e.g. inverting the result). That is, they provide a means for behaviours to wear different ‘hats’ and this combinatorially expands the capabilities of your behaviour library.

_images/many-hats.png

An example:

digraph life {
graph [fontname="times-roman"];
node [fontname="times-roman"];
edge [fontname="times-roman"];
Life [fillcolor=orange, fontcolor=black, fontsize=11, shape=box, style=filled];
Inverter [fillcolor=ghostwhite, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Life -> Inverter;
"Busy?" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Inverter -> "Busy?";
Timeout [fillcolor=ghostwhite, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Life -> Timeout;
"Have a Beer!" [fillcolor=gray, fontcolor=black, fontsize=11, shape=ellipse, style=filled];
Timeout -> "Have a Beer!";
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import py_trees.decorators
import py_trees.display

if __name__ == '__main__':

    root = py_trees.composites.Sequence(name="Life")
    timeout = py_trees.decorators.Timeout(
        name="Timeout",
        child=py_trees.behaviours.Success(name="Have a Beer!")
    )
    failure_is_success = py_trees.decorators.Inverter(
        name="Inverter",
        child=py_trees.behaviours.Success(name="Busy?")
        )
    root.add_children([failure_is_success, timeout])
    py_trees.display.render_dot_tree(root)

Decorators (Hats)

Decorators with very specific functionality:

And the X is Y family:

Decorators for Blocking Behaviours

It is worth making a note of the effect of decorators on behaviours that return RUNNING for some time before finally returning SUCCESS or FAILURE (blocking behaviours) since the results are often at first, surprising.

A decorator, such as py_trees.decorators.RunningIsSuccess() on a blocking behaviour will immediately terminate the underlying child and re-intialise on it’s next tick. This is necessary to ensure the underlying child isn’t left in a dangling state (i.e. RUNNING), but is often not what is being sought.

The typical use case being attempted is to convert the blocking behaviour into a non-blocking behaviour. If the underlying child has no state being modified in either the initialise() or terminate() methods (e.g. machinery is entirely launched at init or setup time), then conversion to a non-blocking representative of the original succeeds. Otherwise, another approach is needed. Usually this entails writing a non-blocking counterpart, or combination of behaviours to affect the non-blocking characteristics.