A Word on TIME_WAIT and CLOSE_WAIT

本文探讨了在网络编程中常见的TIME_WAIT状态及其解决方案, 对于如何避免和处理TIME_WAIT状态进行了深入分析。
讲解代码 import socket import threading import queue import time import utils import numpy as np import tkinter as tk from tkinter import ttk from datetime import datetime import clientDataProcess import savedata import os magic_word = b'\xAF\xBE\xAD\xDE' exit_event = threading.Event() data_queue = queue.Queue() action_info = queue.Queue() batch_storage = [] time_stamp = [] HAR_data = [] bin_fragment_list = [] PCA_results = [] class MotionDetectionUI: def __init__(self): self.root = tk.Tk() self.root.title("动作识别UI") self.root.geometry("800x600") self.root.configure(bg="#f0f5f9") # 创建按钮容器Frame,放置在底部 button_frame = tk.Frame(self.root, bg="#f0f5f9") button_frame.pack(side=tk.BOTTOM, fill=tk.X, pady=10) # 在button_frame中再创建一个Frame用于放置按钮,并使其居中 button_container = tk.Frame(button_frame, bg="#f0f5f9") button_container.pack(anchor=tk.CENTER) # 在button_frame中居中 # clear button clear_button = tk.Button(button_container, text="清空记录", font=("KaiTi_GB2312", 14,"bold"), bg="#70a1ff", fg="white", relief=tk.FLAT, command=self.clear_table) # close button close_button = tk.Button(button_container, text="退出程序", font=("KaiTi_GB2312", 14,"bold"), bg="#CD5C5C", fg="white", relief=tk.FLAT, command=self.close) clear_button.pack(side=tk.LEFT, padx=10) close_button.pack(side=tk.LEFT, padx=10) self.running = True self.after_id = None # 关闭窗口时调用 self.root.protocol("WM_DELETE_WINDOW", self.on_close) # 显示实时检测数据 self.motion_label = tk.Label(self.root, text="动作类型: None", font=("KaiTi_GB2312", 30, "bold"), fg="#2d4059", bg="#f0f5f9", anchor='w') self.motion_label.pack(padx=100,pady=5, fill='x') self.confidence_label = tk.Label(self.root, text="置信度: 0%", font=("KaiTi_GB2312", 30, "bold"), fg="#2d4059", bg="#f0f5f9", anchor='w') self.confidence_label.pack(padx=100,pady=5, fill='x') # 表格显示历史记录 self.setup_treeview_style() columns = ("动作类型", "置信度", "发生时间") self.tree = ttk.Treeview(self.root, columns=columns, show="headings", height=40) for col in columns: self.tree.heading(col, text=col) self.tree.column(col, anchor="center", width=250) self.tree.pack(fill="both",expand=True, pady=20,padx = 20) # 启动 UI 更新定时器 self.update_ui() self.root.mainloop() def setup_treeview_style(self): # 创建一个新的风格 style = ttk.Style(self.root) font_style = ('KaiTi_GB2312', 12) bold = ('KaiTi_GB2312', 14) # 或者使用你想要的字体和大小 style.configure('Treeview', rowheight=30,font=font_style) style.configure('Treeview.Heading', font=bold, relief='flat') # 表头的样式调整 def clear_table(self): for item in self.tree.get_children(): self.tree.delete(item) def close(self): exit_event.set() # 触发全局退出信号 os._exit(0) def update_ui(self): # 如果有新数据,就更新界面 try: while not action_info.empty(): detect_status, final_action, final_score, timestamp = action_info.get_nowait() print("detect_status, final_action, final_score", detect_status, final_action, final_score) if "确定" == detect_status and final_action is not None and final_score is not None: self.motion_label.config(text=f"动作类型: {final_action}") self.confidence_label.config(text=f"置 信 度: {final_score*100:.1f}%") if "等待下一次动作"!=final_action and "动作状态切换中"!=final_action: self.tree.insert("", "end", values=(final_action, f"{final_score*100:.1f}%", timestamp)) if "重复" == detect_status and final_action is not None and final_score is not None: if "等待下一次动作"!=final_action and "动作状态切换中"!=final_action: self.motion_label.config(text=f"动作类型: {detect_status}检测{final_action}") self.confidence_label.config(text=f"置 信 度: {final_score*100:.1f}%") else: self.motion_label.config(text=f"动作类型: {final_action}") self.confidence_label.config(text=f"置 信 度: {final_score*100:.1f}%") if "不确定" == detect_status: continue if None == detect_status: self.motion_label.config(text="动作类型: None") self.confidence_label.config(text="置 信 度: ...") except queue.Empty: pass # 循环更新 UI,每500ms 检查一次队列 self.after_id = self.root.after(450, self.update_ui) def on_close(self): self.running = False if self.after_id: self.root.after_cancel(self.after_id) self.root.destroy() def handle_data(): while True: data = data_queue.get()#一次取出一个包 if data is None: continue # process data bin_fragment_list.append(data) def receive_data(sock): while True: try: data = sock.recv(1024 * 2048) if not data: continue data_queue.put(data) #print("***IN size***:", data_queue.qsize()) savedata.global_debug_handle.append_debug_data(data) except ConnectionResetError: print("Connect reset by peer") break except Exception as e: print(f"Error receiving data: {e}") break def delete_dict(batch_storage,timestap): batch_storage = [(ts, pkt) for ts, pkt in batch_storage if ts > timestap] return batch_storage def start_server(host='0.0.0.0', port=12345): server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_socket.bind((host, port)) server_socket.listen(5) print(f"server listening on {host}:{port}") #如果未连接到客户端,线程处于阻塞状态,程序无法执行下去 client_socket, client_address = server_socket.accept() print(f"Accept connection from {client_address}") threading.Thread(target=receive_data, args=(client_socket,), daemon=True).start() def convert_data_handle(period): global batch_storage while True: time.sleep(period / 1000) #start_time_count = time.time() #print("---bin_fragment_list---",len(bin_fragment_list)) bin_data = b''.join(bin_fragment_list) bin_fragment_list.clear() data_list = bin_data.split(magic_word) for i in range(len(data_list)): bin_wo_mgc = magic_word + data_list[i] np_array = np.frombuffer(bin_wo_mgc, dtype=np.uint8) if len(np_array)==16284: one_pkt = clientDataProcess.trans_data_320m(np_array[216+8032:16280]) #320M host_header = clientDataProcess.released_cypress_read_cfr_host_metadata(np_array) time_stamp = host_header['timestamp'] batch_storage.append([time_stamp, one_pkt]) # ============================================================================= # end_time_count = time.time() #结束计时 # time_elapsed = end_time_count - start_time_count # print(f'转译时间: {time_elapsed:.4f} 秒') # ============================================================================= def STFT_processing_handle(process_period): global batch_storage global PCA_results first_time_low_sample_rate = True while True: time.sleep(process_period / 1000) if batch_storage: time_tamps, pkts = zip(*[(ts,pkt) for ts, pkt in batch_storage]) time_stamp_array = np.array(time_tamps) pkt_array = np.array(pkts) pkt_num_total = pkt_array.shape[0] duration = time_stamp_array[-1] - time_stamp_array[0] if int(duration/1e6) < 5: #print("Please wait for data collection...") continue if time_stamp_array[-1] == time_stamp_array[0]: #print("Please wait for data collection...") continue sample_rate = pkt_num_total//((time_stamp_array[-1] - time_stamp_array[0])/1e6) print(f"队列中堆积的数据时长为:{int((time_stamp_array[-1] - time_stamp_array[0])//1e6):d} s") #print("sample_rate",sample_rate) if sample_rate < 450: if first_time_low_sample_rate == False: print("* 请 增加 流量大小... *") batch_storage = [] continue if sample_rate < 450 and first_time_low_sample_rate: batch_storage = [] first_time_low_sample_rate = False if sample_rate > 700: batch_storage = [] print("* 请 减小 流量大小... *") continue if int(duration/1e6) >= 5 and sample_rate > 450: start_time_count = time.time() batch_storage = delete_dict(batch_storage,time_stamp_array[0]+1*1e6) s_time = time_stamp_array[0] e_time = s_time + 5e6 end_index = utils.find_index_numpy(time_stamp_array, e_time) PCA_results_4_dim = utils.stft_generate(pkt_array[:end_index,:]) PCA_results.append(PCA_results_4_dim) end_time_count = time.time() #结束计时 time_elapsed = end_time_count - start_time_count print(f'--PCA--: {time_elapsed:.4f} 秒') def net_infer_handle(process_period, path, save): global PCA_results while True: time.sleep(process_period / 1000) if PCA_results: start_time_count = time.time() PCA_result_4_dim = PCA_results.pop(0) action_type, score_value = utils.net_infer(PCA_result_4_dim, path, save) detect_status, final_action, final_score = utils.decision(action_type, score_value) timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") action_info.put((detect_status, final_action, final_score, timestamp)) end_time_count = time.time() #结束计时 time_elapsed = end_time_count - start_time_count print(f'**net**: {time_elapsed:.4f} 秒') #print("************************************************") #print(f"pkt_num_total shape: {pkt_num_total:d}") #print(f"Action type is: {action_type:d}") #print(f"Final decision is: {final_action}") #print(f"confidence is : {score_value*100:.1f} %") #print("************************************************") def begin_data_collect_handle(): while True: user_input = input("\n@CSI:") if str(user_input) == "1": #print("savedata.global_debug_handle.set_begin()") savedata.global_debug_handle.set_begin() elif str(user_input).lower() == "exit": exit_event.set() # 触发全局退出信号 os._exit(0) else: print(f"Error: 错误的输入 '{user_input}'") # ============================================================================= # def start_thread(host='0.0.0.0', port=12345, period=500,process_period=5000): # threading.Thread(target=handle_data, daemon=True).start() # threading.Thread(target=convert_data_handle, args=(int(period),), daemon=True).start() # threading.Thread(target=HAR_processing_handle, args=(int(process_period),), daemon=True).start() # threading.Thread(target=begin_data_collect_handle, daemon=True).start() # start_server(host, int(port)) # ============================================================================= if __name__ == "__main__": MotionDetectionUI()
11-20
为我写方式三的全代码 """ 【通义千问 Qwen】API集成模块 用于意图理解和任务处理 """ import json import re import logging import dashscope from dashscope import Generation from database import config from Progress.utils.logger_utils import log_time, log_step, log_var, log_call from Progress.utils.logger_config import setup_logger # --- 初始化日志器 --- logger = logging.getLogger("ai_assistant") DASHSCOPE_API_KEY = config.api_key DASHSCOPE_MODEL = config.model class QWENAssistant: def __init__(self): if not DASHSCOPE_API_KEY: raise ValueError("缺少 DASHSCOPE_API_KEY,请检查配置文件") dashscope.api_key = DASHSCOPE_API_KEY self.model_name = DASHSCOPE_MODEL or 'qwen-max' logger.info(f"✅ QWENAssistant 初始化完成,使用模型: {self.model_name}") self.conversation_history = [] self.system_prompt = """ 你是一个智能语音控制助手,能够理解用户的自然语言指令,并将其转化为可执行的任务计划。 你的职责是: - 准确理解用户意图; - 若涉及多个动作,需拆解为【执行计划】; - 输出一个严格符合规范的 JSON 对象,供系统解析执行; - 所有回复必须使用中文(仅限于 response_to_user 字段); 🎯 输出格式要求(必须遵守): { "intent": "system_control", // 意图类型:"system_control" "task_type": "start_background_tasks",// 任务类型的简要描述(动态生成) "execution_plan": [ // 执行步骤列表(每个步骤包含 operation, parameters, description) { "operation": "函数名", // 必须是已知操作之一 "parameters": { ... }, // 参数对象(按需提供) "description": "该步骤的目的说明" } ], "response_to_user": "你要对用户说的话(用中文)", "requires_confirmation": false, // 是否需要用户确认后再执行 "mode": "parallel" // 执行模式:"parallel"(并行)或 "serial"(串行) } 📌 已知 operation 列表(不可拼写错误): - play_music(music_path: str) - stop_music() - pause_music() - resume_music() - open_application(app_name: str) - create_file(file_name: str, content?: str) - read_file(file_name: str) - write_file(file_name: str, content: str) - set_reminder(reminder_time: str, message: str) - exit() 📌 规则说明: 1. 只有当用户明确要求执行系统级任务时,才设置 intent="system_control"; 否则设为 intent="chat"(例如闲聊、问天气、讲笑话等)。 2. execution_plan 中的每一步都必须与用户需求直接相关; ❌ 禁止添加无关操作(如随便加 speak_response 或 play_music)! 3. mode 决定执行方式: - 如果各步骤互不依赖 → "parallel" - 如果有先后依赖(如先打开再写入)→ "serial" 4. response_to_user 是你对用户的自然语言反馈,必须简洁友好,使用中文。 5. requires_confirmation: - 涉及删除、覆盖文件、长时间运行任务 → true - 普通操作(打开应用、播放音乐)→ false ⚠️ 重要警告: - 绝不允许照搬示例中的参数或路径!必须根据用户输入提取真实信息。 - 不得虚构不存在的 operation 或 parameter 名称。 - 不得省略任何字段,所有 key 都必须存在。 - 不得输出额外文本(如解释、注释、 ```json ``` 包裹符),只输出纯 JSON 对象。 ✅ 正确行为示例: 用户说:“帮我写一份自我介绍到 D:/intro.txt,并打开看看” → 应返回包含 write_file 和 read_file 的 serial 计划。 用户说:“播放 C:/Music/background.mp3 并告诉我准备好了” → 可以并行执行 play_music 和 speak_response。 用户说:“今天过得怎么样?” → intent="chat",response_to_user="我很好,谢谢!" 🚫 错误行为: - 把所有指令都变成和示例一样的操作组合; - 在没有请求的情况下自动添加 speak_response; - 使用未定义的操作如 run_script、send_email。 现在,请根据用户的最新指令生成对应的 JSON 响应。 """ @log_time @log_step("处理语音指令") def process_voice_command(self, voice_text): log_var("原始输入", voice_text) if not voice_text.strip(): return self._create_fallback_response("我没有听清楚,请重新说话。") self.conversation_history.append({"role": "user", "content": voice_text}) try: messages = [{"role": "system", "content": self.system_prompt}] messages.extend(self.conversation_history[-10:]) # 保留最近上下文 response = Generation.call( model=self.model_name, messages=messages, temperature=0.5, top_p=0.8, max_tokens=1024 ) if response.status_code != 200: logger.error(f"Qwen API 调用失败: {response.status_code}, {response.message}") return self._create_fallback_response(f"服务暂时不可用: {response.message}") ai_output = response.output['text'].strip() log_var("模型输出", ai_output) self.conversation_history.append({"role": "assistant", "content": ai_output}) # === 尝试解析 JSON === parsed = self._extract_and_validate_json(ai_output) if parsed: return parsed else: # 若无法解析为有效计划,则降级为普通对话 return self._create_fallback_response(ai_output) except Exception as e: logger.exception("处理语音指令时发生异常") return self._create_fallback_response("抱歉,我遇到了一些技术问题,请稍后再试。") def _extract_and_validate_json(self, text: str): """从文本中提取 JSON 并验证结构""" try: # 方法1:直接加载 data = json.loads(text) return self._validate_plan_structure(data) except json.JSONDecodeError: pass # 方法2:正则匹配第一个大括号包裹的内容 match = re.search(r'\{[\s\S]*\}', text) if not match: return None try: data = json.loads(match.group()) return self._validate_plan_structure(data) except: return None def _validate_plan_structure(self, data: dict): """验证是否符合多任务计划格式""" required_top_level = ["intent", "task_type", "execution_plan", "response_to_user", "requires_confirmation"] for field in required_top_level: if field not in data: logger.warning(f"缺少必要字段: {field}") return None valid_operations = { "play_music", "stop_music", "pause_music", "resume_music", "open_application", "create_file", "read_file", "write_file", "set_reminder", "speak_response", "exit" } for step in data["execution_plan"]: op = step.get("operation") params = step.get("parameters", {}) if not op or op not in valid_operations: logger.warning(f"无效操作: {op}") return None if not isinstance(params, dict): logger.warning(f"parameters 必须是对象: {params}") return None # 补全默认值 if "mode" not in data: data["mode"] = "parallel" return data def _create_fallback_response(self, message: str): """降级响应:用于非结构化输出""" return { "intent": "chat", "task_type": "reply", "response_to_user": message, "requires_confirmation": False, "execution_plan": [], "mode": "serial" } def _create_response(self, intent, action, parameters, response, needs_confirmation): resp = {"intent": intent, "action": action, "parameters": parameters, "response": response, "needs_confirmation": needs_confirmation} log_var("返回响应", resp) return resp @log_time def generate_text(self, prompt, task_type="general"): log_var("任务类型", task_type) log_var("提示词长度", len(prompt)) try: system_prompt = f""" 你是一个专业的文本生成助手。根据用户的要求生成高质量的文本内容。 任务类型:{task_type} 要求:{prompt} 请生成相应的文本内容,确保内容准确、有逻辑、语言流畅。 """ response = Generation.call( model=self.model_name, messages=[ {"role": "system", "content": system_prompt}, {"role": "user", "content": prompt} ], temperature=0.8, max_tokens=2000 ) if response.status_code == 200: result = response.output['text'] log_var("生成结果长度", len(result)) return result else: error_msg = f"文本生成失败: {response.message}" logger.error(error_msg) return error_msg except Exception as e: logger.exception("文本生成出错") return f"抱歉,生成文本时遇到错误:{str(e)}" @log_time def summarize_text(self, text): log_var("待总结文本长度", len(text)) try: prompt = f"请总结以下文本的主要内容:\n\n{text}" response = Generation.call( model=self.model_name, messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=500 ) if response.status_code == 200: result = response.output['text'] log_var("总结结果长度", len(result)) return result else: error_msg = f"总结失败: {response.message}" logger.error(error_msg) return error_msg except Exception as e: logger.exception("文本总结出错") return f"抱歉,总结文本时遇到错误:{str(e)}" @log_time def translate_text(self, text, target_language="英文"): log_var("目标语言", target_language) log_var("原文长度", len(text)) try: prompt = f"请将以下文本翻译成{target_language}:\n\n{text}" response = Generation.call( model=self.model_name, messages=[{"role": "user", "content": prompt}], temperature=0.3, max_tokens=1000 ) if response.status_code == 200: result = response.output['text'] log_var("翻译结果长度", len(result)) return result else: error_msg = f"翻译失败: {response.message}" logger.error(error_msg) return error_msg except Exception as e: logger.exception("文本翻译出错") return f"抱歉,翻译文本时遇到错误:{str(e)}" assistant = QWENAssistant() from functools import wraps import inspect import logging from Progress.app.qwen_assistant import assistant # 全局注册表 REGISTERED_FUNCTIONS = {} FUNCTION_SCHEMA = [] FUNCTION_MAP = {} # (intent, action) -> method_name logger = logging.getLogger("ai_assistant") def ai_callable( *, description: str, params: dict, intent: str = None, action: str = None, concurrent: bool = False # 新增:是否允许并发执行 ): def decorator(func): func_name = func.__name__ metadata = { "func": func, "description": description, "params": params, "intent": intent, "action": action, "signature": str(inspect.signature(func)), "concurrent": concurrent # 记录是否可并发 } REGISTERED_FUNCTIONS[func_name] = metadata FUNCTION_SCHEMA.append({ "name": func_name, "description": description, "parameters": params }) if intent and action: key = (intent, action) if key in FUNCTION_MAP: raise ValueError(f"冲突:语义 ({intent}, {action}) 已被函数 {FUNCTION_MAP[key]} 占用") FUNCTION_MAP[key] = func_name @wraps(func) def wrapper(*args, **kwargs): return func(*args, **kwargs) wrapper._ai_metadata = metadata return wrapper return decorator """ 【语音识别模块】Speech Recognition (Offline) 使用麦克风进行实时语音识别,基于 Vosk 离线模型 支持单次识别 & 持续监听模式 音量可视化、模型路径检查、资源安全释放 """ import random import threading import time import logging import json import os from vosk import Model, KaldiRecognizer import pyaudio from database import config from Progress.utils.logger_utils import log_time, log_step, log_var, log_call from Progress.utils.logger_config import setup_logger # --- 配置参数 --- VOICE_TIMEOUT = config.timeout # 最大等待语音输入时间(秒) VOICE_PHRASE_TIMEOUT = config.phrase_timeout # 单句话最长录音时间 VOSK_MODEL_PATH = "./vosk-model-small-cn-0.22" # --- 初始化日志器 --- logger = logging.getLogger("ai_assistant") # 定义最小有效音量阈值 MIN_VOLUME_THRESHOLD = 600 # 可调(根据环境测试) class SpeechRecognizer: def __init__(self): self.current_timeout = 10 # 可被外部动态调整 self.model = None self.recognizer = None self.audio = None self.is_listening = False self.callback = None # 用户注册的回调函数:callback(text) self._last_text = "" self._listen_thread = None self.sample_rate = 16000 # Vosk 要求采样率 16kHz self.chunk_size = 1600 # 推荐帧大小(对应 ~100ms) # 🔒 TTS 播放状态标志(由外部控制) self._is_tts_playing = False self._tts_lock = threading.Lock() self._load_model() self._init_audio_system() @property def is_tts_playing(self) -> bool: with self._tts_lock: return self._is_tts_playing def set_tts_playing(self, status: bool): """供 TTS 模块调用:通知当前是否正在播放""" with self._tts_lock: self._is_tts_playing = status if not status: logger.debug("🟢 TTS 播放结束,语音识别恢复") @log_step("加载 Vosk 离线模型") @log_time def _load_model(self): """加载本地 Vosk 模型""" if not os.path.exists(VOSK_MODEL_PATH): raise FileNotFoundError(f"❌ Vosk 模型路径不存在: {VOSK_MODEL_PATH}\n","请从 https://alphacephei.com/vosk/models 下载中文小模型并解压至此路径") try: logger.info(f"📦 正在加载模型: {VOSK_MODEL_PATH}") self.model = Model(VOSK_MODEL_PATH) log_call("✅ 模型加载成功") except Exception as e: logger.critical(f"🔴 加载 Vosk 模型失败: {e}") raise RuntimeError("Failed to load Vosk model") from e @log_step("初始化音频系统") @log_time def _init_audio_system(self): """初始化 PyAudio 并创建全局 recognizer""" try: self.audio = pyaudio.PyAudio() # 创建默认识别器(可在每次识别前 Reset) self.recognizer = KaldiRecognizer(self.model, self.sample_rate) logger.debug("✅ 音频系统初始化完成") except Exception as e: logger.exception("❌ 初始化音频系统失败") raise @property def last_text(self) -> str: return self._last_text def is_available(self) -> bool: """检查麦克风是否可用""" if not self.audio: return False try: stream = self.audio.open( format=pyaudio.paInt16, channels=1, rate=self.sample_rate, input=True, frames_per_buffer=self.chunk_size ) stream.close() return True except Exception as e: logger.error(f"🔴 麦克风不可用或无权限: {e}") return False @log_step("执行单次语音识别(自适应超时)") @log_time def listen_and_recognize(self, initial_timeout=10, long_speech_threshold=3.0, post_speech_long_wait=15, post_speech_short_wait=5) -> str: """ 自适应语音识别:用户不说话时等待,说话后根据情况延长等待。 参数说明: initial_timeout: 初始等待语音输入的最大时间(秒) long_speech_threshold: 被认为是“长句”的最小语音持续时间(秒) post_speech_long_wait: 长句后的等待时间(秒) post_speech_short_wait: 短句或中途停顿时的等待时间(秒) """ start_time = time.time() speech_start_time = None # 记录首次检测到语音的时间 last_speech_time = None # 最后一次有语音片段的时间 in_speech = False # 是否正在语音中 final_result_text = "" silence_start_time = None # 开始静默的时间点 logger.debug(f"🎙️ 开始自适应语音识别 (初始等待={initial_timeout}s)...") if self.is_tts_playing: logger.info("🔇 TTS 正在播放,跳过本次识别") return "" logger.info("🔊 请说话...") stream = None try: recognizer = KaldiRecognizer(self.model, self.sample_rate) stream = self.audio.open( format=pyaudio.paInt16, channels=1, rate=self.sample_rate, input=True, frames_per_buffer=self.chunk_size ) while True: current_time = time.time() # 检查是否应中断(TTS 播放) if self.is_tts_playing: logger.info("🔇 TTS 开始播放,中断识别") break # 超出初始等待时间且尚未开始说话 → 超时退出 if not in_speech and (current_time - start_time) > initial_timeout: logger.info("💤 初始等待超时,未检测到语音输入") break # 读取音频数据 data = stream.read(self.chunk_size, exception_on_overflow=False) # 获取音量(用于调试或可视化) audio_level = max(data) is_speaking = audio_level > MIN_VOLUME_THRESHOLD # 处理 Vosk 识别结果 if recognizer.AcceptWaveform(data): result_json = recognizer.FinalResult() text = json.loads(result_json).get("text", "").strip() if text: final_result_text = text last_speech_time = current_time in_speech = True if speech_start_time is None: speech_start_time = current_time logger.debug(f"✅ 完整句子识别: '{text}'") recognizer.Reset() # 重置以便下一句 else: partial_json = recognizer.PartialResult() partial_text = json.loads(partial_json).get("partial", "").strip() if partial_text: last_speech_time = current_time if not in_speech: in_speech = True speech_start_time = current_time logger.debug(f"🎤 检测到语音开始: '{partial_text}'") # === 动态判断是否结束 === if in_speech and last_speech_time: elapsed_since_last_speech = current_time - last_speech_time # 决定当前该用哪个等待阈值 if speech_start_time and (current_time - speech_start_time) > long_speech_threshold: # 长句模式:等久一点 wait_duration = post_speech_long_wait else: # 短句或刚说一点就停:快速收尾 wait_duration = post_speech_short_wait # 设置静默起点(首次进入静默) if is_speaking: silence_start_time = None else: if silence_start_time is None: silence_start_time = current_time elif (current_time - silence_start_time) >= wait_duration: logger.info(f"🔚 静默超过 {wait_duration}s,判定语音结束") break time.sleep(0.05) # 小延迟减少 CPU 占用 # 返回最终识别结果 if final_result_text: self._last_text = final_result_text logger.info(f"🎯 识别结果: '{final_result_text}'") return final_result_text else: logger.info("❓ 未识别到有效内容") self._last_text = "" return "" except Exception as e: logger.exception("🔴 执行语音识别时发生异常") self._last_text = "" return "" finally: if stream: try: stream.stop_stream() stream.close() except Exception as e: logger.warning(f"⚠️ 关闭音频流失败: {e}") @log_step("启动持续语音监听") def start_listening(self, callback=None, language=None): """ 启动后台线程持续监听语音输入 :param callback: 回调函数,接受一个字符串参数 text :param language: 语言代码(忽略,由模型决定) """ if self.is_listening: logger.warning("⚠️ 已在监听中,忽略重复启动") return if not callable(callback): logger.error("🔴 回调函数无效,请传入可调用对象") return self.callback = callback self.is_listening = True self._listen_thread = threading.Thread(target=self._background_listen, args=(language,), daemon=True) self._listen_thread.start() logger.info("🟢 已启动后台语音监听") @log_step("停止语音监听") def stop_listening(self): """安全停止后台监听""" if not self.is_listening: return self.is_listening = False logger.info("🛑 正在停止语音监听...") if self._listen_thread and self._listen_thread != threading.current_thread(): self._listen_thread.join(timeout=3) if self._listen_thread.is_alive(): logger.warning("🟡 监听线程未能及时退出(可能阻塞)") elif self._listen_thread == threading.current_thread(): logger.error("❌ 无法在当前线程中 join 自己!请检查调用栈") else: logger.debug("No thread to join") logger.info("✅ 语音监听已停止") def _background_listen(self, language=None): """后台循环监听线程""" logger.debug("🎧 后台监听线程已启动") stream = None try: stream = self.audio.open( format=pyaudio.paInt16, channels=1, rate=self.sample_rate, input=True, frames_per_buffer=self.chunk_size ) except Exception as e: logger.error(f"🔴 无法打开音频流: {e}") return try: while self.is_listening: # 🔴 检查是否正处于 TTS 播放中 → 跳过本次读取 if self.is_tts_playing: time.sleep(0.1) # 减少 CPU 占用 continue try: data = stream.read(self.chunk_size, exception_on_overflow=False) if self.recognizer.AcceptWaveform(data): result_json = self.recognizer.Result() result_dict = json.loads(result_json) text = result_dict.get("text", "").strip() if text and self.callback: logger.info(f"🔔 回调触发: '{text}'") self.callback(text) self.recognizer.Reset() else: partial = json.loads(self.recognizer.PartialResult()) partial_text = partial.get("partial", "") if partial_text.strip(): logger.debug(f"🗣️ 当前语音片段: '{partial_text}'") except Exception as e: logger.exception("Background listening error") time.sleep(0.05) finally: if stream: stream.stop_stream() stream.close() logger.debug("🔚 后台监听线程退出") recognizer = SpeechRecognizer() """ 【AI语音助手】主程序入口 集成语音识别、Qwen 意图理解、TTS 与动作执行 ✅ 已修复:不再访问 _last_text 私有字段 ✅ 增强:异常防护、类型提示、唤醒词预留接口 """ import random import sys import time import logging # --- 导入日志工具 --- from Progress.utils.logger_config import setup_logger from Progress.utils.logger_utils import log_time, log_step, log_var, log_call # --- 显式导入各模块核心类 --- from Progress.app.voice_recognizer import recognizer from Progress.app.qwen_assistant import assistant from Progress.app.text_to_speech import tts_engine from Progress.app.system_controller import executor from database import config # --- 初始化全局日志器 --- logger = logging.getLogger("ai_assistant") @log_step("处理一次语音交互(AI动态控制等待)") @log_time def handle_single_interaction(): text = recognizer.listen_and_recognize(recognizer.current_timeout) if not text: logger.info("🔇 未检测到语音") return logger.info(f"🗣️ 用户说: '{text}'") # AI 决策包含是否预期后续输入 decision = assistant.process_voice_command(text) expect_follow_up = decision.get("expect_follow_up", False) # 执行任务 result = executor.execute_task_plan(decision) ai_reply = build_reply(result) # 根据 AI 判断设置下次等待策略 if expect_follow_up: recognizer.current_timeout = random.uniform(10, 20) logger.debug("🧠 AI 预期后续提问,延长等待时间") else: recognizer.current_timeout = 5 logger.debug("🔚 AI 认为对话结束,缩短等待") # 回复 logger.info(f"🤖 回复: {ai_reply}") tts_engine.speak(ai_reply) @log_step("启动 AI 语音助手") @log_time def main(): logger.info("🚀 正在启动 AI 语音助手系统...") try: tts_engine.start() log_call("✅ 所有模块初始化完成,进入监听循环") log_call("\n" + "—" * 50) log_call("🎙️ 语音助手已就绪") log_call("💡 说出你的命令,例如:'打开浏览器'、'写一篇春天的文章'") log_call("🛑 说出‘退出’、‘关闭’、‘停止’或‘拜拜’来结束程序") log_call("—" * 50 + "\n") while True: try: handle_single_interaction() # 🚩 检查上一次执行的结果是否有退出请求 last_result = executor.last_result # 假设 TaskOrchestrator 记录了 last_result if last_result and last_result.get("should_exit"): logger.info("🎯 接收到退出指令,即将终止程序...") break # 跳出循环,进入清理流程 except KeyboardInterrupt: logger.info("🛑 用户主动中断 (Ctrl+C),准备退出...") raise # 让 main 捕获并退出 except Exception as e: logger.exception("⚠️ 单次交互过程中发生异常,已降级处理") error_msg = "抱歉,我在处理刚才的操作时遇到了一点问题。" logger.info(f"🗣️ 回复: {error_msg}") tts_engine.speak(error_msg) last_text = recognizer.last_text.lower() exit_keywords = ['退出', '关闭', '停止', '拜拜', '再见'] if any(word in last_text for word in exit_keywords): logger.info("🎯 用户请求退出,程序即将终止") break time.sleep(0.5) tts_engine.stop() logger.info("👋 语音助手已安全退出") except KeyboardInterrupt: logger.info("🛑 用户通过 Ctrl+C 中断程序") print("\n👋 再见!") except Exception as e: logger.exception("❌ 主程序运行时发生未预期异常") print(f"\n🚨 程序异常终止:{e}") sys.exit(1) if __name__ == "__main__": if not logging.getLogger().handlers: setup_logger(name="ai_assistant", log_dir="logs", level=logging.INFO) main()
10-25
采用PyQt5框架与Python编程语言构建图书信息管理平台 本项目基于Python编程环境,结合PyQt5图形界面开发库,设计实现了一套完整的图书信息管理解决方案。该系统主要面向图书馆、书店等机构的日常运营需求,通过模块化设计实现了图书信息的标准化管理流程。 系统架构采用典型的三层设计模式,包含数据存储层、业务逻辑层和用户界面层。数据持久化方案支持SQLite轻量级数据库与MySQL企业级数据库的双重配置选项,通过统一的数据库操作接口实现数据存取隔离。在数据建模方面,设计了包含图书基本信息、读者档案、借阅记录等核心数据实体,各实体间通过主外键约束建立关联关系。 核心功能模块包含六大子系统: 1. 图书编目管理:支持国际标准书号、中国图书馆分类法等专业元数据的规范化著录,提供批量导入与单条录入两种数据采集方式 2. 库存动态监控:实时追踪在架数量、借出状态、预约队列等流通指标,设置库存预警阈值自动提醒补货 3. 读者服务管理:建立完整的读者信用评价体系,记录借阅历史与违规行为,实施差异化借阅权限管理 4. 流通业务处理:涵盖借书登记、归还处理、续借申请、逾期计算等标准业务流程,支持射频识别技术设备集成 5. 统计报表生成:按日/月/年周期自动生成流通统计、热门图书排行、读者活跃度等多维度分析图表 6. 系统维护配置:提供用户权限分级管理、数据备份恢复、操作日志审计等管理功能 在技术实现层面,界面设计遵循Material Design设计规范,采用QSS样式表实现视觉定制化。通过信号槽机制实现前后端数据双向绑定,运用多线程处理技术保障界面响应流畅度。数据验证机制包含前端格式校验与后端业务规则双重保障,关键操作均设有二次确认流程。 该系统适用于中小型图书管理场景,通过可扩展的插件架构支持功能模块的灵活组合。开发过程中特别注重代码的可维护性,采用面向对象编程范式实现高内聚低耦合的组件设计,为后续功能迭代奠定技术基础。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值