Skip to content

Commit

Permalink
MNT: moved IPC signalling mechanisms to live within ActionNode
Browse files Browse the repository at this point in the history
  • Loading branch information
joshc-slac committed Sep 4, 2024
1 parent 8cacd64 commit a2c883f
Show file tree
Hide file tree
Showing 5 changed files with 34 additions and 25 deletions.
13 changes: 6 additions & 7 deletions beams/behavior_tree/ActionNode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,17 @@ def __init__(
name: str,
work_func: Callable[[Any], None],
completion_condition: Callable[[Any], bool],
work_gate=Event(),
work_lock=Lock(),
**kwargs,
): # TODO: can add failure condition argument...
super().__init__(name)
# print(type(self.status))
self.__volatile_status__ = VolatileStatus(self.status)
# TODO may want to instantiate these locally and then decorate the passed work function with them
self.work_gate = work_gate
self.lock = work_lock

# Interprocess signalling mechanisms
self.__volatile_status__ = VolatileStatus(self.status) # Shares BT Status by wrapping Value
self.work_gate = Event() # Mechanism to avoid busy wait / signal node has been py_trees "initialized"

self.worker = ActionWorker(proc_name=name,
volatile_status=self.__volatile_status__,
wait_for_tick=self.work_gate,
work_func=work_func,
comp_cond=completion_condition,
stop_func=None
Expand Down
10 changes: 8 additions & 2 deletions beams/behavior_tree/ActionWorker.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
"""
A worker specialized to execute ActionNode work functions
"""
from multiprocessing import Event
from typing import Callable, Any, Optional

from epics.multiproc import CAProcess
Expand All @@ -14,13 +15,18 @@ def __init__(self,
proc_name: str,
volatile_status: VolatileStatus,
work_func: Callable[[Any], None],
wait_for_tick: Event,
comp_cond: Callable[[Any], bool],
stop_func: Optional[Callable[[None], None]] = None):
super().__init__(proc_name=proc_name,
stop_func=stop_func,
work_func=work_func,
proc_type=CAProcess,
add_args=(comp_cond, volatile_status))
add_args=(comp_cond, volatile_status, wait_for_tick))

# Note: there may be a world where we define a common stop_func here in which case
# the class may have maintain a reference to voltaile_status and or comp_cond
# the class may have maintain a reference to voltaile_status and or comp_cond

# Note: the function passed to action_worker needs to have 4 arguments of
# (self, comp_condition, volatile_status, event). I am considering using functools.wraps and inspect.signature
# to ensure that the work function is well formatted...
8 changes: 6 additions & 2 deletions beams/tests/test_check_and_do.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import time
from typing import Callable, Any
from multiprocessing import Value

import py_trees

from beams.behavior_tree import ActionNode, CheckAndDo, ConditionNode
from beams.behavior_tree import ActionNode, CheckAndDo, ConditionNode, VolatileStatus


class TestTask:
def test_check_and_do(self, capsys):
percentage_complete = Value("i", 0)

def thisjob(myself, comp_condition, volatile_status) -> None:
def thisjob(myself,
comp_condition: Callable[[Any], None],
volatile_status: VolatileStatus,
*args) -> None:
# TODO: grabbing intended keyword argument. Josh's less than pythonic mechanism for closures
volatile_status.set_value(py_trees.common.Status.RUNNING)
while not comp_condition(percentage_complete.value):
Expand Down
7 changes: 6 additions & 1 deletion beams/tests/test_leaf_node.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import time
from typing import Callable, Any
from multiprocessing import Value

import py_trees

from beams.behavior_tree.ActionNode import ActionNode
from beams.behavior_tree.ConditionNode import ConditionNode
from beams.behavior_tree.VolatileStatus import VolatileStatus


class TestTask:
Expand All @@ -13,7 +15,10 @@ def test_action_node(self, capsys):
# For test
percentage_complete = Value("i", 0)

def thisjob(myself, comp_condition, volatile_status) -> None:
def thisjob(myself,
comp_condition: Callable[[Any], None],
volatile_status: VolatileStatus,
*args) -> None:
volatile_status.set_value(py_trees.common.Status.RUNNING)
while not comp_condition(percentage_complete.value):
py_trees.console.logdebug(f"yuh {percentage_complete.value}, {volatile_status.get_value()}")
Expand Down
21 changes: 8 additions & 13 deletions beams/tree_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,11 +167,10 @@ class SetPVActionItem(ActionItem):
termination_check: ConditionItem = field(default_factory=ConditionItem)

def get_tree(self) -> ActionNode:
# TODO: can I put these two lines in a decorator which action node uses on the function?
wait_for_tick = Event()
wait_for_tick_lock = Lock()

def work_func(myself, comp_condition, volatile_status):
def work_func(myself,
comp_condition: Callable[[Any], bool],
volatile_status: VolatileStatus,
wait_for_tick: Event()):
py_trees.console.logdebug(f"WAITING FOR INIT {os.getpid()} "
f"from node: {self.name}")
wait_for_tick.wait()
Expand Down Expand Up @@ -206,8 +205,6 @@ def work_func(myself, comp_condition, volatile_status):
name=self.name,
work_func=work_func,
completion_condition=comp_cond,
work_gate=wait_for_tick,
work_lock=wait_for_tick_lock,
)

return node
Expand All @@ -222,10 +219,10 @@ class IncPVActionItem(ActionItem):

# TODO: DRY this out a bit
def get_tree(self) -> ActionNode:
wait_for_tick = Event()
wait_for_tick_lock = Lock()

def work_func(myself, comp_condition, volatile_status):
def work_func(myself,
comp_condition: Callable[[Any], bool],
volatile_status: VolatileStatus,
wait_for_tick: Event()):
py_trees.console.logdebug(f"WAITING FOR INIT {os.getpid()} "
f"from node: {self.name}")
wait_for_tick.wait()
Expand Down Expand Up @@ -260,8 +257,6 @@ def work_func(myself, comp_condition, volatile_status):
name=self.name,
work_func=work_func,
completion_condition=comp_cond,
work_gate=wait_for_tick,
work_lock=wait_for_tick_lock,
)

return node
Expand Down

0 comments on commit a2c883f

Please sign in to comment.