feat: implemented force speech functionality in RI and refactored actuation_receiver tests
Before actuation_receiver tests started a receiver with real zmq context. This led to flaky tests because of port congestion issues. ref: N25B-386
This commit is contained in:
@@ -10,6 +10,7 @@ from robot_interface.endpoints.receiver_base import ReceiverBase
|
||||
from robot_interface.state import state
|
||||
|
||||
from robot_interface.core.config import settings
|
||||
from robot_interface.endpoints.gesture_settings import GestureTags
|
||||
|
||||
|
||||
class ActuationReceiver(ReceiverBase):
|
||||
@@ -24,8 +25,11 @@ class ActuationReceiver(ReceiverBase):
|
||||
|
||||
:ivar _tts_service: The text-to-speech service object from the Qi session.
|
||||
:vartype _tts_service: qi.Session | None
|
||||
|
||||
:ivar _animation_service: The animation/gesture service object from the Qi session.
|
||||
:vartype _animation_service: qi.Session | None
|
||||
"""
|
||||
def __init__(self, zmq_context, port=settings.agent_settings.actuating_receiver_port):
|
||||
def __init__(self, zmq_context, port=settings.agent_settings.actuation_receiver_port):
|
||||
super(ActuationReceiver, self).__init__("actuation")
|
||||
self.create_socket(zmq_context, zmq.SUB, port)
|
||||
self.socket.setsockopt_string(zmq.SUBSCRIBE, u"") # Causes block if given in options
|
||||
@@ -33,6 +37,7 @@ class ActuationReceiver(ReceiverBase):
|
||||
self._message_queue = Queue.Queue()
|
||||
self.message_thread = Thread(target=self._handle_messages)
|
||||
self.message_thread.start()
|
||||
self._animation_service = None
|
||||
|
||||
def _handle_speech(self, message):
|
||||
"""
|
||||
@@ -67,6 +72,7 @@ class ActuationReceiver(ReceiverBase):
|
||||
else:
|
||||
self._message_queue.put(text)
|
||||
|
||||
|
||||
def clear_queue(self):
|
||||
"""
|
||||
Safely drains all pending messages from the queue.
|
||||
@@ -79,6 +85,55 @@ class ActuationReceiver(ReceiverBase):
|
||||
pass
|
||||
logging.info("Message queue cleared.")
|
||||
|
||||
|
||||
def _handle_gesture(self, message, is_single):
|
||||
"""
|
||||
Handle a gesture actuation request.
|
||||
|
||||
:param message: The gesture to do, must contain properties "endpoint" and "data".
|
||||
:type message: dict
|
||||
|
||||
:param is_single: Whether it's a specific single gesture or a gesture tag.
|
||||
:type is_single: bool
|
||||
"""
|
||||
|
||||
gesture = message.get("data")
|
||||
if not gesture:
|
||||
logging.warn("Received gesture to do, but it lacks data.")
|
||||
return
|
||||
|
||||
if not isinstance(gesture, (str, unicode)):
|
||||
logging.warn("Received gesture to do but it is not a string.")
|
||||
return
|
||||
|
||||
logging.debug("Received gesture to do: {}".format(gesture))
|
||||
|
||||
if is_single:
|
||||
if gesture not in GestureTags.single_gestures:
|
||||
logging.warn("Received single gesture to do, but it does not exist in settings")
|
||||
return
|
||||
else:
|
||||
if gesture not in GestureTags.tags:
|
||||
logging.warn("Received single tag to do, but it does not exist in settings")
|
||||
return
|
||||
|
||||
if not state.qi_session: return
|
||||
# If state has a qi_session, we know that we can import qi
|
||||
import qi # Takes a while only the first time it's imported
|
||||
|
||||
if not self._animation_service:
|
||||
self._animation_service = state.qi_session.service("ALAnimationPlayer")
|
||||
|
||||
# Play the gesture. Pepper comes with predefined animations like "Wave", "Greet", "Clap"
|
||||
# You can also create custom animations using Choregraphe and upload them to the robot.
|
||||
if is_single:
|
||||
logging.debug("Playing single gesture: {}".format(gesture))
|
||||
getattr(qi, "async")(self._animation_service.run, gesture)
|
||||
else:
|
||||
logging.debug("Playing tag gesture: {}".format(gesture))
|
||||
getattr(qi, "async")(self._animation_service.runTag, gesture)
|
||||
|
||||
|
||||
def handle_message(self, message):
|
||||
"""
|
||||
Handle an actuation/speech message with the receiver.
|
||||
@@ -88,6 +143,10 @@ class ActuationReceiver(ReceiverBase):
|
||||
"""
|
||||
if message["endpoint"] == "actuate/speech":
|
||||
self._handle_speech(message)
|
||||
if message["endpoint"] == "actuate/gesture/tag":
|
||||
self._handle_gesture(message, False)
|
||||
if message["endpoint"] == "actuate/gesture/single":
|
||||
self._handle_gesture(message, True)
|
||||
|
||||
def _handle_messages(self):
|
||||
while not state.exit_event.is_set():
|
||||
@@ -101,5 +160,14 @@ class ActuationReceiver(ReceiverBase):
|
||||
state.is_speaking = False
|
||||
except RuntimeError:
|
||||
logging.warn("Lost connection to Pepper. Please check if you're connected to the local WiFi and restart this application.")
|
||||
state.exit_event.set()
|
||||
state.exit_event.set()
|
||||
|
||||
def endpoint_description(self):
|
||||
"""
|
||||
Extend the default endpoint description with gesture tags.
|
||||
Returned during negotiate/ports so the CB knows available gestures.
|
||||
"""
|
||||
desc = super(ActuationReceiver, self).endpoint_description()
|
||||
desc["gestures"] = GestureTags.tags
|
||||
desc["single_gestures"] = GestureTags.single_gestures
|
||||
return desc
|
||||
|
||||
Reference in New Issue
Block a user