Skip to content

Commit

Permalink
[sequences] no memory -> partial memory
Browse files Browse the repository at this point in the history
Resolves #329.
  • Loading branch information
stonier committed Jan 1, 2023
1 parent 617ed97 commit 3845a9d
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 15 deletions.
18 changes: 14 additions & 4 deletions py_trees/composites.py
Original file line number Diff line number Diff line change
Expand Up @@ -439,15 +439,19 @@ def tick(self):

# initialise
index = 0
if self.status != common.Status.RUNNING or not self.memory:
if self.status != common.Status.RUNNING:
self.current_child = self.children[0] if self.children else None
for child in self.children:
if child.status != common.Status.INVALID:
child.stop(common.Status.INVALID)
# user specific initialisation
self.initialise()
else: # self.memory is True and status is RUNNING
self.initialise() # user specific initialisation
elif self.memory and common.Status.RUNNING:
index = self.children.index(self.current_child)
elif not self.memory and common.Status.RUNNING:
self.current_child = self.children[0] if self.children else None
else:
# previous conditional checks should cover all variations
raise RuntimeError("Sequence reached an unknown / invalid state")

# customised work
self.update()
Expand All @@ -465,6 +469,12 @@ def tick(self):
yield node
if node is child and node.status != common.Status.SUCCESS:
self.status = node.status
if not self.memory:
# invalidate the remainder of the sequence
# i.e. kill dangling runners
for child in itertools.islice(self.children, index + 1, None):
if child.status != common.Status.INVALID:
child.stop(common.Status.INVALID)
yield self
return
try:
Expand Down
54 changes: 43 additions & 11 deletions tests/test_sequences.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,53 @@ def assert_details(text, expected, result):
# Tests
##############################################################################

def test_running_with_no_memory_children_do_not_reset():
console.banner('Tick-Running with No Memory - Children Do Not Reset')
assert_banner()
root = py_trees.composites.Sequence(name="Sequence w/o Memory", memory=False)
child_1 = py_trees.behaviours.TickCounter(1) # R-S
child_2 = py_trees.behaviours.Success('Success')
root.add_children([child_1, child_2])

# Expect
# 1. R - [R, I]
# 2. S - [S, S] <- NB: first child doesn't reset

root.tick_once()
print(py_trees.display.unicode_tree(root, show_status=True))
assert_details("1::Sequence Status", py_trees.common.Status.RUNNING, root.status)
assert(root.status == py_trees.common.Status.RUNNING)
assert_details("2::Child 1 Status", py_trees.common.Status.RUNNING, child_1.status)
assert(child_1.status == py_trees.common.Status.RUNNING)
assert_details("2::Child 2 Status", py_trees.common.Status.INVALID, child_2.status)
assert(child_2.status == py_trees.common.Status.INVALID)

def test_running_with_no_memory():
console.banner('Tick-Running with No Memory')
root.tick_once()
print(py_trees.display.unicode_tree(root, show_status=True))
assert_details("2::Selector Status", py_trees.common.Status.SUCCESS, root.status)
assert(root.status == py_trees.common.Status.SUCCESS)
assert_details("2::Child 1 Status", py_trees.common.Status.SUCCESS, child_1.status)
assert(child_1.status == py_trees.common.Status.SUCCESS)
assert_details("2::Child 2 Status", py_trees.common.Status.SUCCESS, child_2.status)
assert(child_2.status == py_trees.common.Status.SUCCESS)

def test_running_with_no_memory_invalidate_dangling_runners():
console.banner('Tick-Running with No Memory - Invalidate Dangling Runners')
assert_banner()
root = py_trees.composites.Sequence(name="Sequence w/o Memory", memory=False)
child_1 = py_trees.behaviours.StatusSequence(
name="Success-Failure",
name="Success-Running",
sequence=[py_trees.common.Status.SUCCESS,
py_trees.common.Status.FAILURE],
py_trees.common.Status.RUNNING],
eventually=None
)
child_2 = py_trees.behaviours.Running(name="Running")
)
child_2 = py_trees.behaviours.Running('Running')
root.add_children([child_1, child_2])

# Expect
# 1. R - [S, R]
# 2. R - [R, I] <- NB: second child is invalidated

root.tick_once()
print(py_trees.display.unicode_tree(root, show_status=True))
assert_details("1::Sequence Status", py_trees.common.Status.RUNNING, root.status)
Expand All @@ -63,14 +96,13 @@ def test_running_with_no_memory():

root.tick_once()
print(py_trees.display.unicode_tree(root, show_status=True))
assert_details("2::Selector Status", py_trees.common.Status.FAILURE, root.status)
assert(root.status == py_trees.common.Status.FAILURE)
assert_details("2::Child 1 Status", py_trees.common.Status.FAILURE, child_1.status)
assert(child_1.status == py_trees.common.Status.FAILURE)
assert_details("2::Selector Status", py_trees.common.Status.RUNNING, root.status)
assert(root.status == py_trees.common.Status.RUNNING)
assert_details("2::Child 1 Status", py_trees.common.Status.RUNNING, child_1.status)
assert(child_1.status == py_trees.common.Status.RUNNING)
assert_details("2::Child 2 Status", py_trees.common.Status.INVALID, child_2.status)
assert(child_2.status == py_trees.common.Status.INVALID)


def test_running_with_memory_proceeds():
# This test should check two things:
# 1) Skipped higher priorities are set to INVALID
Expand Down

0 comments on commit 3845a9d

Please sign in to comment.