|
| 1 | +""" |
| 2 | +NOTE: This is a preempt-handling version of the idiom discussed in |
| 3 | +https://github.com/splintered-reality/py_trees/pull/427 . Once a |
| 4 | +preempt-handling version of that idiom is merged into py_trees, this |
| 5 | +idiom should be removed in favor of the main py_trees one. |
| 6 | +""" |
| 7 | + |
| 8 | +import typing |
| 9 | + |
| 10 | +from py_trees import behaviour, behaviours, composites |
| 11 | + |
| 12 | +from ada_feeding.decorators import OnPreempt |
| 13 | + |
| 14 | + |
| 15 | +def eventually_swiss( |
| 16 | + name: str, |
| 17 | + workers: typing.List[behaviour.Behaviour], |
| 18 | + on_failure: behaviour.Behaviour, |
| 19 | + on_success: behaviour.Behaviour, |
| 20 | + on_preempt: behaviour.Behaviour, |
| 21 | + on_preempt_single_tick: bool = True, |
| 22 | + on_preempt_period_ms: int = 0, |
| 23 | + on_preempt_timeout: typing.Optional[float] = None, |
| 24 | + return_on_success_status: bool = True, |
| 25 | +) -> behaviour.Behaviour: |
| 26 | + """ |
| 27 | + Implement a multi-tick, general purpose 'try-except-else'-like pattern. |
| 28 | +
|
| 29 | + This is a swiss knife version of the eventually idiom |
| 30 | + that facilitates a multi-tick response for specialised |
| 31 | + handling work sequence's completion status. Specifically, this idiom |
| 32 | + guarentees the following: |
| 33 | + 1. The on_success behaviour is ticked only if the workers all return SUCCESS. |
| 34 | + 2. The on_failure behaviour is ticked only if at least one worker returns FAILURE. |
| 35 | + 3. The on_preempt behaviour is ticked only if `stop(INVALID)` is called on the |
| 36 | + root behaviour returned from this idiom while the root behaviour's status is |
| 37 | + :data:`~py_trees.common.Status.RUNNING`. |
| 38 | +
|
| 39 | + The return status of this idiom in non-preemption cases is: |
| 40 | + - If the workers all return SUCCESS: |
| 41 | + - If `return_on_success_status` is True, then the status of the root behaviour |
| 42 | + returned from this idiom is status of `on_success`. |
| 43 | + - If `return_on_success_status` is False, then the status of the root behaviour |
| 44 | + returned from this idiom is :data:`~py_trees.common.Status.SUCCESS`. |
| 45 | + - If at least one worker returns FAILURE, return :data:`~py_trees.common.Status.FAILURE`. |
| 46 | +
|
| 47 | + .. graphviz:: dot/eventually-swiss.dot |
| 48 | +
|
| 49 | + Args: |
| 50 | + name: the name to use for the idiom root |
| 51 | + workers: the worker behaviours or subtrees |
| 52 | + on_success: the behaviour or subtree to tick on work success |
| 53 | + on_failure: the behaviour or subtree to tick on work failure |
| 54 | + on_preempt: the behaviour or subtree to tick on work preemption |
| 55 | + on_preempt_single_tick: if True, tick the on_preempt behaviour once |
| 56 | + on preemption. Else, tick the on_preempt behaviour until it |
| 57 | + reaches a status other than :data:`~py_trees.common.Status.RUNNING`. |
| 58 | + on_preempt_period_ms: how long to sleep between ticks (in milliseconds) |
| 59 | + if `on_preempt_single_tick` is False. If 0, then do not sleep. |
| 60 | + on_preempt_timeout: how long (sec) to wait for the on_preempt behaviour |
| 61 | + to reach a status other than :data:`~py_trees.common.Status.RUNNING` |
| 62 | + if `on_preempt_single_tick` is False. If None, then do not timeout. |
| 63 | + return_on_success_status: if True, pass the `on_success` status to the |
| 64 | + root, else return :data:`~py_trees.common.Status.SUCCESS`. |
| 65 | +
|
| 66 | + Returns: |
| 67 | + :class:`~py_trees.behaviour.Behaviour`: the root of the eventually_swiss subtree |
| 68 | +
|
| 69 | + .. seealso:: :meth:`py_trees.idioms.eventually`, :ref:`py-trees-demo-eventually-swiss-program` |
| 70 | + """ |
| 71 | + # pylint: disable=too-many-arguments, too-many-locals |
| 72 | + # This is acceptable, to give users maximum control over how this swiss-knife |
| 73 | + # idiom behaves. |
| 74 | + # pylint: disable=abstract-class-instantiated |
| 75 | + # behaviours.Failure and behaviours.Success are valid instantiations |
| 76 | + |
| 77 | + workers_sequence = composites.Sequence( |
| 78 | + name="Workers", |
| 79 | + memory=True, |
| 80 | + children=workers, |
| 81 | + ) |
| 82 | + on_failure_return_status = composites.Sequence( |
| 83 | + name="On Failure Return Failure", |
| 84 | + memory=True, |
| 85 | + children=[on_failure, behaviours.Failure(name="Failure")], |
| 86 | + ) |
| 87 | + on_failure_subtree = composites.Selector( |
| 88 | + name="On Failure", |
| 89 | + memory=True, |
| 90 | + children=[workers_sequence, on_failure_return_status], |
| 91 | + ) |
| 92 | + if return_on_success_status: |
| 93 | + on_success_return_status = on_success |
| 94 | + else: |
| 95 | + on_success_return_status = composites.Selector( |
| 96 | + name="On Success Return Success", |
| 97 | + memory=True, |
| 98 | + children=[on_success, behaviours.Success(name="Success")], |
| 99 | + ) |
| 100 | + on_success_subtree = composites.Sequence( |
| 101 | + name="On Success", |
| 102 | + memory=True, |
| 103 | + children=[on_failure_subtree, on_success_return_status], |
| 104 | + ) |
| 105 | + root = OnPreempt( |
| 106 | + name=name, |
| 107 | + child=on_success_subtree, |
| 108 | + on_preempt=on_preempt, |
| 109 | + single_tick=on_preempt_single_tick, |
| 110 | + period_ms=on_preempt_period_ms, |
| 111 | + timeout=on_preempt_timeout, |
| 112 | + ) |
| 113 | + |
| 114 | + return root |
0 commit comments