From 9b51aed4078d3fbccc00d05c3e74d8714bcffbd6 Mon Sep 17 00:00:00 2001 From: Thomas Sedlmayer Date: Tue, 14 Jul 2026 15:07:55 +0200 Subject: [PATCH] fix: defer triggered actions until after activation frame Signed-off-by: Thomas Sedlmayer --- src/osc_simulator/simulation/engine.py | 34 +++++++++++++++++++++++--- 1 file changed, 31 insertions(+), 3 deletions(-) diff --git a/src/osc_simulator/simulation/engine.py b/src/osc_simulator/simulation/engine.py index d9698d5..fff8791 100644 --- a/src/osc_simulator/simulation/engine.py +++ b/src/osc_simulator/simulation/engine.py @@ -86,6 +86,18 @@ def __init__( self.completed = False +class _PendingAction: + def __init__( + self, + event_state: _EventState, + event_key: str, + action_state: _ActionExecutionState, + ) -> None: + self.event_state = event_state + self.event_key = event_key + self.action_state = action_state + + class SimulationEngine: """Execute a :class:`~osc_simulator.parser.openscenario.Scenario`.""" @@ -121,6 +133,7 @@ def __init__(self, scenario: Scenario, step_size: float = 0.05) -> None: self._completed_elements: set[str] = set() self._storyboard_lookup = self._build_storyboard_lookup() self._condition_prev_values: dict[int, bool] = {} + self._pending_actions: list[_PendingAction] = [] # ------------------------------------------------------------------ # Public @@ -132,6 +145,7 @@ def run(self) -> Iterator[tuple[float, Any]]: gt = self._build_ground_truth() yield self._time, gt self._advance() + self._apply_pending_actions() # Emit final frame at stop time yield self._time, self._build_ground_truth() @@ -429,9 +443,7 @@ def _evaluate_maneuver( for action in event.actions: action_state = _ActionExecutionState(action, list(actors)) state.action_states.append(action_state) - action_state.started = True - self._apply_action(action, actors) - action_state.completed = self._is_action_completed(action, actors) + self._pending_actions.append(_PendingAction(state, event_key, action_state)) if not state.action_states or all(a.completed for a in state.action_states): state.completed = True self._completed_elements.add(event_key) @@ -441,6 +453,8 @@ def _update_event_action_completion_states(self) -> None: if not state.started or state.completed: continue for action_state in state.action_states: + if not action_state.started: + continue if action_state.completed: continue action_state.completed = self._is_action_completed( @@ -449,6 +463,20 @@ def _update_event_action_completion_states(self) -> None: if all(a.completed for a in state.action_states): state.completed = True + def _apply_pending_actions(self) -> None: + pending_actions = self._pending_actions + self._pending_actions = [] + for pending in pending_actions: + action_state = pending.action_state + action_state.started = True + self._apply_action(action_state.action, action_state.actor_names) + action_state.completed = self._is_action_completed( + action_state.action, action_state.actor_names + ) + if all(a.completed for a in pending.event_state.action_states): + pending.event_state.completed = True + self._completed_elements.add(pending.event_key) + def _story_key(self, story: Story) -> str: return f"story:{story.name}"