Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix sequence init #108

Merged
merged 5 commits into from
Apr 5, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions dynamic_stack_decider/CMakeLists.txt

This file was deleted.

7 changes: 2 additions & 5 deletions dynamic_stack_decider/dynamic_stack_decider/dsd.py
Original file line number Diff line number Diff line change
@@ -201,13 +201,10 @@ def _bind_modules(self, element):
else:
raise ValueError(f'Unknown parser tree element type "{type(element)}" for element "{element}"!')

def _init_element(self, element: AbstractTreeElement):
def _init_element(self, element: AbstractTreeElement) -> AbstractStackElement:
"""Initializes the module belonging to the given element."""
if isinstance(element, SequenceTreeElement):
initialized_actions = list()
for action in element.action_elements:
initialized_actions.append(action.module(self.blackboard, self, action.parameters))
return SequenceElement(self.blackboard, self, initialized_actions)
return SequenceElement(self.blackboard, self, element.action_elements, self._init_element)
else:
return element.module(self.blackboard, self, element.parameters)

50 changes: 34 additions & 16 deletions dynamic_stack_decider/dynamic_stack_decider/sequence_element.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
from typing import TYPE_CHECKING
from typing import TYPE_CHECKING, Callable

from dynamic_stack_decider.abstract_action_element import AbstractActionElement
from dynamic_stack_decider.abstract_stack_element import AbstractStackElement
from dynamic_stack_decider.tree import AbstractTreeElement, ActionTreeElement

if TYPE_CHECKING:
from dynamic_stack_decider.dsd import DSD
@@ -16,21 +17,44 @@ class SequenceElement(AbstractStackElement):
This is not an abstract class to inherit from.
"""

def __init__(self, blackboard, dsd: "DSD", actions: list[AbstractActionElement]):
def __init__(
self,
blackboard,
dsd: "DSD",
actions: list[ActionTreeElement],
init_function: Callable[[AbstractTreeElement], AbstractStackElement],
):
"""
:param actions: list of initialized action elements
:param blackboard: Shared blackboard for data exchange and code reuse between elements
:param dsd: The stack decider which has this element on its stack.
:param actions: list of of action tree elements / blueprints for the actions
:param init_function: A function that initializes an action element creating a stack element from a tree element
"""
super().__init__(blackboard, dsd, dict())
# Here we store the 'blueprints' of the actions
self.actions = actions
self.current_action_index = 0
# We store a reference to the function that initializes the action elements based on the tree
self._init_function = init_function
# Here we store the actual instances of the active action
# The action is only initialized when it is the current action
assert len(actions) > 0, "A sequence element must contain at least one action"
self.current_action: AbstractActionElement = self._init_function(actions[0])
# Where we are in the sequence
self.current_action_index: int = 0

def perform(self, reevaluate=False):
"""
Perform the current action of the sequence. See AbstractStackElement.perform() for more information

:param reevaluate: Ignored for SequenceElements
"""
# Log the active element
self.publish_debug_data("Active Element", self.current_action.name)
# Pass the perform call to the current action
self.current_action.perform()
# If the action had debug data, publish it
if self.current_action._debug_data:
self.publish_debug_data("Corresponding debug data", self.current_action._debug_data)

def pop_one(self):
"""
@@ -39,30 +63,24 @@ def pop_one(self):
assert not self.in_last_element(), (
"It is not possible to pop a single element when" "the last element of the sequence is active"
)
# Increment the index to the next action and initialize it
self.current_action_index += 1
# We initilize the current action here to avoid the problem described in
# https://github.com/bit-bots/dynamic_stack_decider/issues/107
self.current_action = self._init_function(self.actions[self.current_action_index])

def in_last_element(self):
"""Returns if the current element is the last element of the sequence"""
return self.current_action_index == len(self.actions) - 1

@property
def current_action(self) -> AbstractActionElement:
"""
Returns the currently executed action of the sequence element
"""
return self.actions[self.current_action_index]

def repr_dict(self) -> dict:
"""
Represent this stack element as dictionary which is JSON encodable
"""
self.publish_debug_data("Active Element", self.current_action.name)
if self.current_action._debug_data:
self.publish_debug_data("Corresponding debug data", self.current_action._debug_data)
data = {
"type": "sequence",
"current_action_id": self.current_action_index,
"content": [elem.repr_dict() for elem in self.actions],
"current_action_index": self.current_action_index,
"current_action": self.current_action.repr_dict(),
"debug_data": self._debug_data,
}
self.clear_debug_data()
Original file line number Diff line number Diff line change
@@ -182,7 +182,7 @@ def param_string(params: dict) -> str:
# Spaces for indentation
action_label = " "
# Mark current element (if this sequence is on the stack)
if stack_element is not None and i == stack_element["current_action_id"]:
if stack_element is not None and i == stack_element["current_action_index"]:
action_label += "--> "
action_label += action["name"] + param_string(action["parameters"])
label.append(action_label)
@@ -312,6 +312,9 @@ def to_q_item_model(self):
# Start with the root/bottom of the stack
stack_element = self.stack

# Store the corresponding tree element
tree_element = self.tree

# Go through all stack elements
while stack_element is not None:
# Sanity check
@@ -327,7 +330,7 @@ def to_q_item_model(self):
# Set the text of the item
if stack_element["type"] == "sequence":
# Get the names of all actions
action_names = [element["name"] for element in stack_element["content"]]
action_names = [action["name"] for action in tree_element["action_elements"]]
# Join them together and set the text
elem_item.setText("Sequence: " + ", ".join(action_names))
else:
@@ -351,6 +354,9 @@ def to_q_item_model(self):

# Go to next element
stack_element = stack_element["next"]
# Also select the corresponding tree element if there are any
if "children" in tree_element and stack_element is not None:
tree_element = tree_element["children"][stack_element["activation_reason"]]

return model