247 lines
7.1 KiB
Python
247 lines
7.1 KiB
Python
import socket
|
||
import json
|
||
import time
|
||
import os
|
||
import network
|
||
import struct
|
||
import gc
|
||
from audio_module import AudioPlayer
|
||
|
||
# Try importing wave, handle different environments
|
||
try:
|
||
import media.wave as wave
|
||
except ImportError:
|
||
try:
|
||
import wave
|
||
except ImportError:
|
||
wave = None
|
||
|
||
|
||
class AgentClientHTTP:
|
||
"""
|
||
K230 Agent Client (HTTP)
|
||
"""
|
||
|
||
def __init__(self, server_host, server_port=8000):
|
||
self._server_host = server_host
|
||
self._server_port = server_port
|
||
self._sta = None
|
||
self._is_connected = False
|
||
|
||
def connect_wifi(self, ssid, password=None, timeout=15):
|
||
"""
|
||
Connect to WiFi
|
||
"""
|
||
self._sta = network.WLAN(network.STA_IF)
|
||
self._sta.active(True)
|
||
|
||
if password:
|
||
self._sta.connect(ssid, password)
|
||
else:
|
||
self._sta.connect(ssid)
|
||
|
||
print("正在连接WiFi: " + ssid + "...")
|
||
|
||
start_time = time.time()
|
||
while not self._sta.isconnected():
|
||
if time.time() - start_time > timeout:
|
||
raise RuntimeError("WiFi连接超时: " + ssid)
|
||
time.sleep(1)
|
||
try:
|
||
os.exitpoint()
|
||
except BaseException:
|
||
pass
|
||
|
||
ip = self._sta.ifconfig()[0]
|
||
print("WiFi连接成功! IP: " + ip)
|
||
self._is_connected = True
|
||
return ip
|
||
|
||
def disconnect(self):
|
||
"""断开网络连接"""
|
||
if self._sta:
|
||
if self._sta.isconnected():
|
||
self._sta.disconnect()
|
||
self._sta.active(False)
|
||
print("WiFi已断开,网卡已关闭。")
|
||
self._is_connected = False
|
||
|
||
def trigger_abnormal_state(self, reason, context_data=None):
|
||
"""
|
||
Trigger abnormal state and save response as WAV.
|
||
"""
|
||
url_path = "/abnormal_trigger"
|
||
|
||
payload = {
|
||
"type": "abnormal_trigger",
|
||
"trigger_reason": reason,
|
||
"enable_streaming": True,
|
||
"context_data": context_data or {}
|
||
}
|
||
|
||
body_json = json.dumps(payload)
|
||
print(f"[AgentHTTP] 请求服务器: {self._server_host}:{self._server_port}...")
|
||
|
||
try:
|
||
# Send Request
|
||
response_data = self._send_http_request(url_path, body_json)
|
||
|
||
# Parse Header/Body
|
||
header_end = response_data.find(b"\r\n\r\n")
|
||
if header_end == -1:
|
||
print("[AgentHTTP] 响应格式错误")
|
||
return None
|
||
|
||
body_bytes = response_data[header_end + 4:]
|
||
|
||
if len(body_bytes) == 0:
|
||
print("[AgentHTTP] 响应正文为空")
|
||
return None
|
||
|
||
print(f"[AgentHTTP] 成功接收音频数据: {len(body_bytes)} 字节")
|
||
|
||
# Save as WAV file
|
||
# TTS server returns 24000Hz, 1 channel, 16bit (2 bytes) PCM
|
||
wav_file = "/sdcard/agent_response.wav"
|
||
self._save_wav(wav_file, body_bytes, rate=24000, channels=1, sampwidth=2)
|
||
|
||
return wav_file
|
||
|
||
except Exception as e:
|
||
print(f"[AgentHTTP] 请求失败: {e}")
|
||
return None
|
||
|
||
def _save_wav(self, filename, pcm_data, rate=24000, channels=1, sampwidth=2):
|
||
"""
|
||
Save PCM data with WAV header
|
||
"""
|
||
print(f"[AgentHTTP] 保存 WAV 文件: {filename} (Rate: {rate}, Ch: {channels})")
|
||
try:
|
||
if wave:
|
||
# Use wave module if available
|
||
wf = wave.open(filename, 'wb')
|
||
wf.set_channels(channels)
|
||
wf.set_sampwidth(sampwidth)
|
||
wf.set_framerate(rate)
|
||
wf.write_frames(pcm_data)
|
||
wf.close()
|
||
else:
|
||
# Manual WAV header creation
|
||
total_len = len(pcm_data) + 36
|
||
header = struct.pack(
|
||
'<4sI4s4sIHHIIHH4sI',
|
||
b'RIFF',
|
||
total_len,
|
||
b'WAVE',
|
||
b'fmt ',
|
||
16,
|
||
1,
|
||
channels,
|
||
rate,
|
||
rate * channels * sampwidth,
|
||
channels * sampwidth,
|
||
sampwidth * 8,
|
||
b'data',
|
||
len(pcm_data))
|
||
with open(filename, 'wb') as f:
|
||
f.write(header)
|
||
f.write(pcm_data)
|
||
|
||
except Exception as e:
|
||
print(f"[AgentHTTP] 保存 WAV 失败: {e}")
|
||
|
||
def play_audio_file(self, file_path):
|
||
"""
|
||
Play audio file using AudioPlayer.play_file (handles WAV)
|
||
"""
|
||
if not file_path:
|
||
return
|
||
|
||
print(f"[AgentHTTP] 正在播放: {file_path}...")
|
||
|
||
player = None
|
||
try:
|
||
player = AudioPlayer()
|
||
# play_file will init and start stream, write frames, and then deinit in
|
||
# finally block
|
||
player.play_file(file_path)
|
||
|
||
print("[AgentHTTP] 播放完成。")
|
||
|
||
except Exception as e:
|
||
print(f"[AgentHTTP] 播放出错: {e}")
|
||
finally:
|
||
# Double check deinit to prevent noise/resource leak
|
||
if player:
|
||
try:
|
||
if player.is_running: # Check if flag exists
|
||
player.deinit()
|
||
except BaseException:
|
||
pass
|
||
|
||
def _send_http_request(self, path, body_json):
|
||
"""
|
||
Send HTTP POST
|
||
"""
|
||
addr_info = socket.getaddrinfo(self._server_host, self._server_port)
|
||
addr = addr_info[0][-1]
|
||
|
||
s = socket.socket()
|
||
s.settimeout(10)
|
||
|
||
try:
|
||
s.connect(addr)
|
||
body_bytes = body_json.encode('utf-8')
|
||
|
||
req = f"POST {path} HTTP/1.1\r\n"
|
||
req += f"Host: {self._server_host}:{self._server_port}\r\n"
|
||
req += "Content-Type: application/json\r\n"
|
||
req += f"Content-Length: {len(body_bytes)}\r\n"
|
||
req += "Connection: close\r\n\r\n"
|
||
|
||
s.send(req.encode('utf-8'))
|
||
s.send(body_bytes)
|
||
|
||
response = bytearray()
|
||
while True:
|
||
chunk = s.recv(4096)
|
||
if not chunk:
|
||
break
|
||
response.extend(chunk)
|
||
|
||
return response
|
||
|
||
finally:
|
||
s.close()
|
||
|
||
|
||
if __name__ == "__main__":
|
||
# ========== CONFIG ==========
|
||
WIFI_SSID = "dongshengwu"
|
||
WIFI_PASSWORD = "wds666666"
|
||
SERVER_HOST = "172.20.10.2"
|
||
SERVER_PORT = 8000
|
||
# ============================
|
||
|
||
client = AgentClientHTTP(server_host=SERVER_HOST, server_port=SERVER_PORT)
|
||
|
||
try:
|
||
# 1. Connect
|
||
client.connect_wifi(WIFI_SSID, WIFI_PASSWORD)
|
||
|
||
# 2. Trigger & Save
|
||
print("\n[测试] 触发皮肤状态异常提示...")
|
||
wav_file = client.trigger_abnormal_state("poor_skin")
|
||
|
||
# 3. Play
|
||
if wav_file:
|
||
client.play_audio_file(wav_file)
|
||
|
||
except Exception as e:
|
||
print("程序异常: " + str(e))
|
||
finally:
|
||
# 4. Clean up resources
|
||
print("\n[资源清理] 断开 WiFi 并回收资源...")
|
||
gc.collect()
|
||
print("测试结束,已安全退出。")
|