- 2 Minutes to read
Listeners & Callbacks
- 2 Minutes to read
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?
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
text_to_speech(). In simple words, the robot starts greeting when it detects the person in front of it.
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.
Listeners creation process can throw the following exceptions:
|Exception from listener method.|
|Provided listener name already used to create another listener.|
|Provided listener name not recognized.|
|Invalid 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