diff --git a/src/control_backend/agents/bdi/agentspeak_generator.py b/src/control_backend/agents/bdi/agentspeak_generator.py index 630f75c..3c45c56 100644 --- a/src/control_backend/agents/bdi/agentspeak_generator.py +++ b/src/control_backend/agents/bdi/agentspeak_generator.py @@ -235,6 +235,39 @@ class AgentSpeakGenerator: ) ) + def _add_stop_plan(self, phase: Phase): + """ + Adds a plan to stop the program. This just skips to the end phase, + where there is no behavior defined. + """ + self._asp.plans.append( + AstPlan( + TriggerType.ADDED_GOAL, + AstLiteral("stop"), + [AstLiteral("phase", [AstString(phase.id)])], + [ + AstStatement( + StatementType.DO_ACTION, + AstLiteral( + "notify_transition_phase", + [ + AstString(phase.id), + AstString("end") + ] + ) + ), + AstStatement( + StatementType.REMOVE_BELIEF, + AstLiteral("phase", [AstVar("Phase")]), + ), + AstStatement( + StatementType.ADD_BELIEF, + AstLiteral("phase", [AstString("end")]) + ) + ] + ) + ) + def _process_phases(self, phases: list[Phase]) -> None: """ Processes all phases in the program and their transitions. @@ -277,6 +310,9 @@ class AgentSpeakGenerator: for trigger in phase.triggers: self._process_trigger(trigger, phase) + # Add force transition to end phase + self._add_stop_plan(phase) + def _add_phase_transition(self, from_phase: Phase | None, to_phase: Phase | None) -> None: """ Adds plans for transitioning between phases. @@ -567,6 +603,7 @@ class AgentSpeakGenerator: - check_triggers: When no triggers are applicable - transition_phase: When phase transition conditions aren't met - force_transition_phase: When forced transitions aren't possible + - stop: When we are already in the end phase """ # Trigger fallback self._asp.plans.append( @@ -598,6 +635,16 @@ class AgentSpeakGenerator: ) ) + # Stop fallback + self._asp.plans.append( + AstPlan( + TriggerType.ADDED_GOAL, + AstLiteral("stop"), + [], + [AstStatement(StatementType.EMPTY, AstLiteral("true"))], + ) + ) + @singledispatchmethod def _astify(self, element: ProgramElement) -> AstExpression: """ diff --git a/src/control_backend/agents/bdi/bdi_core_agent.py b/src/control_backend/agents/bdi/bdi_core_agent.py index ffad8ce..905c392 100644 --- a/src/control_backend/agents/bdi/bdi_core_agent.py +++ b/src/control_backend/agents/bdi/bdi_core_agent.py @@ -176,6 +176,8 @@ class BDICoreAgent(BaseAgent): self._force_norm(msg.body) case "force_next_phase": self._force_next_phase() + case "stop": + self._stop() case _: self.logger.warning("Received unknown user interruption: %s", msg) @@ -335,6 +337,11 @@ class BDICoreAgent(BaseAgent): self.logger.info("Manually forced phase transition.") + def _stop(self): + self._set_goal("stop") + + self.logger.info("Stopped the program (skipped to end phase).") + def _add_custom_actions(self) -> None: """ Add any custom actions here. Inside `@self.actions.add()`, the first argument is diff --git a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py index 5553fe8..83854fb 100644 --- a/src/control_backend/agents/user_interrupt/user_interrupt_agent.py +++ b/src/control_backend/agents/user_interrupt/user_interrupt_agent.py @@ -164,6 +164,12 @@ class UserInterruptAgent(BaseAgent): else: self.logger.info("Sent resume command.") + case "stop": + self.logger.debug( + "Received stop command." + ) + await self._send_stop_command() + case "next_phase" | "reset_phase": await self._send_experiment_control_to_bdi_core(event_type) case _: @@ -422,4 +428,16 @@ class UserInterruptAgent(BaseAgent): ) await self.send(vad_message) # Voice Activity Detection and Visual Emotion Recognition agents - self.logger.info("Sent resume command to VAD and VED agents.") \ No newline at end of file + self.logger.info("Sent resume command to VAD and VED agents.") + + async def _send_stop_command(self): + """ + Send a command to the BDI to stop the program (i.e., skip to end phase). + """ + msg = InternalMessage( + to=settings.agent_settings.bdi_core_name, + body="", + thread="stop" + ) + + await self.send(msg)