Skip to content

Commit 783acce

Browse files
committed
Use ROS messages instead of strings
1 parent 46debb4 commit 783acce

File tree

2 files changed

+76
-24
lines changed

2 files changed

+76
-24
lines changed

coffee_ws/src/coffee_voice_agent/coffee_voice_agent/voice_agent_bridge.py

Lines changed: 74 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
import threading
1313
import time
1414
import uuid
15+
import datetime
1516
from typing import Optional
1617

1718
import rclpy
@@ -20,6 +21,7 @@
2021
from rclpy.callback_groups import ReentrantCallbackGroup
2122
from std_msgs.msg import String, Bool
2223
from geometry_msgs.msg import Twist
24+
from coffee_voice_agent_msgs.msg import TtsEvent, AgentState, EmotionState, ConversationItem
2325

2426
try:
2527
import websockets
@@ -55,26 +57,33 @@ def __init__(self):
5557

5658
# ROS2 Publishers (Voice Agent → ROS2)
5759
self.state_pub = self.create_publisher(
58-
String,
60+
AgentState,
5961
'voice_agent/state',
6062
10,
6163
callback_group=self.callback_group
6264
)
6365

6466
self.conversation_pub = self.create_publisher(
65-
String,
67+
ConversationItem,
6668
'voice_agent/conversation',
6769
10,
6870
callback_group=self.callback_group
6971
)
7072

7173
self.emotion_pub = self.create_publisher(
72-
String,
74+
EmotionState,
7375
'voice_agent/emotion',
7476
10,
7577
callback_group=self.callback_group
7678
)
7779

80+
self.tts_events_pub = self.create_publisher(
81+
TtsEvent,
82+
'voice_agent/tts_events',
83+
10,
84+
callback_group=self.callback_group
85+
)
86+
7887
self.connected_pub = self.create_publisher(
7988
Bool,
8089
'voice_agent/connected',
@@ -158,32 +167,58 @@ async def _handle_websocket_message(self, message: str):
158167

159168
if message_type == 'STATE_CHANGE':
160169
# Publish agent state change
161-
state_msg = String()
162-
state_msg.data = json.dumps({
163-
'state': data.get('state'),
164-
'timestamp': data.get('timestamp'),
165-
'previous_state': data.get('previous_state')
166-
})
170+
state_msg = AgentState()
171+
state_msg.current_state = data.get('state', 'unknown')
172+
state_msg.previous_state = data.get('previous_state', 'unknown')
173+
# Parse timestamp if provided, otherwise use current time
174+
timestamp_str = data.get('timestamp')
175+
if timestamp_str:
176+
# Convert ISO timestamp to ROS Time if needed
177+
try:
178+
dt = datetime.datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
179+
state_msg.timestamp.sec = int(dt.timestamp())
180+
state_msg.timestamp.nanosec = int((dt.timestamp() % 1) * 1e9)
181+
except:
182+
# Fallback to current time
183+
state_msg.timestamp = self.get_clock().now().to_msg()
184+
else:
185+
state_msg.timestamp = self.get_clock().now().to_msg()
167186
self.state_pub.publish(state_msg)
168187

169188
elif message_type == 'CONVERSATION':
170189
# Publish conversation transcript
171-
conv_msg = String()
172-
conv_msg.data = json.dumps({
173-
'role': data.get('role'),
174-
'text': data.get('text'),
175-
'timestamp': data.get('timestamp')
176-
})
190+
conv_msg = ConversationItem()
191+
conv_msg.role = data.get('role', 'unknown')
192+
conv_msg.text = data.get('text', '')
193+
# Parse timestamp if provided, otherwise use current time
194+
timestamp_str = data.get('timestamp')
195+
if timestamp_str:
196+
try:
197+
dt = datetime.datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
198+
conv_msg.timestamp.sec = int(dt.timestamp())
199+
conv_msg.timestamp.nanosec = int((dt.timestamp() % 1) * 1e9)
200+
except:
201+
conv_msg.timestamp = self.get_clock().now().to_msg()
202+
else:
203+
conv_msg.timestamp = self.get_clock().now().to_msg()
177204
self.conversation_pub.publish(conv_msg)
178205

179206
elif message_type == 'EMOTION':
180207
# Publish emotion change
181-
emotion_msg = String()
182-
emotion_msg.data = json.dumps({
183-
'emotion': data.get('emotion'),
184-
'previous_emotion': data.get('previous_emotion'),
185-
'timestamp': data.get('timestamp')
186-
})
208+
emotion_msg = EmotionState()
209+
emotion_msg.emotion = data.get('emotion', 'unknown')
210+
emotion_msg.previous_emotion = data.get('previous_emotion', 'unknown')
211+
# Parse timestamp if provided, otherwise use current time
212+
timestamp_str = data.get('timestamp')
213+
if timestamp_str:
214+
try:
215+
dt = datetime.datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
216+
emotion_msg.timestamp.sec = int(dt.timestamp())
217+
emotion_msg.timestamp.nanosec = int((dt.timestamp() % 1) * 1e9)
218+
except:
219+
emotion_msg.timestamp = self.get_clock().now().to_msg()
220+
else:
221+
emotion_msg.timestamp = self.get_clock().now().to_msg()
187222
self.emotion_pub.publish(emotion_msg)
188223

189224
elif message_type == 'STATUS':
@@ -205,9 +240,24 @@ async def _handle_websocket_message(self, message: str):
205240

206241
self.get_logger().info(f"TTS {event}: emotion={emotion}, source={source}, text='{text_preview}'")
207242

208-
# TODO: Publish to ROS2 topics when robot coordination is ready
209-
# self.tts_started_pub.publish(...) for event == "started"
210-
# self.tts_finished_pub.publish(...) for event == "finished"
243+
# Publish TTS event to ROS2 topic
244+
tts_msg = TtsEvent()
245+
tts_msg.event = event
246+
tts_msg.emotion = emotion
247+
tts_msg.text = text_preview # Use truncated text for efficiency
248+
tts_msg.source = source
249+
# Parse timestamp if provided, otherwise use current time
250+
timestamp_str = event_data.get('timestamp')
251+
if timestamp_str:
252+
try:
253+
dt = datetime.datetime.fromisoformat(timestamp_str.replace('Z', '+00:00'))
254+
tts_msg.timestamp.sec = int(dt.timestamp())
255+
tts_msg.timestamp.nanosec = int((dt.timestamp() % 1) * 1e9)
256+
except:
257+
tts_msg.timestamp = self.get_clock().now().to_msg()
258+
else:
259+
tts_msg.timestamp = self.get_clock().now().to_msg()
260+
self.tts_events_pub.publish(tts_msg)
211261

212262
elif message_type == 'ACKNOWLEDGMENT':
213263
# Handle acknowledgment messages from voice agent

coffee_ws/src/coffee_voice_agent/package.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
<depend>rclpy</depend>
1111
<depend>std_msgs</depend>
12+
<depend>geometry_msgs</depend>
1213
<depend>coffee_machine_control_msgs</depend>
1314
<depend>coffee_expressions_msgs</depend>
15+
<depend>coffee_voice_agent_msgs</depend>
1416

1517
<test_depend>ament_copyright</test_depend>
1618
<test_depend>ament_flake8</test_depend>

0 commit comments

Comments
 (0)