File size: 7,331 Bytes
27e74f3 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 |
import asyncio
import json
import logging
import websockets
from src.constants.constants import AudioConfig
from src.protocols.protocol import Protocol
from src.utils.config_manager import ConfigManager
from src.utils.logging_config import get_logger
logger = get_logger(__name__)
class WebsocketProtocol(Protocol):
def __init__(self):
super().__init__()
# 获取配置管理器实例
self.config = ConfigManager.get_instance()
self.websocket = None
self.connected = False
self.hello_received = None # 初始化时先设为 None
self.WEBSOCKET_URL = self.config.get_config("SYSTEM_OPTIONS.NETWORK.WEBSOCKET_URL")
self.HEADERS = {
"Authorization": f"Bearer {self.config.get_config('SYSTEM_OPTIONS.NETWORK.WEBSOCKET_ACCESS_TOKEN')}",
"Protocol-Version": "1",
"Device-Id": self.config.get_config("SYSTEM_OPTIONS.DEVICE_ID"), # 获取设备MAC地址
"Client-Id": self.config.get_config("SYSTEM_OPTIONS.CLIENT_ID")
}
async def connect(self) -> bool:
"""连接到WebSocket服务器"""
try:
# 在连接时创建 Event,确保在正确的事件循环中
self.hello_received = asyncio.Event()
# 建立WebSocket连接 (兼容不同Python版本的写法)
try:
# 新的写法 (在Python 3.11+版本中)
self.websocket = await websockets.connect(
uri=self.WEBSOCKET_URL,
additional_headers=self.HEADERS
)
except TypeError:
# 旧的写法 (在较早的Python版本中)
self.websocket = await websockets.connect(
self.WEBSOCKET_URL,
extra_headers=self.HEADERS
)
# 启动消息处理循环
asyncio.create_task(self._message_handler())
# 发送客户端hello消息
hello_message = {
"type": "hello",
"version": 1,
"transport": "websocket",
"audio_params": {
"format": "opus",
"sample_rate": AudioConfig.INPUT_SAMPLE_RATE,
"channels": AudioConfig.CHANNELS,
"frame_duration": AudioConfig.FRAME_DURATION,
}
}
await self.send_text(json.dumps(hello_message))
# 等待服务器hello响应
try:
await asyncio.wait_for(
self.hello_received.wait(),
timeout=10.0
)
self.connected = True
logger.info("已连接到WebSocket服务器")
return True
except asyncio.TimeoutError:
logger.error("等待服务器hello响应超时")
if self.on_network_error:
self.on_network_error("等待响应超时")
return False
except Exception as e:
logger.error(f"WebSocket连接失败: {e}")
if self.on_network_error:
self.on_network_error(f"无法连接服务: {str(e)}")
return False
async def _message_handler(self):
"""处理接收到的WebSocket消息"""
try:
async for message in self.websocket:
if isinstance(message, str):
try:
data = json.loads(message)
msg_type = data.get("type")
if msg_type == "hello":
# 处理服务器 hello 消息
await self._handle_server_hello(data)
else:
if self.on_incoming_json:
self.on_incoming_json(data)
except json.JSONDecodeError as e:
logger.error(f"无效的JSON消息: {message}, 错误: {e}")
elif self.on_incoming_audio: # 使用 elif 更清晰
self.on_incoming_audio(message)
except websockets.ConnectionClosed:
logger.info("WebSocket连接已关闭")
self.connected = False
if self.on_audio_channel_closed:
# 使用 schedule 确保回调在主线程中执行
await self.on_audio_channel_closed()
except Exception as e:
logger.error(f"消息处理错误: {e}")
self.connected = False
if self.on_network_error:
# 使用 schedule 确保错误处理在主线程中执行
self.on_network_error(f"连接错误: {str(e)}")
async def send_audio(self, data: bytes):
"""发送音频数据"""
if not self.is_audio_channel_opened(): # 使用已有的 is_connected 方法
return
try:
await self.websocket.send(data)
except Exception as e:
if self.on_network_error:
self.on_network_error(f"发送音频数据失败: {str(e)}")
async def send_text(self, message: str):
"""发送文本消息"""
if self.websocket:
try:
await self.websocket.send(message)
except Exception as e:
await self.close_audio_channel()
if self.on_network_error:
self.on_network_error(f"发送消息失败: {str(e)}")
def is_audio_channel_opened(self) -> bool:
"""检查音频通道是否打开"""
return self.websocket is not None and self.connected
async def open_audio_channel(self) -> bool:
"""建立 WebSocket 连接
如果尚未连接,则创建新的 WebSocket 连接
Returns:
bool: 连接是否成功
"""
if not self.connected:
return await self.connect()
return True
async def _handle_server_hello(self, data: dict):
"""处理服务器的 hello 消息"""
try:
# 验证传输方式
transport = data.get("transport")
if not transport or transport != "websocket":
logger.error(f"不支持的传输方式: {transport}")
return
print("服务链接返回初始化配置", data)
# 设置 hello 接收事件
self.hello_received.set()
# 通知音频通道已打开
if self.on_audio_channel_opened:
await self.on_audio_channel_opened()
logger.info("成功处理服务器 hello 消息")
except Exception as e:
logger.error(f"处理服务器 hello 消息时出错: {e}")
if self.on_network_error:
self.on_network_error(f"处理服务器响应失败: {str(e)}")
async def close_audio_channel(self):
"""关闭音频通道"""
if self.websocket:
try:
await self.websocket.close()
self.websocket = None
self.connected = False
if self.on_audio_channel_closed:
await self.on_audio_channel_closed()
except Exception as e:
logger.error(f"关闭WebSocket连接失败: {e}") |