import argparse from tools.yaml_operator import read_yaml import sys import os import threading import signal import time import re import websockets import asyncio import requests import atexit from Speech_processor import Speechprocessor from Speech_processor import SpeechRecognizer from Speech_processor import SpeechAudio from Hotword_awaker import Awaker from LLM import DashscopeClient # from LLM import DifyClient from tools.log import CustomLogger from pathlib import Path import random import subprocess import datetime import shutil import json from ask_summarize import extract_data_from_log import socket import os import copy current_file_path = os.path.abspath(__file__) Language_Path = os.path.dirname(os.path.dirname(os.path.dirname(current_file_path))) MassageRobot_Dobot_Path = os.path.dirname(Language_Path) print("MassageRobot_Dobot_Path:",MassageRobot_Dobot_Path) sys.path.append(MassageRobot_Dobot_Path) from VortXDB.client import VTXClient # 自定义输出类,将输出同时发送到终端和日志文件 class MultiWriter: def __init__(self, *writers): self.writers = writers def write(self, message): for writer in self.writers: writer.write(message) writer.flush() # 确保及时输出 def flush(self): for writer in self.writers: writer.flush() # 重定向标准输出和标准错误到文件和终端 def redirect_output(log_file): # 保存当前的标准输出和标准错误(终端输出) original_stdout_fd = sys.stdout.fileno() original_stderr_fd = sys.stderr.fileno() # 打开日志文件用于写入 log_fd = os.open(log_file, os.O_WRONLY | os.O_CREAT | os.O_TRUNC) # 保留原始的标准输出和错误输出,创建文件对象 original_stdout = os.fdopen(os.dup(original_stdout_fd), 'w') original_stderr = os.fdopen(os.dup(original_stderr_fd), 'w') # 将 stdout 和 stderr 分别指向日志文件 os.dup2(log_fd, original_stdout_fd) os.dup2(log_fd, original_stderr_fd) # 将输出同时发送到终端和日志文件 sys.stdout = MultiWriter(original_stdout, os.fdopen(log_fd, 'w')) sys.stderr = MultiWriter(original_stderr, os.fdopen(log_fd, 'w')) # 获取当前时间并格式化为适合文件名的字符串(例如:'2024-11-13_154530') timestamp = datetime.datetime.now().strftime("%Y-%m-%d_%H%M%S") # 目标目录和目标文件路径(将日志文件复制到新的位置) target_dir = '/home/jsfb/jsfb_ws/LanguageLog/' os.makedirs(target_dir, exist_ok=True) # 确保目标目录存在 ask_summarize_dir = '/home/jsfb/jsfb_ws/LanguageLog/ask_summarize' os.makedirs(target_dir, exist_ok=True) # 确保目标目录存在 os.makedirs(ask_summarize_dir, exist_ok=True) # 确保目标目录存在 source_log_file = '../log/Language.log' # 如果源文件不存在,则创建一个空文件 if not os.path.exists(source_log_file): with open(source_log_file, 'w'): pass # 创建空文件 # 定义复制的目标文件路径(带时间戳) target_file = os.path.join(target_dir, f'Language_{timestamp}.log') # 复制文件到新位置 shutil.copy('../log/Language.log', target_file) try: log_file_path = target_file # 替换为实际的日志文件路径 output_file_path = os.path.join(ask_summarize_dir, f'Language_{timestamp}.txt') extract_data_from_log(log_file_path, output_file_path) except Exception as e: print("保存上次聊天记录出错了") log_file = '../log/Language.log' redirect_output(log_file) # 获取资源文件的正确路径 def get_resource_path(relative_path): """获取资源文件的路径,打包后的应用会把资源放到_MEIPASS目录""" try: # PyInstaller会把资源放到_MEIPASS路径下 base_path = sys._MEIPASS except Exception: # 没有打包时,使用脚本的当前目录 base_path = os.path.abspath(".") return os.path.join(base_path, relative_path) def parse_args(): parser = argparse.ArgumentParser(description='Speech processor') parser.add_argument('--keyword_path', type=str, default=get_resource_path('keyword.yaml')) args = parser.parse_args() return args class RoboStorm(): def __init__(self,args): vtxdb = VTXClient() self.logger = CustomLogger() self.Recognize_initialization_successful = True self.Synthesizer_initialization_successful = True self.LLM_initialization_successful = True self.Awaken_initialization_successful = True self.ui_speech_interrupted=False self.ui_speech_end_flag = False self.ui_speech_path=None self.ui_speech_cancel = False self.ksam_flag=False self.player_flag=False self.wait_1_second_flag=False self.massage_status=None self.speech_audio=SpeechAudio() max = 10 # 设置最大重试次数 retry_time = 2 # 设置每次重试之间的延迟时间(秒) keyword_config: dict = read_yaml(args.keyword_path) retries1 = 0 max_retries1 = 10 # 最大重试次数 retry_delay1 = 5 # 每次重试之间的延迟时间(秒) while retries1 < max_retries1: try: self.recognizer = SpeechRecognizer() self.recognizer.test_token() if self.recognizer.token_success==True: break else: self.logger.log_error(f"参数服务器token_HW有误,尝试自动获取{retries1}次") self.recognizer.get_Speech_Recognize_token() self.recognizer.token=self.recognizer.get_Speech_Recognize_token() vtxdb.set("robot_config", "Language.Speech_processor.huaweiyun_recognize_config.token_HW",self.recognizer.token) self.logger.log_info("设置参数服务器成功") self.Recognize_initialization_successful=True break except Exception as e: # self.logger.logger(f"语音识别初始化问题{e}") retries1 += 1 self.logger.log_error(f"语音初始化失败: {e}, 尝试重试 {retries1}/{max_retries1}") if retries1 < max_retries1: instruction = {'message': f"语音初始化失败,正在重试第 {retries1} 次..."} self.send_instruction(instruction, 'on_message') time.sleep(retry_delay1) # 等待 retry_delay 秒后再重试 else: instruction = {'message': "无法聊天,请检查网络连接!"} self.send_instruction(instruction, 'on_message') # sys.exit(1) # 视情况决定是否退出程序 self.Recognize_initialization_successful = False self.logger.log_error("达到最大重试次数,初始化失败,退出循环") break # 达到最大重试次数后,退出循环 try: self.hotword_detector = Awaker() self.Awaken_initialization_successful=True except Exception as e: self.logger.log_error(f"{e},Please cheack path or app_id") instruction = {'message':"唤醒失败!!!"} self.send_instruction(instruction,'on_message') # sys.exit(1) self.Awaken_initialization_successful = False return retries = 0 max_retries = 10 # 最大重试次数 retry_delay = 5 # 每次重试之间的延迟时间(秒) while retries < max_retries: try: self.speech_processor = Speechprocessor() self.Synthesizer_initialization_successful = True self.logger.log_info("语音初始化成功") break # 初始化成功后,跳出循环 except Exception as e: retries += 1 self.logger.log_error(f"语音初始化失败: {e}, 尝试重试 {retries}/{max_retries}") if retries < max_retries: instruction = {'message': f"语音初始化失败,正在重试第 {retries} 次..."} self.send_instruction(instruction, 'on_message') time.sleep(retry_delay) # 等待 retry_delay 秒后再重试 else: instruction = {'message': "无法聊天,请检查网络连接!"} self.send_instruction(instruction, 'on_message') # sys.exit(1) # 视情况决定是否退出程序 self.Synthesizer_initialization_successful = False self.logger.log_error("达到最大重试次数,初始化失败,退出循环") break # 达到最大重试次数后,退出循环 self.speech_thread_stop_event = threading.Event() self.correct_stop_event = threading.Event() try: self.llm = DashscopeClient() self.LLM_initialization_successful=True # self.llm_dify = DifyClient() except Exception as e: self.logger.log_error(f"大模型初始化{e}") instruction = {'message':"请检查网络连接!!!"} self.send_instruction(instruction,'on_message') # sys.exit(1) self.LLM_initialization_successful = False return self.keyword = keyword_config self.hotword_interrupted = False # 检测是否退出主程序 self.running_task = None self.running_zone = None self.audio_map = { # 'jttq': 'pre_mp3/weather.mp3', # 'jtxw': 'pre_mp3/news.mp3', 'ksam': 'pre_mp3/not_begin', 'tzam': 'pre_mp3/amstop', 'ldzd': 'pre_mp3/move_deeper/', # 指向文件夹路径 'ldjx': 'pre_mp3/lift_up/', # 指向文件夹路径 'wdzj': 'pre_mp3/tem_up/', # 指向文件夹路径 'wdjx': 'pre_mp3/tem_down/', # 指向文件夹路径 'dlzd': 'pre_mp3/gear_up/', # 指向文件夹路径 'dljx': 'pre_mp3/gear_down/', # 指向文件夹路径 'pljk': 'pre_mp3/shockwave_frequency_up/', # 指向文件夹路径 'pljd': 'pre_mp3/shockwave_frequency_down/', # 指向文件夹路径 'cjlz': 'pre_mp3/shockwave_press_up/', # 指向文件夹路径 'cjlj': 'pre_mp3/shockwave_press_down/', # 指向文件夹路径 'tsgd':'pre_mp3/increase_high/', 'jdgd':'pre_mp3/decrease_high/', 'zszj': 'pre_mp3/stone_speed_up/', 'zsjx': 'pre_mp3/stone_speed_down/', 'gbzx': 'pre_mp3/stone_direction_change/', 'bfyy': '', 'tzbf': '', 'ssyy': '', 'xsyy': '', 'yltj': '' } self.speech_thread = threading.Thread(target=self.hello()) self.speech_thread.start() self.start_awaken() self.stop_event = asyncio.Event() # 用于停止事件 self.loop = None self.server_thread = None self.client_thread = None self.correct_thread = None def start_awaken(self): base_path = os.path.dirname(os.path.abspath(__file__)) script_path = os.path.join(base_path, "Hotword_awaker", "run.sh") script_dir = os.path.join(base_path, "Hotword_awaker") log_path = os.path.join(base_path, "Hotword_awaker", "awaken.log") # 指定日志文件路径 print("script_dir:", script_dir) if not os.path.exists(script_path): raise FileNotFoundError(f"找不到脚本文件:{script_path}") # 打开日志文件,用于写入脚本输出 with open(log_path, "w") as log_file: # 使用 subprocess 后台运行脚本并将输出重定向到日志文件 self.process = subprocess.Popen( ["/bin/bash", script_path], cwd=script_dir, stdout=log_file, # 重定向标准输出到日志文件 stderr=log_file # 重定向标准错误到日志文件 ) print(f"Script started, output redirected to {log_path}") def get_random_mp3_file(self,folder_path): """ 从指定文件夹中随机选择一个 .mp3 文件并返回其完整路径。 :param folder_path: 文件夹路径 :return: 选定的 .mp3 文件的完整路径,如果文件夹不存在或没有 .mp3 文件则返回 None """ if os.path.isdir(folder_path) and any(f.endswith('.mp3') for f in os.listdir(folder_path)): mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')] mp3_file = random.choice(mp3_files) full_path = os.path.join(folder_path, mp3_file) return full_path else: return None def hello(self): self.speech_audio.player.play(get_resource_path('pre_mp3/' + 'hello.mp3'),remove_file=False) self.speech_audio.player.play(get_resource_path('pre_mp3/' + 'successfully_start.mp3'),remove_file=False) def detected_callback(self): '''检测到热词就打断语音识别过程并重新运行''' # self.logger.log_info("检测到小悠小悠、小悠师傅") self.send_instruction("get_status",'massage') if self.speech_thread.is_alive(): self.logger.log_error(f"self.ui_speech_interrupted状态:{self.ui_speech_interrupted}") self.logger.log_error(f"self.ui_speech_end_flag状态:{self.ui_speech_end_flag}") self.logger.log_error(f"self.ui_speech_cancel状态:{self.ui_speech_cancel}") print("正在播放中打断") self.speech_thread_stop_event.set() if self.ui_speech_interrupted == False: self.logger.log_info("检测到小悠小悠、小悠师傅") folder_path = get_resource_path(f'pre_mp3/我在/') if os.path.isdir(folder_path) and any(f.endswith('.mp3') for f in os.listdir(folder_path)): mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')] mp3_files = random.choice(mp3_files) path=folder_path+mp3_files self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path,remove_file=False,priority=10) print('folder_path:',folder_path) else: self.logger.log_info("UI打断") folder_path = get_resource_path(f'pre_mp3/empty_audio/') if os.path.isdir(folder_path) and any(f.endswith('.mp3') for f in os.listdir(folder_path)): mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')] mp3_files = random.choice(mp3_files) path=folder_path+mp3_files self.speech_audio.player.play(path,remove_file=False,priority=10) print('folder_path:',folder_path) self.speech_thread.join() print("78787878") self.speech_thread_stop_event.clear() self.speech_thread = threading.Thread(target=self.run_speech) self.speech_thread.start() else: self.logger.log_error(f"self.ui_speech_interrupted状态:{self.ui_speech_interrupted}") self.logger.log_error(f"self.ui_speech_end_flag状态:{self.ui_speech_end_flag}") self.logger.log_error(f"self.ui_speech_cancel状态:{self.ui_speech_cancel}") if self.correct_thread is not None and self.correct_thread.is_alive(): self.speech_thread_stop_event.set() print("correct_thread还活着") else: print("111111111111111") print("没有在播放中打断") if self.ui_speech_interrupted == False: folder_path = get_resource_path(f'pre_mp3/我在/') if os.path.isdir(folder_path) and any(f.endswith('.mp3') for f in os.listdir(folder_path)): mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')] mp3_files = random.choice(mp3_files) path=folder_path+mp3_files self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path,remove_file=False) else: folder_path = get_resource_path(f'pre_mp3/empty_audio/') if os.path.isdir(folder_path) and any(f.endswith('.mp3') for f in os.listdir(folder_path)): mp3_files = [f for f in os.listdir(folder_path) if f.endswith('.mp3')] mp3_files = random.choice(mp3_files) path=folder_path+mp3_files self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path,remove_file=False,priority=10) print('folder_path:',folder_path) self.speech_thread_stop_event.clear() self.speech_thread = threading.Thread(target=self.run_speech) self.speech_thread.start() def signal_handler(self, signal, frame): '''捕获Ctrl+C信号,停止热词检测''' self.stop_event.set() if self.loop is not None: self.loop.call_soon_threadsafe(self.loop.stop) # 停止事件循环 self.hotword_detector.interrupted = True sys.exit(0) def cleanup(self): '''程序退出时的清理工作''' print("程序退出,正在清理资源...") if self.server_thread is not None: self.stop_event.set() self.server_thread.join() if self.client_thread is not None: self.client_thread.join() print("资源清理完毕") def run_hotword_detect(self): '''启动热词检测线程''' self.logger.log_info('Listening... please say wake-up word.') self.hotword_detector.start_server(self.detected_callback) def run_speech(self): self.logger.log_blue("进入run_speech") '''进行一次语音处理''' while not self.speech_thread_stop_event.is_set(): self.logger.log_blue("run_speech stop没set") self.logger.log_blue(f"run_speech这里ui_speech_interrupted{self.ui_speech_interrupted}") try: if self.ui_speech_interrupted == True: print("111111111111111111") while not self.ui_speech_end_flag: self.logger.log_blue("self.ui_speech_end_flag还在True,退不出去了") time.sleep(0.1) if self.speech_thread_stop_event.is_set(): return if self.ui_speech_cancel: self.ui_speech_interrupted = False self.ui_speech_end_flag = False self.send_message(message="UI_not_recognizing") return print("1222222222222222222") text = self.recognizer.speech_recognize_UI(self.ui_speech_path) instruction = {'message': text} self.send_instruction(instruction, 'user_input') self.ui_speech_interrupted = False self.ui_speech_end_flag = False self.send_message(message="UI_not_recognizing") else: print("33333") if self.speech_thread_stop_event.is_set(): return text, if_timeout, remaining_time = self.recognizer.speech_recognize(timeout=7) if self.speech_thread_stop_event.is_set(): return # 如果识别到的文本为空且还有剩余时间,则继续识别 while not text.strip() and remaining_time > 0: text, if_timeout, remaining_time = self.recognizer.speech_recognize(timeout=remaining_time) if self.speech_thread_stop_event.is_set(): return instruction = {'message': text} self.send_instruction(instruction, 'user_input') if self.speech_thread_stop_event.is_set(): return if if_timeout: path = get_resource_path('pre_mp3/timeout.mp3') self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path, remove_file=False) return if self.speech_thread_stop_event.is_set(): return self.logger.log_info("Recognized text: " + text) self.logger.log_error("--------------------------") text_status = self.run_text(text) if self.speech_thread_stop_event.is_set(): return self.logger.log_blue(f"text_status: {text_status}") if text_status == 2: return if text_status==1: return except Exception as e: instruction = {'message':"失败,请检查网络连接"} self.logger.log_error(f"失败{e}") self.send_instruction(instruction,'on_error') return def run_text(self,text): '''进行一次文本处理,返回0代表正常处理,返回1代表用户打断''' # 获取机械臂现在的状态给LLM进行处理,包括力度、电流这些 try: response = requests.get('http://127.0.0.1:5000/get_deep_thought') self.logger.log_info("获取思考状态 ") print(response.json()) # 输出返回的深度思考状态 self.llm.deep_thinking_flag=response.json().get('deep_thought_active') self.logger.log_info(f"获取到的深度思考状态为:{self.llm.deep_thinking_flag}") except Exception as e: self.logger.log_error("获取不到深度思考状态") try: response = requests.get('http://127.0.0.1:5000/get_ai_search') self.logger.log_info("获取思考状态 ") print(response.json()) # 输出返回的深度思考状态 self.llm.search_flag=response.json().get('ai_search_active') self.logger.log_info(f"获取到的联网搜索状态为:{self.llm.search_flag}") except Exception as e: self.logger.log_error("获取不到联网搜索状态") try: self.send_instruction("get_status",'massage') self.llm.massage_status_languge = {key: self.massage_status[key] for key in ['task_time','progress','force','press','frequency','temperature','gear','body_part','current_head','shake','speed','direction','start_pos','end_pos','massage_path']} except Exception as e: self.logger.log_error(f"机械臂还未开启,无法获取状态:{e}") if not self.speech_thread_stop_event.is_set(): if text.strip(): # 识别到有效文本则正常运行,否则语音提醒 # 语料库匹配 for key, value in self.keyword.items(): if key in text: mp3_folder, instruction = value path = get_resource_path(f'pre_mp3/{mp3_folder}') if key == "开始按摩": if self.ksam_flag==False: try: self.send_instruction("get_status",'massage') if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == False: path = self.get_random_mp3_file(path) # 发送按摩和UI指令 self.send_instruction(instruction, 'massage') self.send_instruction({'path': path}, '_lip_sync') # 播放选定的mp3文件 self.speech_audio.player.play(path, remove_file=False) return 1 else: self.logger.log_error("机械臂状态不对,请确保机械臂已连接且不在按摩状态") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 except Exception as e: self.logger.log_error(f"获取机械臂状态失败:{e}") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 else: if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == False: path = get_resource_path('pre_mp3/begin.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() else: self.logger.log_error("请确保机械臂已连接且不在按摩中") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 else: try: self.send_instruction("get_status",'massage') if self.massage_status['massage_service_started'] == True: path = self.get_random_mp3_file(path) # 发送按摩和UI指令 self.send_instruction(instruction, 'massage') self.send_instruction({'path': path}, 'ui_lip_sync') # 播放选定的mp3文件 self.speech_audio.player.play(path, remove_file=False) return 1 else: self.logger.log_error("机械臂状态不对,请确保机械臂已连接且正在按摩") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 except Exception as e: self.logger.log_error(f"获取机械臂状态失败:{e}") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 # 调用大模型 # 先获取一下机械臂以及按摩头状态 try: self.send_instruction("get_status",'massage') except Exception as e: self.logger.log_info(f"无法获取机械臂状态:{e}") punctuation_marks_regex = r'[。,,;!?]' # return_info = self.llm.chat(text) if self.speech_thread_stop_event.is_set(): return 1 # 立即退出 return_info = None # 存储 `llm.chat(text)` 结果 update_info = None return_gen = None chat_thread_done = threading.Event() # 标志线程是否完成 def chat_task(): """运行 LLM 处理任务""" nonlocal return_info nonlocal return_gen return_gen = self.llm.chat(text) return_info=next(return_gen) print("first return_info:",return_info) chat_thread_done.set() # 标记任务完成 print("完成") # **启动 `self.llm.chat(text)` 线程** chat_thread = threading.Thread(target=chat_task, daemon=True) chat_thread.start() timeout = 60 # 最长等待时间(秒) start_time = time.time() self.logger.log_info("现在时间是:",start_time) while not chat_thread_done.is_set(): # 等待线程完成 if self.speech_thread_stop_event.is_set(): self.logger.log_error("Stop event detected! Exiting immediately.") return 1 # 立即退出 if time.time() - start_time > timeout: self.logger.log_error("LLM chat request timed out. Exiting.") try: path = get_resource_path(f'pre_mp3/check_network.mp3') self.send_instruction({'path': path}, 'ui_lip_sync') threading.Thread(target=self.speech_audio.player.play, args=(path, False)).start() except Exception as e: self.logger.log_error("请检查message和mp3是否对应上") return 2 # 超时返回 time.sleep(0.1) # 避免高频率 CPU 轮询 def handle_all_finish(): nonlocal update_info if return_info is None: print("return_info 为 None,无法进行深拷贝") else: try: return_info_copy = return_info.copy() # 浅拷贝 if 'response_generator' in return_info_copy: del return_info_copy['response_generator'] return_info1 = copy.deepcopy(return_info_copy) except Exception as e: print(f"深拷贝失败: {e}") print("return_info1:",return_info1) try: update_info = next(return_gen) if return_info1['question_function']!=update_info['question_function']: print("前后两次分类结果不一样") self.wait_1_second_flag=True correct_response_generator=update_info['response_generator'] correct_chat_message=update_info['chat_message'] self.command_process(update_info) print("correct_chat_message:",correct_chat_message) full_content = '' last_index = 0 full_reasoning_content = '' self.play_recored_file_thread = None if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == True: self.logger.log_info(f"correct_response_generator{correct_response_generator}") for response in correct_response_generator: if response.choices: reasoning_result = getattr(response.choices[0].delta, "reasoning_content", None) result = getattr(response.choices[0].delta, "content", None) if reasoning_result: full_reasoning_content += reasoning_result instruction = {'message':reasoning_result,'isReasoning':True} self.send_instruction(instruction,'ui_ai_respon') if self.speech_thread_stop_event.is_set(): self.logger.log_error("未在播报打断成功----------------------------------------------------------") self.speech_thread_stop_event.clear() return 1 if result: # print("response:",response) full_content += result punctuation_indices = [m.start() for m in re.finditer(punctuation_marks_regex, full_content)] # 标点符号分隔断句位置 for index in punctuation_indices: if self.speech_thread_stop_event.is_set(): self.logger.log_error("correct_current收到speech_thread_stop_event结束标志,退出") self.speech_thread_stop_event.clear() return 1 if index > last_index: accumulated_text = full_content[last_index:index+1] last_index = index + 1 if accumulated_text.strip(): print("accumulated_text:",accumulated_text) instruction = {'message':accumulated_text.strip()} self.send_instruction(instruction,'ui_ai_respon') filename = get_resource_path(self.speech_processor.synthesizer.speech_synthesize(accumulated_text.strip())) if self.play_recored_file_thread is not None and self.play_recored_file_thread.is_alive(): # 用户打断则等待本句语音播放完成再返回 self.play_recored_file_thread.join() if self.speech_thread_stop_event.is_set(): self.logger.log_error("正在播报打断成功----------------------------------------------------------") self.speech_thread_stop_event.clear() return 1 # time.sleep(4) instruction = {'path':filename} self.send_instruction(instruction,'ui_lip_sync') if self.speech_thread_stop_event.is_set(): self.logger.log_error("未在播报打断成功----------------------------------------------------------") self.speech_thread_stop_event.clear() return 1 # 等待 player_flag 为 False 再进行播放 self.logger.log_info(f"self.player_flag:{self.player_flag}") while self.player_flag: self.logger.log_info("还在player_flag") if self.speech_thread_stop_event.is_set(): self.logger.log_error("由于wait_1_second_flag没有置为True导致player_flag没有变为Flase,所以再次唤醒就退出卡住的这里----------------------------------------------------------") self.speech_thread_stop_event.clear() return 1 time.sleep(0.1) self.play_recored_file_thread = threading.Thread(target=self.speech_audio.player.play, args=(filename,True,10)) self.play_recored_file_thread.start() else: print("机械臂未连接或者不再按摩") self.logger.log_yellow("-----------------------------------------------------") self.wait_1_second_flag=False else: print("分类结果一致") if self.play_recored_file_thread is not None and self.play_recored_file_thread.is_alive(): self.logger.log_blue("222222222222222222222222222222222") self.play_recored_file_thread.join() self.logger.log_blue("33333333333333333333") self.logger.log_blue("11111111111111111111111111111111111") print("return_info1/1:",return_info1) print("update_dict:",update_info) print("获取所有分类结果结束") self.logger.log_blue("11111111111111111111111111111111111") if self.speech_thread_stop_event.is_set(): self.logger.log_blue("handle_all_finishm没清除speech_thread_stop_event") self.speech_thread_stop_event.clear() except StopIteration as s: print(f"生成器已结束了{s}") pass # 生成器已结束 except Exception as e: print("处理最终结果时出错:",e) self.correct_thread=threading.Thread(target=handle_all_finish) self.correct_thread.start() if self.speech_thread_stop_event.is_set(): self.logger.log_blue(f"handle_all_finis后停止speech_thread_stop_event没更新") self.speech_thread_stop_event.clear() return 1 # 终止 self.logger.log_blue(return_info) if isinstance(return_info, str): path = get_resource_path('pre_mp3/error_input.mp3') self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path,remove_file=False) return 2 self.command_process(return_info) question_function=return_info['question_function'] if question_function in self.audio_map: if question_function in ['ldzd', 'ldjx', 'wdzj', 'wdjx','dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx','tsgd','jdgd']: try: if self.massage_status['massage_service_started'] == True: # if question_function=='dayh': # self.logger.log_error("多按一会成功") # path = get_resource_path('pre_mp3/massage_time_longer.mp3') # threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() # return 1 if self.massage_status['massage_head'] == 'thermotherapy_head' and question_function in ['cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是热疗头,无该控制指令") path = get_resource_path('pre_mp3/thermotherapy_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'shockwave_head' and question_function in ['wdzj', 'wdjx','dlzd','dljx','zszj','zsjx','gbzx']: self.logger.log_error("获取到是冲击波,无该控制指令") path = get_resource_path('pre_mp3/shockwave_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'ball_head' and question_function in ['wdzj', 'wdjx','dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是滚珠,无该控制指令") path = get_resource_path('pre_mp3/ball_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'finger_head' and question_function in ['wdzj', 'wdjx','dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是一指禅,无该控制指令") path = get_resource_path('pre_mp3/finger_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'roller_head' and question_function in ['wdzj', 'wdjx','dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是狼牙滚珠按摩头,无该控制指令") path = get_resource_path('pre_mp3/roller_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'stone_head' and question_function in ['dlzd','dljx','pljk','pljd','cjlz','cjlj']: self.logger.log_error("获取到是砭石按摩头,无该控制指令") path = get_resource_path('pre_mp3/stone_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'heat_head' and question_function in ['dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是能量热疗按摩头,无该控制指令") path = get_resource_path('pre_mp3/heat_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'ion_head' and question_function in ['dlzd','dljx','pljk','pljd','cjlz','cjlj','ldzd','ldjx','zszj','zsjx','gbzx']: self.logger.log_error("获取到是ion按摩头,无该控制指令") path = get_resource_path('pre_mp3/ion_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif self.massage_status['massage_head'] == 'spheres_head' and question_function in ['wdzj', 'wdjx','dlzd','dljx','pljk','pljd','cjlz','cjlj','zszj','zsjx','gbzx']: self.logger.log_error("获取到是天球滚捏按摩头,无该控制指令") path = get_resource_path('pre_mp3/spheres_not_adjust.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 else: folder_path = get_resource_path(self.audio_map[question_function]) random_audio_path = self.get_random_mp3_file(folder_path) # random_audio_path = self.play_random_audio_from_folder(folder_path) if random_audio_path: self.player_flag=True self.send_instruction({'path': random_audio_path}, 'ui_lip_sync') self.speech_audio.player.play(random_audio_path, remove_file=False)\ # 测试一下卡死的时候这里self.wait_1_second_flag的状态,万一还没改变他是会卡死的 self.logger.log_error(f"self.wait_1_second_flag:{self.wait_1_second_flag}") if self.wait_1_second_flag==True: time.sleep(1) # while True: # print("测试有没有更新player_flag 111111") self.player_flag=False self.logger.log_blue("已完成标志位变为flase") return 1 else: self.logger.log_error("请确保机械臂已连接且正在按摩中") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 except Exception as e: self.logger.log_error(f"请确保机械臂已连接且正在按摩中{e}1") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif question_function=='yltj': path = get_resource_path('pre_mp3/music_control') path = self.get_random_mp3_file(path) threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 1 elif question_function == 'bfyy': music_chat_message=return_info['chat_message'] retrieve_message = music_chat_message.split("音乐播放调整:")[-1] self.logger.log_blue(f"retrieve_message:{retrieve_message}") if 'None' in retrieve_message: path = get_resource_path('pre_mp3/music_control') path = self.get_random_mp3_file(path) threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() self.send_instruction(target='resume_music') else: response=self.send_instruction(instruction=retrieve_message,target='search') if response.status_code == 200: # 获取返回的数据(JSON 格式) data = response.json() if isinstance(data, list) and not data: self.logger.log_yellow("音乐为空列表,无法播放") path = get_resource_path('pre_mp3/music_vip') path = self.get_random_mp3_file(path) threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() else: path = get_resource_path('pre_mp3/music_control') path = self.get_random_mp3_file(path) threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() music_result = data[0] print("Response received:", data) print("music_result:",music_result) music_result={"music_name":music_result[0], "singer_name":music_result[1], "songmid":music_result[2], "album_img_url":( f"http://y.gtimg.cn/music/photo_new/T002R180x180M000{music_result[3]}.jpg" if music_result[3] else "" )} # music_result=json.dumps(music_result, ensure_ascii=False, indent=4) print("music_result:",music_result) print(type(music_result)) # instruction=music_result self.send_instruction(instruction=music_result,target='song_clicked') else: print(f"Request failed with status code: {response.status_code}") return 1 elif question_function in ['tzbf', 'ssyy', 'xsyy']: path = get_resource_path('pre_mp3/music_control') path = self.get_random_mp3_file(path) threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() if question_function == 'tzbf': self.send_instruction(target='pause_music') elif question_function == 'ssyy': self.send_instruction(target='play_last_song') elif question_function == 'xsyy': self.send_instruction(target='play_next_song') # print("音乐") return 1 elif question_function=='ksam': if self.ksam_flag==False: try: if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == False: path = get_resource_path(self.audio_map[question_function]) path = self.get_random_mp3_file(path) self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path, remove_file=False) return 1 else: self.logger.log_error("请确保机械臂已连接且不在按摩中") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 except Exception as e: self.logger.log_error(f"开始按摩失败:{e}") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 else: if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == False: path = get_resource_path('pre_mp3/begin.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() else: self.logger.log_error("请确保机械臂已连接且不在按摩中") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 elif question_function=='tzam': try: if self.massage_status['massage_service_started'] == True and self.massage_status['is_massaging'] == True: path = get_resource_path(self.audio_map[question_function]) path = self.get_random_mp3_file(path) self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path, remove_file=False) return 1 else: self.logger.log_error("请确保机械臂已连接且正在按摩中") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 except Exception as e: self.logger.log_error(f"停止按摩失败:{e}") path = get_resource_path('pre_mp3/connect_and_status.mp3') threading.Thread(target=self.speech_audio.player.play, args=(path,False)).start() return 2 else: path = get_resource_path(self.audio_map[question_function]) path = self.get_random_mp3_file(path) self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(path, remove_file=False) return 1 else: response_generator = return_info['response_generator'] full_content = '' last_index = 0 full_reasoning_content = '' self.play_recored_file_thread = None for response in response_generator: if self.speech_thread_stop_event.is_set(): return 1 if response.choices: reasoning_result = getattr(response.choices[0].delta, "reasoning_content", None) result = getattr(response.choices[0].delta, "content", None) if reasoning_result: full_reasoning_content += reasoning_result instruction = {'message':reasoning_result,'isReasoning':True} self.send_instruction(instruction,'ui_ai_respon') if self.speech_thread_stop_event.is_set(): self.logger.log_error("未在播报打断成功----------------------------------------------------------") return 1 if result: # print("response:",response) full_content += result punctuation_indices = [m.start() for m in re.finditer(punctuation_marks_regex, full_content)] # 标点符号分隔断句位置 for index in punctuation_indices: if index > last_index: accumulated_text = full_content[last_index:index+1] last_index = index + 1 if accumulated_text.strip(): print("accumulated_text:",accumulated_text) instruction = {'message':accumulated_text.strip()} self.send_instruction(instruction,'ui_ai_respon') filename = get_resource_path(self.speech_processor.synthesizer.speech_synthesize(accumulated_text.strip())) if self.play_recored_file_thread is not None and self.play_recored_file_thread.is_alive(): # 用户打断则等待本句语音播放完成再返回 self.play_recored_file_thread.join() if self.speech_thread_stop_event.is_set(): self.logger.log_error("正在播报打断成功----------------------------------------------------------") return 1 # time.sleep(4) instruction = {'path':filename} self.send_instruction(instruction,'ui_lip_sync') if self.speech_thread_stop_event.is_set(): self.logger.log_error("未在播报打断成功----------------------------------------------------------") return 1 self.play_recored_file_thread = threading.Thread(target=self.speech_audio.player.play, args=(filename,True,8)) self.play_recored_file_thread.start() self.logger.log_yellow("-----------------------------------------------------") if self.play_recored_file_thread is not None and self.play_recored_file_thread.is_alive(): self.play_recored_file_thread.join() self.llm.chat_message.append({'role': 'assistant', 'content': full_content}) return 1 else: self.logger.log_info("empty input") # self.speech_audio.player.play('pre_mp3/empty_input.mp3',remove_file=False) return 0 else: return 1 def command_process(self,info): '''处理大模型指令并发送给硬件模块''' command = info['chat_message'] instruction = '' if '停止' in command: instruction = 'stop' elif '加长' in command: instruction = 'insert_queue' elif '跳过' in command: instruction = 'skip_queue' else: if '变重' in command: instruction = 'adjust:force:increase' if '变轻' in command: instruction = 'adjust:force:decrease' if '升温' in command: instruction = 'adjust:temperature:increase' if '降温' in command: instruction = 'adjust:temperature:decrease' if '增电' in command: instruction = 'adjust:gear:increase' if '减电' in command: instruction = 'adjust:gear:decrease' if '增频' in command: if self.massage_status['massage_head'] == 'thermotherapy_head': instruction = 'adjust:shake:increase' elif self.massage_status['massage_head'] == 'shockwave_head': instruction = 'adjust:frequency:increase' else: instruction = 'adjust:frequency:increase' if '减频' in command: if self.massage_status['massage_head'] == 'thermotherapy_head': instruction = 'adjust:shake:decrease' elif self.massage_status['massage_head'] == 'shockwave_head': instruction = 'adjust:frequency:decrease' else: instruction = 'adjust:frequency:increase' if '增冲' in command: instruction = 'adjust:press:increase' if '减冲' in command: instruction = 'adjust:press:decrease' if '增速' in command: instruction = 'adjust:speed:increase' if '减速' in command: instruction = 'adjust:speed:decrease' if '减速' in command: instruction = 'adjust:speed:decrease' if '换向' in command: instruction = 'adjust:direction:null:null' if '升高' in command: instruction = 'adjust:high:increase:low' if '降高' in command: instruction = 'adjust:high:decrease:low' if '一点点' in command: instruction+= ':low' elif '较大幅度' in command: instruction+= ':high' elif '一点' in command: instruction+= ':mid' if instruction != '': self.send_instruction(instruction,'massage') def start_client(self): self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) self.loop.run_forever() async def client(self,instruction,uri="ws://localhost:8765"): async with websockets.connect(uri) as websocket: self.logger.log_info("begin to send") await websocket.send(instruction) self.logger.log_info("Message " + instruction + " sent to the server") def send_instruction(self,instruction = None,target='massage'): '''todo:之后改成三个进程用ros通信,每个进程都将自己的指令pub出去需要的自己sub就行''' try: if target == 'massage': uri = "ws://localhost:8765" asyncio.run_coroutine_threadsafe(self.client(instruction,uri),self.loop) self.logger.log_info("Instruction " + str(instruction) + " sent to massage") elif target == 'ui': requests.post('http://127.0.0.1:5000/update_massage_status', data=instruction) self.logger.log_info("Instruction " + str(instruction) + " sent to ui") elif target == 'ui_lip_sync': requests.post('http://127.0.0.1:5000/lip_sync', data=instruction) self.logger.log_info("Instruction " + str(instruction) + " sent to ui_lip_sync") elif target == 'ui_ai_respon': requests.post('http://127.0.0.1:5000/ai_respon', data=instruction) # self.logger.log_info("Instruction " + str(instruction) + " sent to ui_ai_respon") elif target == 'user_input': requests.post('http://127.0.0.1:5000/user_input', data=instruction) self.logger.log_info("Instruction " + str(instruction) + " sent to ui user_input") elif target == 'on_message': requests.post('http://127.0.0.1:5000/on_message', data=instruction) self.logger.log_info("Instruction " + str(instruction) + " sent to ui on_message") # 音乐请求 elif target == 'resume_music': requests.post('http://127.0.0.1:5000/resume_music') self.logger.log_info(" sent to ui resume_music") elif target == 'search': response=requests.post('http://127.0.0.1:5000/search',json={'term': instruction}) self.logger.log_info(" sent to ui search") return response elif target == 'song_clicked': requests.post('http://127.0.0.1:5000/song_clicked',json=instruction) self.logger.log_info(" sent to ui song_clicked") elif target == 'pause_music': requests.post('http://127.0.0.1:5000/pause_music') self.logger.log_info(" sent to ui pause_music") elif target == 'play_last_song': requests.post('http://127.0.0.1:5000/play_last_song') self.logger.log_info(" sent to ui play_last_song") elif target == 'play_next_song': requests.post('http://127.0.0.1:5000/play_next_song') self.logger.log_info(" sent to ui play_next_song") except Exception as e: self.logger.log_info(f"请求失败: {e}") def send_message(self,host="127.0.0.1", port=8767, message="UI_recognizing"): """ 向指定的主机和端口发送消息。 :param host: 目标主机地址 (默认: 127.0.0.1) :param port: 目标端口 (默认: 8767) :param message: 要发送的消息内容 (默认: "WAKEUP_TRIGGER") """ # 创建一个客户端 socket client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: # 连接到服务器 client_socket.connect((host, port)) # 发送消息 client_socket.sendall(message.encode('utf-8')) print(f"Message sent: {message}") except BrokenPipeError: print(f"Error: Broken pipe, could not send message.") except Exception as e: print(f"Error sending message: {e}") finally: # 关闭连接 client_socket.close() def start_server(self): async def server(): # 使用 asyncio.Future() 使服务器保持运行 async with websockets.serve(self.websocket_handler, "0.0.0.0", 8766): print("WebSocket server started on ws://0.0.0.0:8766") await asyncio.Future() # 保持服务器运行,直到手动停止 self.loop = asyncio.new_event_loop() asyncio.set_event_loop(self.loop) try: self.loop.run_until_complete(server()) # 启动 WebSocket 服务器 except asyncio.CancelledError: pass finally: print("WebSocket server closed") self.loop.close() async def websocket_handler(self, websocket, path): async for message in websocket: self.logger.log_info(f"Received: {message}") if 'user_input' in message: user_input = message.split(':')[1] self.logger.log_info(user_input) if self.speech_thread.is_alive(): self.speech_thread_stop_event.set() self.speech_thread.join() self.speech_thread_stop_event.clear() self.speech_thread = threading.Thread(target=self.run_text,args=(user_input,)) self.speech_thread.start() elif 'running_zone' in message: running_zone = message.split(':')[1] self.logger.log_blue(f"Running zone: {running_zone}") if self.running_zone != running_zone: self.running_zone = running_zone filename = re.sub(r'(_left|_right)', '', running_zone) folder_path = get_resource_path(f'pre_mp3/{filename}') full_path = self.get_random_mp3_file(folder_path) self.send_instruction({'path': full_path}, 'ui_lip_sync') threading.Thread(target=self.speech_audio.player.play, args=(full_path,False, 5)).start() elif 'speech_audio_start' in message: print("监听到按住识别开始") self.ui_speech_interrupted=True self.ui_speech_cancel=False self.ui_speech_end_flag = False self.send_message() # self.detected_callback() self.callback_thread = threading.Thread(target=self.detected_callback) self.callback_thread.start() elif 'speech_audio_stop' in message: self.send_message(message="UI_not_recognizing") print("监听到按住识别结束") self.ui_speech_end_flag = True self.ui_speech_interrupted=False _,self.ui_speech_path = message.split(":", 1) print("self.ui_speech_path",self.ui_speech_path) elif 'speech_audio_cancel' in message: self.ui_speech_interrupted=False self.ui_speech_end_flag = True self.ui_speech_cancel = True self.send_message(message="UI_not_recognizing") print("UI取消语音") elif 'change_awaken' in message: new_awaken_word = message.split(':')[1] print(new_awaken_word) print(type(new_awaken_word)) file_path = file_path = os.path.join('Hotword_awaker', 'resource', 'keyword-nhxd.txt') prompts_path = os.path.join('LLM', 'config', 'chat_prompts_new.txt') # 打开文件并处理 with open(prompts_path, 'r+', encoding='utf-8') as file: # 读取文件内容 content = file.read() # 使用正则表达式匹配并替换 '你的名字:悠悠' 中的名字部分 updated_content = re.sub(r'你的名字:.*?。', f'你的名字:{new_awaken_word}。', content) # 将文件指针移到文件开头 file.seek(0) # 写入更新后的内容 file.write(updated_content) file.truncate() print("替换完成!") # 打开文件并处理 with open(file_path, 'r+', encoding='utf-8') as file: # 读取文件内容 content = file.read() # 使用正则表达式匹配 `任意字符;nCM:300;` 并替换 updated_content = re.sub(r'.*?;nCM:30;', f"{new_awaken_word};nCM:30;", content) # 将文件指针移到文件开头 file.seek(0) # 写入更新后的内容 file.write(updated_content) file.truncate() print("替换完成!") # 打开文件并进行内容替换 cpp_file_path = file_path = os.path.join('Hotword_awaker', 'ivw_demo.cpp') with open(cpp_file_path, 'r+', encoding='utf-8') as file: # 读取文件内容 content = file.read() # 替换 `temp.find("悠悠")` 中的关键词 content = re.sub(r'temp\.find\(".*?"\) != string::npos', f'temp.find("{new_awaken_word}") != string::npos', content) # 替换 `printf("----悠悠,拦截----");` 中的关键词 content = re.sub(r'printf\("----.*?,拦截----"\);', f'printf("----{new_awaken_word},拦截----");', content) # 将修改后的内容写回文件 file.seek(0) file.write(content) file.truncate() print("C++ 文件修改完成!") try: # 使用 subprocess 执行命令 process = subprocess.Popen( ['/bin/sudo', '-S', 'systemctl', 'restart', 'language'], # 命令 stdin=subprocess.PIPE, # 管道传递密码 stdout=subprocess.PIPE, # 捕获输出 stderr=subprocess.PIPE, # 捕获错误 text=True # 以文本模式传递 ) # 写入密码到 stdin stdout, stderr = process.communicate(input='jsfb\n') # 打印输出(可选) print(stdout) if stderr: print("Error:", stderr) print("服务重启完成!") except Exception as e: print("执行命令时出错:", e) elif 'massage_status' in message: self.massage_status = eval(message[message.find("{"):message.rfind("}")+1]) # print(massage_status) elif 'massage_plan_finish' in message: path = get_resource_path(f'pre_mp3/massage_plan_finish.mp3') self.send_instruction({'path': path}, 'ui_lip_sync') self.speech_audio.player.play(file_path=path,remove_file=False) # threading.Thread(target=self.speech_audio.player.play, args=(path, False)).start() else: try: path = get_resource_path(f'pre_mp3/{message}.mp3') self.send_instruction({'path': path}, 'ui_lip_sync') threading.Thread(target=self.speech_audio.player.play, args=(path, False)).start() except Exception as e: self.logger.log_error("请检查message和mp3是否对应上") await websocket.send(f"success") def run(self): '''启动服务器和客户端的线程''' self.server_thread = threading.Thread(target=self.start_server) self.server_thread.start() self.client_thread = threading.Thread(target=self.start_client) self.client_thread.start() if __name__ == "__main__": args = parse_args() robostorm = RoboStorm(args) # print(robostorm.llm_port_config) # 捕获SIGINT和SIGTERM信号 signal.signal(signal.SIGINT, robostorm.signal_handler) # 捕获 Ctrl+C signal.signal(signal.SIGTERM, robostorm.signal_handler) # 捕获 systemctl stop # threading.Thread(target=robostorm.start_server).start() # threading.Thread(target=robostorm.start_client).start() # 使用atexit在程序退出时清理 atexit.register(robostorm.cleanup) # 启动服务 robostorm.run() if robostorm.Recognize_initialization_successful and robostorm.Synthesizer_initialization_successful and robostorm.Awaken_initialization_successful and robostorm.LLM_initialization_successful: print("1234567") threading.Thread(target=robostorm.run_hotword_detect).start() # 阻止主线程退出 robostorm.server_thread.join() robostorm.client_thread.join()