Skip to content

Commit

Permalink
call on_exit from state_machine and use that to handle concurrency co…
Browse files Browse the repository at this point in the history
…ntainer exit
  • Loading branch information
David Conner committed Jul 24, 2024
1 parent 426bdc7 commit 141e231
Show file tree
Hide file tree
Showing 3 changed files with 21 additions and 25 deletions.
23 changes: 8 additions & 15 deletions flexbe_core/flexbe_core/core/concurrency_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,11 +117,7 @@ def _execute_current_state(self):
CommandFeedback(command='transition',
args=[command_msg.target, self.name]))
Logger.localwarn(f"--> Manually triggered outcome {outcome} of concurrency container '{self.name}'")
self.on_exit(self.userdata,
states=[s for s in self._states if (s.name not in self._returned_outcomes
or self._returned_outcomes[s.name] is None)
]
)

self._returned_outcomes = {}
self._current_state = None
self._last_outcome = outcome
Expand Down Expand Up @@ -154,6 +150,7 @@ def _execute_current_state(self):
self._returned_outcomes[state.name] = outcome
with UserData(reference=self._userdata, remap=self._remappings[state.name],
input_keys=state.input_keys, output_keys=state.output_keys) as userdata:
Logger.localinfo(f" CC '{self}' manual transition and on exit for '{state}'")
state.on_exit(userdata)

# ConcurrencyContainer bypasses normal operatable state handling of manual request, so do that here
Expand Down Expand Up @@ -211,19 +208,11 @@ def _execute_current_state(self):
if outcome is None:
return None

# trigger on_exit for those states that are not done yet
self.on_exit(self.userdata,
states=[s for s in self._states if (s.name not in self._returned_outcomes
or self._returned_outcomes[s.name] is None)])
self._returned_outcomes = {}
# right now, going out of a concurrency container may break sync
# thus, as a quick fix, explicitly request sync again on any output
# self._inner_sync_request = True
self._current_state = None

if self._is_controlled:
# reset previously requested outcome if applicable
if self._last_requested_outcome is not None and outcome is None:
if self._last_requested_outcome != outcome:
self._pub.publish(Topics._OUTCOME_REQUEST_TOPIC, OutcomeRequest(outcome=255, target=self.path))
self._last_requested_outcome = None

Expand Down Expand Up @@ -273,6 +262,7 @@ def _execute_single_state(self, state, force_exit=False):
def on_enter(self, userdata): # pylint: disable=W0613
"""Call on entering the concurrency container."""
super().on_enter(userdata)
self._returned_outcomes = {}
for state in self._states:
# Force on_enter at state level (userdata passed by _execute_single_state)
state._entering = True # force state to handle enter on first execute
Expand All @@ -281,10 +271,13 @@ def on_enter(self, userdata): # pylint: disable=W0613
def on_exit(self, userdata, states=None):
"""Call when concurrency container exits."""
for state in self._states if states is None else states:
if state in self._returned_outcomes:
if state.name in self._returned_outcomes and self._returned_outcomes[state.name] is not None:
continue # skip states that already exited themselves
Logger.localinfo(f" CC '{self}' exiting contained state '{state}'")
self._execute_single_state(state, force_exit=True)
self._current_state = None
self._returned_outcomes = {}
self._entering = True

def get_deep_states(self):
"""
Expand Down
14 changes: 8 additions & 6 deletions flexbe_core/flexbe_core/core/operatable_state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -416,13 +416,15 @@ def _notify_stop(self):
super()._notify_stop()
self._structure = None # Flag for destruction

def on_exit(self, userdata):
def on_exit(self, userdata=None):
"""Call on exiting the statemachine."""
self._entering = True
if self._current_state is not None:
udata = UserData(reference=self.userdata,
input_keys=self._current_state.input_keys,
output_keys=self._current_state.output_keys,
remap=self._remappings[self._current_state.name])
with UserData(reference=self._userdata,
input_keys=self._current_state.input_keys,
output_keys=self._current_state.output_keys,
remap=self._remappings[self._current_state.name]) as udata:
# Pass userdata to internal states matching as defined in state_machine
self._current_state.on_exit(udata)
self._current_state._entering = True
self._current_state.on_exit(udata)
self._current_state = None
9 changes: 5 additions & 4 deletions flexbe_core/flexbe_core/core/state_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ def execute(self, userdata):
# Logger.localinfo(f"Entering StateMachine '{self.name}' "
# f"({self._state_id}) initial state='{self._current_state.name}'")
outcome = self._execute_current_state()

if outcome:
# Exit this statemachine
self.on_exit(self._userdata)

return outcome

def _execute_current_state(self):
Expand All @@ -155,11 +160,7 @@ def _execute_current_state(self):

self._current_state = self._labels.get(target)
if self._current_state is None:
Logger.localinfo(f" SM '{self.name}' ({self.id}) returning '{target}' ")
return target
# else:
# Logger.localinfo(f" SM '{self.name}' ({self.id}) updated current state to "
# f"'{self._current_state.name}' ({self._current_state._state_id}) given outcome='{target}' ")

return None

Expand Down

0 comments on commit 141e231

Please sign in to comment.