Listeners & Callbacks
  • 2 Minutes to read

Listeners & Callbacks


Article summary

In modern application development, Event-Driven development has been increasing in popularity. Developers nowadays are widely using Synchronous and Asynchronous callbacks/listeners in their applications to achieve flexibility.

In our scenario, listeners & callbacks will allow you to asynchronously access robot information and get notified when a specific event or functionality is done.
Listeners are created by controllers according to its specific functionalities.

1. Why listeners and callbacks?

Synchronous example

Consider the following application to make Gary to greet when it detects someone in front of it:

from raya.application_base import RayaApplicationBase

class RayaApplication(RayaApplicationBase):
    async def setup(self):
        self.ai_vision = await self.enable_controller('ai_vision')
        self.sound = await self.enable_controller('sound')
        self.arms = await self.enable_controller('arms')

    async def loop(self):
        # Check for a person in front of the robot
        if(self.ai_vision.check_person_in_front()):
            # Start arm trajectory and speaking
            self.arm.predef_trajectory('right_hand_waving')
            self.sound.text_to_speech('Hello. How are you?')
            # Wait until arm trajectory and speaking finish
            await self.arm.await_while_moving()
            # Recovery initial robot position
            self.arm.move_to('home')
        # ...
        # Other application stuff
        # ...

    async def finish(self):
        self.motion.stop_motion()
    

This application needs to be polling the check_person_in_front() method all the time, because the loop() method needs to be aware of a person in front of the robot. When the condition is met, arm sequence and speaking starts, and the loop() method gets busy until the arm reaches the end of the trajectory. Then, the arm goes back to the initial position.

You could add extra functionalities to the program after the polling condition, but they will be paused during the execution of the greeting.

Example based on Listeners and Callbacks

If we would want to create the same application using listeners, it would look like:

from garyapi import GaryApplicationBase

class GaryApplication(GaryApplicationBase):
    async def setup(self):
        self.ai_vision = await self.enable_controller('ai_vision')
        self.sound = await self.enable_controller('sound')
        self.arms = await self.enable_controller('arms')
        # Local Variables
        self.greeting = False
        # Listeners
        self.ai_vision.create_object_detection_listener(object="person", callback=self.check_person_callback)

    async def loop(self):
        # ...
        # Other application stuff
        # ...

    async def finish(self):
        self.motion.stop_motion()
    
    def check_person_callback():
        if not self.greeting:
            self.arm.predef_trajectory('right_hand_waving', end_callback=self.end_arms_mov_callback)
            self.voice.text_to_speech('Hello. How are you?')
            self.greeting = True
    
    def end_arms_mov_callback():
        self.arm.move_to('home')
        self.greeting = False

After enabling the controllers, listeners are created with unique names in the setup() method. Each controller has a number of listeners acording to the possible conditions that its associated hardware can meet.

In the example, a check_person listener is created, and it will be triggered when the frontal camera of the robot detects a person. The controller automatically checks the condition and calls the callback when it is met. The callback, which is a method called check_person_callback(), executes the predef_trajectory() and text_to_speech(). In simple words, the robot starts greeting when it detects the person in front of it.

The predef_trajectory() method creates a future event that is executed when the trajectory finishes, so you don't need to create a listener for it. When the callback end_arms_mov_callback() is executed, it brings the arm to its original position.

The functionality of the last application is the same than the first one, but now you have an empty loop() method, and procedures you execute there will not be interrupted.

2. Exceptions

Listeners creation process can throw the following exceptions:

ExceptionCondition
RayaListenerExceptionException from listener method.
RayaListenerAlreadyCreatedProvided listener name already used to create another listener.
RayaListenerUnknownProvided listener name not recognized.
RayaInvalidCallbackInvalid or not callable callback.

In addition, each specific controller can throw specific exceptions (see each controller documentation for more detailed information).

See the Full List of Exceptions


Was this article helpful?

What's Next