-
Notifications
You must be signed in to change notification settings - Fork 2
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
FEAT: re-enable multi shot trees, ActionNode work function decorator, implement tests to utilize multi shot trees #43
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I want to think about this a bit more, I think there's an opportunity here to really clean this up nicely. But functionally this does what we wanted it to do
beams/behavior_tree/ActionNode.py
Outdated
@@ -12,72 +12,80 @@ | |||
logger = logging.getLogger(__name__) | |||
|
|||
|
|||
def wrapped_action_work(func): | |||
def work_wrapper( | |||
work_self, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We claimed to have removed unclear self
arguments but here it is again? I think this should explicitly pass in the do_work.value
and avoid accessing members of an object that might have non-process safe members, for the sake of being clear to our future selves exactly what values we can manipulate
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're not wrong here, I debated this a little bit when implementing. I thought the most clarity came from this notion of work_self being understood to be an instance of the actual Worker (ActionWorker) that is running this thing made things the most clear but your ownership argument is very compelling.
To be clear this would be considered the "super-super" user level..... upon more reflection the "extensibility" I was aiming at with this decision may be misplaced. I can cut this change into the PR
beams/behavior_tree/ActionNode.py
Outdated
@@ -12,72 +12,80 @@ | |||
logger = logging.getLogger(__name__) | |||
|
|||
|
|||
def wrapped_action_work(func): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the future we'll probably have to come up with a more descriptive name for this decorator, since we'll probably have different ones for different types of actions. This one is a multi-shot decorator, but do we want ones with different timeout options in the future?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thats a great perspective. This could also be the base wrapper or the wrapper could take a time out argument nice way to advertise to user space
beams/behavior_tree/ActionNode.py
Outdated
@@ -12,72 +12,80 @@ | |||
logger = logging.getLogger(__name__) | |||
|
|||
|
|||
def wrapped_action_work(func): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my main gripe discomfort with this whole construction is how it masks the arguments that the (decorated) work function requires. We pass some of them into ActionWorker
, then another in Worker
, ultimately requiring a bunch of examination. This is something that I think has existed for a while and is a bit exacerbated by this decorator
A musing:
- Where we can we might have the decorator take arguments, to reduce the amount of passing around to
Worker
subclasses. This also makes clear which args come from which scope, though it requires us to decorate the function in said scope
@wrapped_action_work(volatile_status, completion_condition) -> Callable[[work_gate, log_queue, log_configurer], None]
- Maybe this also lets us keep the expected work function signature consistent inside the worker?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahhh I actually liked this abstraction. The work function is just the work function. All the "super" user who is not just editing config files has to concern themself with is writing the function that (thanks to #41) just has to logically return SUCCESS or FAILURE
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But agree that we should much more thoroughly document how and why this works, likely with a small passage in ActionWorker that can be grabbed for more general documentation purposes
ac37dff
to
739ea4b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is good step forward, I don't have specific objections about the core ideas of the PR or the implementation.
Currently the PR text itself reads confusingly:
Title: multi shot trees
description: decorators (and motivation for decorators)
motivation: multi shot trees
I'd like to see this rearranged for documentation purposes so that we can document exactly what part of this PR enabled multi-shot trees (I assume it's clearing the work_gate + maybe one other thing?)
beams/sequencer/helpers/Worker.py
Outdated
@@ -42,12 +44,12 @@ def start_work(self): | |||
logger.debug("Starting work") | |||
|
|||
def stop_work(self): | |||
logger.info("Calling stop work on") | |||
logger.debug("Calling stop work on") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small pre-existing condition I'd like to note: these and similar log messages lack any sort of helpful context
"Calling stop work on {name}" for example
Test failures stem from the use of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this looks good. If our offline conversations are any indication, this will be an often-refactored part of our codebase, haha. But I do think this is an improvement.
beams/tests/test_check_and_do.py
Outdated
|
||
logger = logging.getLogger(__name__) | ||
|
||
|
||
def test_check_and_do(): | ||
percentage_complete = Value("i", 0) | ||
|
||
@wrapped_action_work(loop_period_sec=0.001) | ||
def thisjob(comp_condition) -> None: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type hint is now incorrect, this should return a Status
I think aside from tests passing, I'd just want some documentation in the docstring of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks mostly good to me. Some stray questions
beams/tests/test_worker.py
Outdated
@@ -32,7 +32,7 @@ def work_func(self): | |||
def test_inline_instantation(self): | |||
val = Value("i", 10) | |||
|
|||
def work_func(): | |||
def work_func(myself): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this myself
crept back in here, and it remains un-used. The work function signature actually gets passed a Value
now right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, myself should be better named to represent Value. This parameter is now needed after we decided to remove the self and explicitly pass just the Value. We could even make a wrapper to edit the work function signature again...
BEAMS/beams/sequencer/helpers/Worker.py
Lines 33 to 35 in 885ddb0
self.work_proc = proc_type(target=self.work_func, | |
name=self.proc_name, | |
args=(self.do_work, *self.add_args,)) |
But we can hold off on that and leave this breadcrumb for future git blamers
beams/tree_config.py
Outdated
try: | ||
# Set to running | ||
value = caget(self.termination_check.pv) | ||
value = caget(self.pv) # double caget, this |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is there a continuation to this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is adding, Zach addresses it in future PR so I am happy to just leave call out
beams/typing_helper.py
Outdated
Evaluatable = Callable[..., bool] | ||
|
||
''' | ||
Defines signature for an ActionNode work loop function handle. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Giga nitpick: I think technically block comments are supposed to have each line prepended with #
. In my head triple quotes == docstrings, so this was very slightly disorienting to me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good to know
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm somewhat curious what the pep8 reasoning is on this one
Maybe something to do with making it very syntactically clear which text is a comment and which text is to some extent functional?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm dropping a before-I-leave approval stamp, I like this. It's worth addressing small things now if they aren't too scope-changing or aren't handled elsewhere.
check flips state
…his adds clarity. clean up of some Worker debug messages, fixed wrong type hint, made compatible with py 3.10 and 3.9
7310454
to
573ab0b
Compare
573ab0b
to
dcefc01
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 LTGM
Description
beams.typing_helper.ActionNodeWorkLoop
, this is the type return by the decoratorActionNode
requires a function to be called within a while loop that lives for the lifecycle of the program.ActionWorker
handles the spawning of the process that contains the while loop, it does so by extending theWorker
base class. This spawned process must have a function signature ofbeams.typing_helper.ActionNodeWorkLoop
such that all needed variables can be passed to the spawned processActionWorker
which spawns theActionNodeWorkLoop
the function which is called at the specified loop frequency must have the signature of typeActionNodeWorkFunction
which ensures a BT status is generated each loop cycle.Motivation and Context
Sequence automation aims to actively manage the state of our configured devices such that should they transition to an undesired state they can automatically transition back along defined transition vectors. Up until this point all the work packaged in ActionNodes only ran once, this will keep it living with the life cycle of the program such that when the tree is traversed again and a CheckAndDo node notices a state is out of whack it can fire off the action to deal with it again!
How Has This Been Tested?
With unit tests
Alternatives:
There is a world where Worker when spawning the process only passes the Value that signals program termination. (This could also be an Event). This would allow the wrapper to have remained a bound member function; however using a decorator seems more pythonic in both the way that it signals to users, properly signatures the function, and percolates up errors.
docs/pre-release-notes.sh
and created a pre-release documentation page