from PySide6.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout,
                             QHBoxLayout, QPushButton, QComboBox, QLabel,
                             QScrollArea, QSplitter, QGroupBox, QFormLayout,
                             QDoubleSpinBox, QCheckBox, QLineEdit, QSpinBox,
                             QMessageBox, QMenuBar, QMenu, QFileDialog, QProgressBar)
from PySide6.QtCore import Qt, QObject, Signal, Slot, QTimer
import numpy as np
import matplotlib
# 设置字体为支持中文的字体
matplotlib.rcParams['font.family'] = ['Microsoft YaHei', 'SimHei', 'SimSun']
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg
from matplotlib.figure import Figure
import json
from pathlib import Path
import os
import hid  # 添加hidapi导入
import logging  # 添加logging模块
import struct  # 添加struct模块用于float打包/解包

# 定义日志级别
LOG_LEVEL_ERROR = 0    # 错误信息
LOG_LEVEL_WARN = 1     # 警告信息
LOG_LEVEL_INFO = 2     # 一般信息
LOG_LEVEL_DEBUG = 3    # 调试信息
LOG_LEVEL_VERBOSE = 4  # 详细信息

# 日志级别名称映射
LOG_LEVEL_NAMES = {
    LOG_LEVEL_ERROR: "错误",
    LOG_LEVEL_WARN: "警告",
    LOG_LEVEL_INFO: "信息",
    LOG_LEVEL_DEBUG: "调试",
    LOG_LEVEL_VERBOSE: "详细"
}

def log_message(level, message, current_level=LOG_LEVEL_INFO):
    """打印日志信息
    Args:
        level: 消息的日志级别
        message: 要打印的消息
        current_level: 当前的日志级别设置
    """
    if level <= current_level:
        prefix = f"[{LOG_LEVEL_NAMES[level]}]"
        print(f"{prefix} {message}")

# 导入滤波器计算器
from filter_utils import (BiquadFilterCalculator, make_biquad_bypass, make_biquad_lowpass,
                        make_biquad_highpass, make_biquad_bandpass, make_biquad_bandstop,
                        make_biquad_notch, make_biquad_allpass, make_biquad_peaking,
                        make_biquad_constant_q, make_biquad_lowshelf, make_biquad_highshelf)

# 常量定义
MAX_EQ_BANDS = 8  # 最大EQ滤波器数量 (0-7)
NUM_EQ_MODES = 12  # EQ模式数量（10预设 + 2用户）
EQ_DISABLED_MODE = 12  # 禁用EQ的模式编号
# HID 固件升级命令码（使用命令名代替裸命令码）
FIRMWARE_UPGRADE_START = 0xA7      # 开始固件升级
FIRMWARE_UPGRADE_DATA = 0xA8       # 传输固件数据块
FIRMWARE_UPGRADE_END = 0xA9        # 结束固件升级
FIRMWARE_UPGRADE_STATUS = 0xAA     # 获取升级状态
FIRMWARE_UPGRADE_ABORT = 0xAB      # 中止固件升级
FIRMWARE_UPGRADE_ERASE = 0xAC      # 擦除现有升级镜像
DEVICE_REBOOT = 0xAE               # 设备重启（通用命令）

class BandFilter(QGroupBox):
    """单个滤波器带控件"""
    def __init__(self, band_id, parent=None):
        super().__init__(f"滤波器 {band_id}", parent)
        self.band_id = band_id
        self.parent_window = parent
        self.calculator = BiquadFilterCalculator()  # 创建滤波器计算器实例

        layout = QFormLayout()

        # 启用/禁用复选框 - 默认启用但隐藏
        self.enabled = QCheckBox()
        self.enabled.setChecked(True)
        #self.enabled.hide()  # 隐藏复选框

        # 滤波器类型选择 - 默认为全通
        self.filter_type = QComboBox()
        self.filter_type.addItems([
            "bypass滤波器",  # 新增bypass类型
            "全通滤波器", "峰值滤波器", "低通滤波器", "高通滤波器",
            "带通滤波器", "带阻滤波器", "陷波滤波器", "恒定Q值滤波器",
            "低架滤波器", "高架滤波器"
        ])
        self.filter_type.setCurrentText("bypass滤波器")  # 设置默认值为bypass
        self.filter_type.currentTextChanged.connect(self.on_filter_type_changed)
        layout.addRow("类型:", self.filter_type)

        # 中心频率
        self.fc = QSpinBox()
        self.fc.setRange(20, 96000)
        self.fc.setValue(1000)
        self.fc.setSuffix(" Hz")
        self.fc.valueChanged.connect(self.on_parameter_changed)
        layout.addRow("频率:", self.fc)

        # Q值
        self.Q = QDoubleSpinBox()
        self.Q.setRange(0.01, 100)
        self.Q.setValue(0.707)
        self.Q.setSingleStep(0.1)
        self.Q.valueChanged.connect(self.on_parameter_changed)
        layout.addRow("Q值:", self.Q)

        # 带宽
        self.bw = QSpinBox()
        self.bw.setRange(1, 192000)
        self.bw.setValue(1)
        self.bw.valueChanged.connect(self.on_parameter_changed)
        layout.addRow("带宽:", self.bw)

        # 增益
        self.gain = QDoubleSpinBox()
        self.gain.setRange(-80, 80)
        self.gain.setValue(0)
        self.gain.setSuffix(" dB")
        self.gain.valueChanged.connect(self.on_parameter_changed)
        layout.addRow("增益:", self.gain)

        # 添加bshift显示
        self.bshift_label = QLabel("bshift: 0")
        #layout.addRow("bshift:", self.bshift_label)
        self.bshift_label.hide()

        # 系数显示
        self.coeffs_label = QLineEdit()
        self.coeffs_label.setReadOnly(True)  # 设置为只读
        self.coeffs_label.setMinimumWidth(300)  # 设置最小宽度
        self.coeffs_label.setStyleSheet("""
            QLineEdit {
                background-color: #f0f0f0;
                border: 1px solid #cccccc;
                padding: 2px;
                font-family: monospace;
            }
        """)
        self.coeffs_label.setText("Coefficients: Not calculated")
        self.coeffs_label.setToolTip("Filter coefficients in Q30 format")
        #layout.addRow("系数:", self.coeffs_label)
        self.coeffs_label.hide()

        self.setLayout(layout)

        # 初始化时更新系数
        if self.parent_window and self.parent_window.show_qshift_debug:
            self.update_coefficients()

    def on_filter_type_changed(self):
        """当滤波器类型改变时更新参数显示"""
        filter_type = self.filter_type.currentText()

        # 隐藏所有参数
        self.fc.hide()
        self.Q.hide()
        self.bw.hide()
        self.gain.hide()

        # 根据滤波器类型显示相应参数
        if filter_type in ["峰值滤波器", "低通滤波器", "高通滤波器", "陷波滤波器",
                          "全通滤波器", "恒定Q值滤波器", "低架滤波器", "高架滤波器"]:
            self.fc.show()
            self.Q.show()
            if filter_type in ["峰值滤波器", "恒定Q值滤波器", "低架滤波器", "高架滤波器"]:
                self.gain.show()
        elif filter_type in ["带通滤波器", "带阻滤波器"]:
            self.fc.show()
            self.bw.show()

        elif filter_type == "bypass滤波器":
            pass

        self.on_parameter_changed()

    def on_parameter_changed(self):
        """当参数改变时更新系数和响应曲线"""
        if self.parent_window and self.parent_window.show_qshift_debug:
            self.update_coefficients()
        if self.parent_window:
            self.parent_window.update_eq_curve()

    def update_coefficients(self):
        """更新滤波器系数"""
        if not self.enabled.isChecked():
            if self.parent_window and self.parent_window.show_qshift_debug:
                log_message(LOG_LEVEL_DEBUG, f"滤波器 {self.band_id} 已禁用，bshift = 0", self.parent_window.log_level)
            return

        try:
            fs = float(self.parent_window.sample_rate.currentText())
            coeffs = self.calculate_coefficients(fs)

            # 只有在显示系数时才更新显示
            if self.parent_window and self.parent_window.show_qshift_debug and coeffs is not None:
                # 反转 b1 和 b2 的符号用于显示
                display_coeffs = [
                    coeffs[0],  # a0
                    coeffs[1],  # a1
                    coeffs[2],  # a2
                    -coeffs[3], # b1 符号取反
                    -coeffs[4]  # b2 符号取反
                ]

                # 计算并更新bshift
                bshift = self.calculate_bshift(display_coeffs)

                # 计算缩放后的系数
                scale_factor = 2 ** (-bshift)

                # 更新系数显示
                self.coeffs_label.setText(
                    f"Coefficients: {display_coeffs[0]:.16f}, {display_coeffs[1]:.16f}, "
                    f"{display_coeffs[2]:.16f}, {display_coeffs[3]:.16f}, {display_coeffs[4]:.16f}\n"
                    f"Scaled: {display_coeffs[0] * scale_factor:.16f}, {display_coeffs[1] * scale_factor:.16f}, "
                    f"{display_coeffs[2] * scale_factor:.16f}, {display_coeffs[3] * scale_factor:.16f}, {display_coeffs[4] * scale_factor:.16f}"
                )

        except Exception as e:
            if self.parent_window and self.parent_window.show_qshift_debug:
                self.coeffs_label.setText("Coefficients: Calculation error")
                self.bshift_label.setText("bshift: 0")

    def calculate_coefficients(self, fs):
        """计算滤波器系数"""
        try:
            filter_type = self.filter_type.currentText()

            # 定义filter_calculator支持的类型
            calculator_types = {
                "峰值滤波器": "peak",
                "低通滤波器": "lowpass",
                "高通滤波器": "highpass",
                "带通滤波器": "bandpass",
                "陷波滤波器": "notch",
                "低架滤波器": "lowshelf",
                "高架滤波器": "highshelf"
            }

            # 如果是filter_calculator支持的类型，使用filter_calculator
            if filter_type in calculator_types:
                coeffs = self.calculator.calculate(
                    calculator_types[filter_type],
                    self.fc.value(),
                    fs,
                    self.Q.value(),
                    self.gain.value(),
                    self.bw.value() if filter_type in ['bandpass', 'bandstop'] else None
                )
                return [coeffs['a0'], coeffs['a1'], coeffs['a2'], coeffs['b1'], coeffs['b2']]

            # 对于不支持的类型，使用biquad.py中的算法
            if filter_type == "全通滤波器":
                coeffs = make_biquad_allpass(fs, self.fc.value(), self.Q.value())
            elif filter_type == "带阻滤波器":
                coeffs = make_biquad_bandstop(fs, self.fc.value(), self.bw.value())
            elif filter_type == "恒定Q值滤波器":
                coeffs = make_biquad_constant_q(fs, self.fc.value(), self.Q.value(), self.gain.value())
            elif filter_type == "bypass滤波器":
                coeffs = make_biquad_bypass(fs)
            else:
                # 如果都不支持，使用bypass
                coeffs = make_biquad_bypass(fs)

            return coeffs

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"计算系数时出错: {str(e)}", self.parent_window.log_level if self.parent_window else LOG_LEVEL_ERROR)
            return None

    def get_parameters(self):
        """获取当前参数"""
        params = {
            "enabled": self.enabled.isChecked(),
            "type": self.filter_type.currentText(),
            "fc": self.fc.value(),
            "Q": self.Q.value(),
            "bw": self.bw.value(),
            "gain": self.gain.value()
        }
        return params

    def set_parameters(self, params):
        """设置滤波器参数"""
        try:
            # 设置启用状态
            self.enabled.setChecked(params.get("enabled", True))

            # 设置滤波器类型
            filter_type = params.get("type", "全通滤波器")
            self.filter_type.setCurrentText(filter_type)

            # 设置频率
            self.fc.setValue(params.get("fc", 1000))

            # 设置Q值
            self.Q.setValue(params.get("Q", 0.707))

            # 设置带宽
            self.bw.setValue(params.get("bw", 1))

            # 设置增益
            self.gain.setValue(params.get("gain", 0))

            # 更新显示
            self.on_parameter_changed()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"设置滤波器参数时出错: {str(e)}", self.parent_window.log_level if self.parent_window else LOG_LEVEL_ERROR)

    def calculate_bshift(self, coeffs):
        """根据最大b系数的二进制位宽自动计算bshift（与_get_bshift一致，直接返回bshift）"""
        if not self.enabled.isChecked():
            if self.parent_window and self.parent_window.show_qshift_debug:
                log_message(LOG_LEVEL_DEBUG, f"滤波器 {self.band_id} 已禁用，bshift = 0", self.parent_window.log_level)
            return 0
        b_coeffs = coeffs[:3]
        max_b = max(abs(x) for x in b_coeffs)
        if self.parent_window and self.parent_window.show_qshift_debug:
            log_message(LOG_LEVEL_DEBUG, f"滤波器 {self.band_id} b系数最大值: {max_b:.6f}", self.parent_window.log_level)
        if max_b != 0:
            bshift = int(np.floor(np.log2(max_b)))
        else:
            bshift = 0
        bshift = bshift if bshift >= 0 else 0
        if self.parent_window and self.parent_window.show_qshift_debug:
            log_message(LOG_LEVEL_DEBUG, f"滤波器 {self.band_id} bshift={bshift}", self.parent_window.log_level)
        self.bshift_label.setText(f"bshift: {bshift}")
        return bshift

    def get_bshift(self):
        """获取当前滤波器的bshift"""
        coeffs = self.calculate_coefficients(float(self.parent_window.sample_rate.currentText()))
        if coeffs is None:
            return 0
        bshift = self.calculate_bshift(coeffs)
        return bshift

    def get_scale_factor(self):
        """获取当前滤波器的scale_factor（由bshift计算）"""
        bshift = self.get_bshift()
        scale_factor = 2 ** (-bshift)
        return scale_factor

class MplCanvas(FigureCanvasQTAgg):
    def __init__(self, parent=None, width=5, height=4, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.subplots(1, 1)  # 改为1行1列

        # 更新标题为中文
        fig.suptitle("PEQ响应曲线")

        # 设置子图属性
        self.axes.set_xscale('log')
        self.axes.set_xlabel('频率 (Hz)')
        self.axes.set_ylabel('增益 (dB)')
        self.axes.grid(True, which='both', linestyle='-', alpha=0.3)
        self.axes.set_xlim(20, 20000)
        self.axes.set_ylim(-30, 30)

        super().__init__(fig)

    def update_plot(self, f, h, fs):
        """更新响应曲线"""
        try:
            # 计算幅度响应
            h_db = 20 * np.log10(np.abs(h))

            # 清除当前图形
            self.axes.clear()

            # 绘制新的响应曲线
            self.axes.semilogx(f, h_db, 'b-', linewidth=2, label='总响应')

            # 设置x轴范围为20Hz到采样率的一半
            x_max = fs / 2
            self.axes.set_xlim([20, x_max])

            # 设置x轴刻度
            x_ticks = [20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000]
            if x_max > 20000:
                x_ticks.extend([40000, 80000])
            self.axes.set_xticks(x_ticks)

            # 设置网格
            self.axes.grid(True, which='both', linestyle='-', alpha=0.3)

            # 更新y轴和x轴标签
            self.axes.set_ylim([-30, 30])
            self.axes.set_ylabel("幅度 (dB)")
            self.axes.set_xlabel("频率 (Hz)")

            # 添加图例
            self.axes.legend(prop={'family': ['Microsoft YaHei', 'SimHei', 'SimSun']})

            # 重绘图形
            self.draw()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"更新响应曲线时出错: {str(e)}", self.parent_window.log_level if self.parent_window else LOG_LEVEL_ERROR)

class EQDesigner(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("EQ参数设计器")
        self.setMinimumSize(1200, 800)

        # 添加控制是否显示系数的变量
        self.show_qshift_debug = False  # 默认不显示qshift调试信息
        self.current_mode = 0  # 初始化当前模式为0
        self.total_bshift = 0  # 初始化total_bshift为0
        self.log_level = LOG_LEVEL_VERBOSE # 设置默认日志级别

        # HID通知轮询定时器（接收设备主动上报的0x9F采样率变化等通知）
        self._notification_timer = QTimer(self)
        self._notification_timer.timeout.connect(self._poll_hid_notification)

        # 创建菜单栏
        # 改动原因：添加菜单栏，包含视图菜单用于隐藏/显示左侧面板的各组
        self.create_menu_bar()

        # 创建主窗口部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)

        # 创建主布局
        main_layout = QHBoxLayout(main_widget)

        # 创建分割器
        splitter = QSplitter(Qt.Horizontal)

        # 左侧控制面板
        left_panel = QWidget()
        left_layout = QVBoxLayout(left_panel)
        left_layout.setContentsMargins(5, 5, 5, 5)  # 设置边距

        # 创建左侧面板内容的滚动区域，让所有内容都能滚动查看
        # 改动原因：解决左侧面板内容太多显示不下的问题，添加滚动条让所有内容都能查看
        left_scroll = QScrollArea()
        left_scroll.setWidgetResizable(True)
        left_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)  # 需要时显示水平滚动条
        left_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)    # 需要时显示垂直滚动条

        # 创建左侧内容容器
        left_content = QWidget()
        left_content_layout = QVBoxLayout(left_content)
        left_content_layout.setContentsMargins(5, 5, 5, 5)

        # 滤波器带滚动区域（仅用于滤波器带的滚动）
        # 改动原因：增加滤波器参数的空间，设置最小高度以确保有足够的显示空间
        bands_scroll = QScrollArea()
        bands_scroll.setWidgetResizable(True)
        bands_scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        bands_scroll.setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        bands_scroll.setMinimumHeight(400)  # 设置最小高度400像素，给滤波器参数更多显示空间
        self.bands_widget = QWidget()
        self.bands_layout = QVBoxLayout(self.bands_widget)
        bands_scroll.setWidget(self.bands_widget)
        left_content_layout.addWidget(bands_scroll)

        # 采样率选择
        sample_rate_layout = QHBoxLayout()
        sample_rate_layout.addWidget(QLabel("采样率:"))
        self.sample_rate = QComboBox()
        self.sample_rate.addItems(["44100"])  # 只保留44100Hz，其他采样率使用相同参数
        self.sample_rate.currentTextChanged.connect(self.on_sample_rate_changed)
        sample_rate_layout.addWidget(self.sample_rate)
        left_content_layout.addLayout(sample_rate_layout)

        # 添加调试选项
        debug_layout = QHBoxLayout()
        self.debug_checkbox = QCheckBox("显示bshift调试信息")
        self.debug_checkbox.stateChanged.connect(self.on_debug_changed)
        self.debug_checkbox.hide()
        debug_layout.addWidget(self.debug_checkbox)

        # 添加日志级别选择
        log_level_layout = QHBoxLayout()
        self.debug_label = QLabel("调试信息:")
        self.debug_label.hide()
        log_level_layout.addWidget(self.debug_label)
        self.log_level_combo = QComboBox()
        self.log_level_combo.addItems(["错误", "警告", "信息", "调试", "详细"])
        self.log_level_combo.setCurrentIndex(2)  # 默认选择"信息"级别
        self.log_level_combo.currentIndexChanged.connect(self.on_log_level_changed)
        self.log_level_combo.hide()

        log_level_layout.addWidget(self.log_level_combo)
        debug_layout.addLayout(log_level_layout)
        left_content_layout.addLayout(debug_layout)

        # 添加设备选择下拉框
        device_layout = QHBoxLayout()
        device_layout.addWidget(QLabel("USB设备:"))
        self.device_combo = QComboBox()
        print(self.device_combo)
        device_layout.addWidget(self.device_combo)
        refresh_button = QPushButton("获取设备")
        refresh_button.clicked.connect(self.refresh_devices)
        device_layout.addWidget(refresh_button)
        left_content_layout.addLayout(device_layout)

        # 保存各组引用，用于菜单控制可见性
        # 改动原因：保存各组引用，以便在菜单中控制可见性，EQ参数相关的组（bands_scroll和param_group）要始终显示
        self.ui_groups = {}

        # 添加设备信息显示组
        device_info_group = QGroupBox("设备信息")
        self.ui_groups['device_info'] = device_info_group  # 保存引用
        device_info_layout = QFormLayout(device_info_group)

        # 设备信息显示标签
        self.device_pid_label = QLabel("PID: 未知")
        self.device_vid_label = QLabel("VID: 未知")
        self.device_product_label = QLabel("产品: 未知")
        self.device_vendor_label = QLabel("厂商: 未知")
        self.device_serial_label = QLabel("序列号: 未知")

        device_info_layout.addRow("产品ID:", self.device_pid_label)
        device_info_layout.addRow("厂商ID:", self.device_vid_label)
        device_info_layout.addRow("产品名称:", self.device_product_label)
        device_info_layout.addRow("厂商名称:", self.device_vendor_label)
        device_info_layout.addRow("序列号:", self.device_serial_label)

        # 获取设备信息按钮
        self.get_device_info_btn = QPushButton("获取设备信息")
        self.get_device_info_btn.clicked.connect(self.on_get_device_info)
        device_info_layout.addRow(self.get_device_info_btn)

        left_content_layout.addWidget(device_info_group)


         # 添加音效模式设置组
        mode_group = QGroupBox("EQ模式")
        self.ui_groups['mode'] = mode_group  # 保存引用
        mode_layout = QFormLayout(mode_group)
        self.mode_label = QLabel("EQ模式:")
        self.mode_combo = QComboBox()
        self.mode_combo.addItems(["预设模式1", "预设模式2", "预设模式3", "预设模式4", "预设模式5",
                                   "预设模式6", "预设模式7", "预设模式8", "预设模式9", "预设模式10",
                                   "用户模式1", "用户模式2", "禁用EQ"])
        self.mode_combo.currentIndexChanged.connect(self.on_mode_changed)
        mode_layout.addRow(self.mode_label, self.mode_combo)

        # 模式名称编辑
        self.mode_name_label = QLabel("模式名称:")
        self.mode_name_edit = QLineEdit()
        self.mode_name_edit.setMaxLength(15)  # 限制16字节，但显示15个字符
        self.mode_name_edit.setPlaceholderText("输入模式名称（最多15个字符）")
        self.mode_name_edit.textChanged.connect(self.on_mode_name_changed)
        mode_layout.addRow(self.mode_name_label, self.mode_name_edit)

        # 切换音效模式按钮（只发送0x8A命令）
        self.switch_mode_btn = QPushButton("切换EQ模式")
        self.switch_mode_btn.clicked.connect(self.on_switch_mode)
        self.switch_mode_btn.hide()
        mode_layout.addRow(self.switch_mode_btn)

        # 获取音效模式按钮（只处理0x8B命令）
        # 改动原因：移除hide()调用，让按钮显示出来，方便用户获取当前模式
        self.get_mode_btn = QPushButton("获取当前EQ模式")
        self.get_mode_btn.clicked.connect(self.on_get_mode)
        mode_layout.addRow(self.get_mode_btn)

        # 设置并保存音效模式按钮（发送0x92命令）
        self.set_and_save_mode_btn = QPushButton("设置并保存EQ模式")
        self.set_and_save_mode_btn.clicked.connect(self.on_set_and_save_mode)
        mode_layout.addRow(self.set_and_save_mode_btn)

        left_content_layout.addWidget(mode_group)

        # 添加音效参数配置组（EQ参数相关，可通过菜单隐藏）
        # 改动原因：将音效参数配置组也添加到ui_groups中，使其可以通过菜单隐藏/显示
        param_group = QGroupBox("EQ参数配置")
        self.ui_groups['param'] = param_group  # 保存引用，用于菜单控制可见性
        param_layout = QVBoxLayout(param_group)

        # 配置音效参数按钮
        self.config_params_btn = QPushButton("配置EQ参数")
        self.config_params_btn.clicked.connect(self.on_config_params)
        param_layout.addWidget(self.config_params_btn)

        # 读取音效参数按钮
        self.read_params_btn = QPushButton("读取EQ参数")
        self.read_params_btn.hide()
        self.read_params_btn.clicked.connect(self.on_read_params)
        param_layout.addWidget(self.read_params_btn)

        # 设置整体增益按钮
        self.set_gain_btn = QPushButton("设置整体增益")
        self.set_gain_btn.clicked.connect(self.on_set_gain)
        param_layout.addWidget(self.set_gain_btn)

        # 复位EQ参数按钮
        self.reset_eq_btn = QPushButton("复位EQ参数")
        self.reset_eq_btn.clicked.connect(self.on_reset_eq_params)
        param_layout.addWidget(self.reset_eq_btn)

        # 获取EQ模式总数按钮
        self.get_mode_count_btn = QPushButton("获取EQ模式总数")
        self.get_mode_count_btn.clicked.connect(self.on_get_mode_count)
        param_layout.addWidget(self.get_mode_count_btn)

        left_content_layout.addWidget(param_group)

        # 添加音量控制组
        volume_group = QGroupBox("音量控制")
        self.ui_groups['volume'] = volume_group  # 保存引用
        volume_layout = QFormLayout(volume_group)

        # 监听音量（DAC/耳机）级别显示和设置
        volume_control_layout = QHBoxLayout()
        self.volume_label = QLabel("监听音量 (0x93/0x94):")
        self.volume_spin = QSpinBox()
        # 改动原因：与固件0x93协议保持一致，DAC监听音量改为49级(0~48)。
        self.volume_spin.setRange(0, 48)
        self.volume_spin.setValue(48)
        self.volume_spin.setSuffix(" (0=静音, 48=0dB)")
        volume_control_layout.addWidget(self.volume_label)
        volume_control_layout.addWidget(self.volume_spin)
        volume_layout.addRow(volume_control_layout)

        # 设置监听音量按钮
        vol_btn_layout = QHBoxLayout()
        self.set_volume_btn = QPushButton("设置监听音量")
        self.set_volume_btn.clicked.connect(self.on_set_volume)
        self.get_volume_btn = QPushButton("读取监听音量")
        self.get_volume_btn.clicked.connect(self.on_get_volume)
        vol_btn_layout.addWidget(self.set_volume_btn)
        vol_btn_layout.addWidget(self.get_volume_btn)
        volume_layout.addRow(vol_btn_layout)

        # 麦克风增益级别显示和设置
        mic_vol_control_layout = QHBoxLayout()
        self.mic_volume_label = QLabel("麦克风增益 (0x82/0x83):")
        self.mic_volume_spin = QSpinBox()
        # 改动原因：与固件0x82协议保持一致，MIC增益改为49级(0~48)，2级=1dB，最大24dB。
        self.mic_volume_spin.setRange(0, 48)
        self.mic_volume_spin.setValue(48)
        self.mic_volume_spin.setSuffix(" (0=静音, 2级=1dB, 48=24dB)")
        mic_vol_control_layout.addWidget(self.mic_volume_label)
        mic_vol_control_layout.addWidget(self.mic_volume_spin)
        volume_layout.addRow(mic_vol_control_layout)

        # 设置/读取麦克风增益按钮
        mic_btn_layout = QHBoxLayout()
        self.set_mic_volume_btn = QPushButton("设置麦克风增益")
        self.set_mic_volume_btn.clicked.connect(self.on_set_mic_volume)
        self.get_mic_volume_btn = QPushButton("读取麦克风增益")
        self.get_mic_volume_btn.clicked.connect(self.on_get_mic_volume)
        mic_btn_layout.addWidget(self.set_mic_volume_btn)
        mic_btn_layout.addWidget(self.get_mic_volume_btn)
        volume_layout.addRow(mic_btn_layout)

        # AI降噪强度控制
        noise_strength_layout = QHBoxLayout()
        self.noise_strength_label = QLabel("AI降噪强度 (0x85/0x86):")
        self.noise_strength_spin = QSpinBox()
        self.noise_strength_spin.setRange(0, 100)
        self.noise_strength_spin.setValue(100)
        self.noise_strength_spin.setSingleStep(2)
        self.noise_strength_spin.setSuffix(" (0=关闭, 2-100=强度, 步进2)")
        noise_strength_layout.addWidget(self.noise_strength_label)
        noise_strength_layout.addWidget(self.noise_strength_spin)
        volume_layout.addRow(noise_strength_layout)

        noise_btn_layout = QHBoxLayout()
        self.set_noise_strength_btn = QPushButton("设置AI降噪强度")
        self.set_noise_strength_btn.clicked.connect(self.on_set_noise_strength)
        self.get_noise_strength_btn = QPushButton("读取AI降噪强度")
        self.get_noise_strength_btn.clicked.connect(self.on_get_noise_strength)
        noise_btn_layout.addWidget(self.set_noise_strength_btn)
        noise_btn_layout.addWidget(self.get_noise_strength_btn)
        volume_layout.addRow(noise_btn_layout)

        # 耳返开关控制
        monitor_switch_layout = QHBoxLayout()
        self.monitor_switch_label = QLabel("耳返开关 (0x87/0x88):")
        self.monitor_switch_combo = QComboBox()
        self.monitor_switch_combo.addItems(["关闭 (0)", "开启 (1)"])
        self.monitor_switch_combo.setCurrentIndex(0)  # 默认关闭
        monitor_switch_layout.addWidget(self.monitor_switch_label)
        monitor_switch_layout.addWidget(self.monitor_switch_combo)
        volume_layout.addRow(monitor_switch_layout)

        monitor_btn_layout = QHBoxLayout()
        self.set_monitor_switch_btn = QPushButton("设置耳返开关")
        self.set_monitor_switch_btn.clicked.connect(self.on_set_monitor_switch)
        self.get_monitor_switch_btn = QPushButton("读取耳返开关")
        self.get_monitor_switch_btn.clicked.connect(self.on_get_monitor_switch)
        monitor_btn_layout.addWidget(self.set_monitor_switch_btn)
        monitor_btn_layout.addWidget(self.get_monitor_switch_btn)
        volume_layout.addRow(monitor_btn_layout)

        # 读取采样率和格式按钮
        self.get_sample_format_btn = QPushButton("读取采样率和格式")
        self.get_sample_format_btn.clicked.connect(self.on_get_sample_format)
        volume_layout.addRow(self.get_sample_format_btn)

        # 采样率状态显示（自动从设备主动上报的0x9F通知更新）
        self.sample_format_status_label = QLabel("当前采样率: 未知")
        volume_layout.addRow("采样率状态(0x9F):", self.sample_format_status_label)

        left_content_layout.addWidget(volume_group)

        # 添加音效模式控制组
        device_mode_group = QGroupBox("音效模式控制")
        self.ui_groups['device_mode'] = device_mode_group  # 保存引用
        device_mode_layout = QFormLayout(device_mode_group)

        # 音效模式
        game_control_layout = QHBoxLayout()
        self.game_mode_combo = QComboBox()
        self.game_mode_combo.addItem("无音效 (0)", 0)
        self.game_mode_combo.addItem("音乐模式 (1)", 1)
        self.game_mode_combo.addItem("游戏模式 (2)", 2)
        self.game_mode_combo.addItem("AI7.1模式 (3)", 3)
        game_control_layout.addWidget(QLabel("音效模式:"))
        game_control_layout.addWidget(self.game_mode_combo)
        device_mode_layout.addRow(game_control_layout)

        game_btn_layout = QHBoxLayout()
        self.set_game_mode_btn = QPushButton("设置音效模式")
        self.set_game_mode_btn.clicked.connect(self.on_set_game_mode)
        self.get_game_mode_btn = QPushButton("读取音效模式")
        self.get_game_mode_btn.clicked.connect(self.on_get_game_mode)
        game_btn_layout.addWidget(self.set_game_mode_btn)
        game_btn_layout.addWidget(self.get_game_mode_btn)
        device_mode_layout.addRow(game_btn_layout)

        # 固件版本按钮
        firmware_btn_layout = QHBoxLayout()
        self.get_firmware_version_btn = QPushButton("获取固件版本")
        self.get_firmware_version_btn.clicked.connect(self.on_get_firmware_version)
        firmware_btn_layout.addWidget(self.get_firmware_version_btn)
        device_mode_layout.addRow(firmware_btn_layout)

        # 恢复出厂默认按钮
        factory_reset_btn_layout = QHBoxLayout()
        self.factory_reset_btn = QPushButton("恢复出厂默认 (0x84)")
        self.factory_reset_btn.clicked.connect(self.on_factory_reset)
        factory_reset_btn_layout.addWidget(self.factory_reset_btn)
        device_mode_layout.addRow(factory_reset_btn_layout)

        left_content_layout.addWidget(device_mode_group)


        # Log控制组（0x70 SET_LOG_SWITCH，0=关闭 1=开启），便于在视图菜单中显示/隐藏
        log_group = QGroupBox("Log控制")
        self.ui_groups['log_switch'] = log_group
        log_layout = QFormLayout(log_group)
        log_control_layout = QHBoxLayout()
        self.log_switch_combo = QComboBox()
        self.log_switch_combo.addItem("关闭log (0)", 0)
        self.log_switch_combo.addItem("开启log (1)", 1)
        log_control_layout.addWidget(QLabel("Log打印:"))
        log_control_layout.addWidget(self.log_switch_combo)
        log_layout.addRow(log_control_layout)
        self.set_log_switch_btn = QPushButton("设置log开关")
        self.set_log_switch_btn.clicked.connect(self.on_set_log_switch)
        log_layout.addRow(self.set_log_switch_btn)
        left_content_layout.addWidget(log_group)
        # 添加EQ使能控制组
        eq_enable_group = QGroupBox("EQ使能控制")
        self.ui_groups['eq_enable'] = eq_enable_group  # 保存引用
        eq_enable_layout = QFormLayout(eq_enable_group)

        # EQ使能开关
        eq_enable_switch_layout = QHBoxLayout()
        self.eq_enable_label = QLabel("EQ使能开关:")
        self.eq_enable_combo = QComboBox()
        self.eq_enable_combo.addItems(["OFF (禁用)", "ON (启用)"])
        self.eq_enable_combo.setCurrentIndex(1)  # 默认启用
        eq_enable_switch_layout.addWidget(self.eq_enable_label)
        eq_enable_switch_layout.addWidget(self.eq_enable_combo)
        eq_enable_layout.addRow(eq_enable_switch_layout)

        # 保存的模式显示
        self.saved_mode_label = QLabel("保存的模式: 未获取")
        eq_enable_layout.addRow("保存的模式:", self.saved_mode_label)

        # EQ使能控制按钮
        eq_enable_buttons_layout = QVBoxLayout()

        # 设置EQ使能开关按钮
        self.set_eq_enable_btn = QPushButton("设置EQ使能开关")
        self.set_eq_enable_btn.clicked.connect(self.on_set_eq_enable)
        eq_enable_buttons_layout.addWidget(self.set_eq_enable_btn)

        # 获取EQ使能开关按钮
        self.get_eq_enable_btn = QPushButton("获取EQ使能开关")
        self.get_eq_enable_btn.clicked.connect(self.on_get_eq_enable)
        eq_enable_buttons_layout.addWidget(self.get_eq_enable_btn)

        eq_enable_layout.addRow(eq_enable_buttons_layout)

        left_content_layout.addWidget(eq_enable_group)



        # 添加固件升级控制组
        # 改动原因：添加HID固件升级功能，支持通过HID协议升级设备固件
        firmware_upgrade_group = QGroupBox("固件升级")
        self.ui_groups['firmware'] = firmware_upgrade_group  # 保存引用
        firmware_upgrade_layout = QFormLayout(firmware_upgrade_group)

        # 固件文件选择
        firmware_file_layout = QHBoxLayout()
        self.firmware_file_edit = QLineEdit()
        self.firmware_file_edit.setPlaceholderText("选择固件文件...")
        firmware_file_layout.addWidget(self.firmware_file_edit)
        browse_btn = QPushButton("浏览")
        browse_btn.clicked.connect(self.on_browse_firmware)
        firmware_file_layout.addWidget(browse_btn)
        firmware_upgrade_layout.addRow("固件文件:", firmware_file_layout)

        # 升级进度条
        self.firmware_progress = QProgressBar()
        self.firmware_progress.setRange(0, 100)
        self.firmware_progress.setValue(0)
        firmware_upgrade_layout.addRow("升级进度:", self.firmware_progress)

        # 状态标签
        self.firmware_status_label = QLabel("状态: 空闲")
        firmware_upgrade_layout.addRow("当前状态:", self.firmware_status_label)

        # 固件升级按钮
        firmware_buttons_layout = QVBoxLayout()

        # 擦除upgrade image按钮
        self.erase_upgrade_btn = QPushButton("擦除现有升级镜像")
        self.erase_upgrade_btn.clicked.connect(self.on_erase_upgrade_image)
        firmware_buttons_layout.addWidget(self.erase_upgrade_btn)

        # 开始升级按钮
        self.start_upgrade_btn = QPushButton("开始固件升级")
        self.start_upgrade_btn.clicked.connect(self.on_start_firmware_upgrade)
        firmware_buttons_layout.addWidget(self.start_upgrade_btn)

        # 查询升级状态按钮
        self.status_upgrade_btn = QPushButton("查询升级状态")
        self.status_upgrade_btn.clicked.connect(self.on_get_firmware_upgrade_status)
        firmware_buttons_layout.addWidget(self.status_upgrade_btn)

        # 中止升级按钮
        self.abort_upgrade_btn = QPushButton("中止固件升级")
        self.abort_upgrade_btn.clicked.connect(self.on_abort_firmware_upgrade)
        firmware_buttons_layout.addWidget(self.abort_upgrade_btn)

        # 验证镜像按钮

        # 重启设备按钮
        self.reboot_device_btn = QPushButton("重启设备")
        self.reboot_device_btn.clicked.connect(self.on_reboot_device)
        firmware_buttons_layout.addWidget(self.reboot_device_btn)

        firmware_upgrade_layout.addRow(firmware_buttons_layout)

        left_content_layout.addWidget(firmware_upgrade_group)

        ex3d_group = QGroupBox("EX3D控制")
        self.ui_groups['ex3d'] = ex3d_group  # 保存引用
        ex3d_layout = QVBoxLayout(ex3d_group)

        # EX3D命令码选择
        ex3d_cmd_layout = QHBoxLayout()
        ex3d_cmd_layout.addWidget(QLabel("EX3D命令码:"))
        self.ex3d_cmd_combo = QComboBox()
        # 添加SET命令选项（清除第9位）
        self.ex3d_cmd_combo.addItem("CMD_OPEN (0x81)", 0x81)
        self.ex3d_cmd_combo.addItem("CMD_ONOFF (0x82)", 0x82)
        self.ex3d_cmd_combo.addItem("CMD_ANGLE (0x83)", 0x83)
        self.ex3d_cmd_combo.addItem("CMD_SOUND_FIELD (0x84)", 0x84)
        self.ex3d_cmd_combo.addItem("CMD_MUTE (0x85)", 0x85)
        self.ex3d_cmd_combo.addItem("CMD_ON_GAIN (0x86)", 0x86)
        self.ex3d_cmd_combo.addItem("CMD_LMT_THRESHOLD (0x87)", 0x87)
        self.ex3d_cmd_combo.addItem("CMD_UPMIX (0x88)", 0x88)
        self.ex3d_cmd_combo.addItem("CMD_LFE (0x89)", 0x89)
        self.ex3d_cmd_combo.addItem("CMD_OFF_GAIN (0x8A)", 0x8A)
        self.ex3d_cmd_combo.addItem("CMD_LMT_ATTACKK_TIME (0x8E)", 0x8E)
        self.ex3d_cmd_combo.addItem("CMD_LMT_RELEASE_TIME (0x8F)", 0x8F)
        self.ex3d_cmd_combo.addItem("CMD_EXPAND_GAIN (0x93)", 0x93)
        self.ex3d_cmd_combo.addItem("CMD_REDUCE_GAIN (0x94)", 0x94)
        ex3d_cmd_layout.addWidget(self.ex3d_cmd_combo)
        ex3d_layout.addLayout(ex3d_cmd_layout)

        # EX3D命令类型选择（SET或GET）
        ex3d_type_layout = QHBoxLayout()
        ex3d_type_layout.addWidget(QLabel("命令类型:"))
        self.ex3d_cmd_type_combo = QComboBox()
        self.ex3d_cmd_type_combo.addItem("SET (设置)", "SET")
        self.ex3d_cmd_type_combo.addItem("GET (读取)", "GET")
        ex3d_type_layout.addWidget(self.ex3d_cmd_type_combo)
        ex3d_layout.addLayout(ex3d_type_layout)

        # EX3D参数输入（最多3个参数）
        ex3d_params_layout = QFormLayout()
        self.ex3d_param1 = QSpinBox()
        self.ex3d_param1.setRange(-2147483648, 2147483647)
        self.ex3d_param1.setValue(0)
        ex3d_params_layout.addRow("参数1 (int32):", self.ex3d_param1)
        self.ex3d_param2 = QSpinBox()
        self.ex3d_param2.setRange(-2147483648, 2147483647)
        self.ex3d_param2.setValue(0)
        ex3d_params_layout.addRow("参数2 (int32):", self.ex3d_param2)
        self.ex3d_param3 = QSpinBox()
        self.ex3d_param3.setRange(-2147483648, 2147483647)
        self.ex3d_param3.setValue(0)
        ex3d_params_layout.addRow("参数3 (int32):", self.ex3d_param3)
        ex3d_layout.addLayout(ex3d_params_layout)

        # EX3D命令按钮
        ex3d_btn_layout = QHBoxLayout()
        self.send_ex3d_btn = QPushButton("发送EX3D命令")
        self.send_ex3d_btn.clicked.connect(self.on_send_ex3d_cmd)
        self.get_ex3d_btn = QPushButton("读取EX3D参数")
        self.get_ex3d_btn.clicked.connect(self.on_get_ex3d_cmd)
        ex3d_btn_layout.addWidget(self.send_ex3d_btn)
        ex3d_btn_layout.addWidget(self.get_ex3d_btn)
        ex3d_layout.addLayout(ex3d_btn_layout)

        left_content_layout.addWidget(ex3d_group)

        # EQ后增益补偿
        post_gain_layout = QHBoxLayout()
        post_gain_label = QLabel("EQ整体增益 (dB):")
        self.post_gain_spin = QSpinBox()
        self.post_gain_spin.setRange(-50, 0)
        self.post_gain_spin.setSingleStep(1)
        self.post_gain_spin.setValue(0)
        post_gain_layout.addWidget(post_gain_label)
        post_gain_layout.addWidget(self.post_gain_spin)
        left_content_layout.addLayout(post_gain_layout)

        # 保存/加载按钮（始终显示）
        save_load_layout = QHBoxLayout()
        save_button = QPushButton("保存参数")
        save_button.clicked.connect(self.save_parameters)
        load_button = QPushButton("加载参数")
        load_button.clicked.connect(self.load_parameters)
        apply_all_button = QPushButton("保存到所有采样率")
        apply_all_button.clicked.connect(self.apply_to_all_sample_rates)
        save_load_layout.addWidget(save_button)
        save_load_layout.addWidget(load_button)
        save_load_layout.addWidget(apply_all_button)
        left_content_layout.addLayout(save_load_layout)

        # 添加弹性空间，让内容靠上对齐
        left_content_layout.addStretch()

        # 将内容容器放入滚动区域
        left_scroll.setWidget(left_content)
        left_layout.addWidget(left_scroll)

        # 右侧响应曲线
        right_panel = QWidget()
        right_layout = QVBoxLayout(right_panel)

        # 创建matplotlib画布
        self.plot = MplCanvas(self, width=5, height=4, dpi=100)
        right_layout.addWidget(self.plot)

        # 添加左右面板到分割器
        splitter.addWidget(left_panel)
        splitter.addWidget(right_panel)

        # 设置分割器初始比例
        splitter.setStretchFactor(0, 1)
        splitter.setStretchFactor(1, 2)

        # 添加分割器到主布局
        main_layout.addWidget(splitter)

        # 初始化变量
        self.bands = []
        self.visible_bands = 0

        # 创建初始滤波器
        self.create_initial_filter()
        self.refresh_devices()

        # 改动原因：默认隐藏所有视图选项，只显示EQ参数相关的组（bands_scroll，即滤波器带）
        # 隐藏所有在ui_groups中的组，包括音效参数配置组
        for group_key in self.ui_groups.keys():
            self.ui_groups[group_key].setVisible(False)


    def create_menu_bar(self):
        """创建菜单栏和视图菜单，用于控制各组可见性"""
        # 改动原因：添加菜单栏，包含视图菜单用于隐藏/显示左侧面板的各组
        # 改动原因：默认隐藏所有视图选项，只显示EQ参数相关的组（bands_scroll，即滤波器带）
        menubar = self.menuBar()

        # 创建视图菜单
        view_menu = menubar.addMenu("视图")

        # 定义各组的中文名称和对应的key
        # 改动原因：添加'param'（音效参数配置）到菜单控制列表中
        group_names = {
            'device_info': '设备信息',
            'mode': 'EQ模式',
            'param': 'EQ参数配置',
            'volume': '音量控制',
            'device_mode': '音效模式控制',
            'log_switch': 'Log控制',
            'eq_enable': 'EQ使能控制',
            'ex3d': 'EX3D控制',
            'firmware': '固件升级'
        }

        # 为每组创建切换可见性的动作
        self.view_actions = {}
        for key, name in group_names.items():
            action = view_menu.addAction(f"显示 {name}")
            action.setCheckable(True)
            action.setChecked(False)  # 改动原因：默认隐藏所有视图选项，只显示EQ参数相关的组
            action.triggered.connect(lambda checked, k=key: self.toggle_group_visibility(k, checked))
            self.view_actions[key] = action

    def toggle_group_visibility(self, group_key, visible):
        """切换组的可见性"""
        # 改动原因：切换指定组的可见性，现在所有组都可以通过菜单控制（包括音效参数配置）
        # 只有bands_scroll（滤波器带）始终显示，不受此函数控制
        if group_key in self.ui_groups:
            self.ui_groups[group_key].setVisible(visible)
            # 更新菜单项的文本
            if group_key in self.view_actions:
                group_names = {
                    'device_info': '设备信息',
                    'mode': 'EQ模式',
                    'param': 'EQ参数配置',
                    'volume': '音量控制',
                    'device_mode': '音效模式控制',
                    'log_switch': 'Log控制',
                    'eq_enable': 'EQ使能控制',
                    'ex3d': 'EX3D控制',
                    'firmware': '固件升级'
                }
                name = group_names.get(group_key, group_key)
                self.view_actions[group_key].setText(f"{'显示' if visible else '隐藏'} {name}")

    def create_initial_filter(self):
        """创建所有MAX_EQ_BANDS个滤波器"""
        for i in range(MAX_EQ_BANDS):
            band = BandFilter(i + 1, self)
            self.bands.append(band)
            self.bands_layout.addWidget(band)
        self.visible_bands = MAX_EQ_BANDS

    def on_sample_rate_changed(self):
        """采样率改变时更新所有滤波器"""
        for band in self.bands:
            if self.show_qshift_debug:
                band.update_coefficients()
        self.update_eq_curve()

    def on_log_level_changed(self, index):
        """当日志级别改变时的处理"""
        self.log_level = index
        log_message(LOG_LEVEL_INFO, f"日志级别已更改为: {LOG_LEVEL_NAMES[index]}", self.log_level)

    def on_mode_changed(self, index):
        """音效模式改变时的处理"""
        # 防止循环调用：如果是由代码内部设置触发的，不执行操作
        if hasattr(self, '_mode_changing') and self._mode_changing:
            return

        if index == NUM_EQ_MODES:  # 禁用EQ选项
            self.current_mode = EQ_DISABLED_MODE
        else:
            self.current_mode = index
        log_message(LOG_LEVEL_INFO, f"音效模式已更改为: {index}", self.log_level)

        # 切换模式
        self.on_switch_mode()

        # 等待设备处理模式切换（添加延迟确保设备完成切换）
        import time
        time.sleep(0.1)  # 等待100ms让设备完成模式切换

        # 读取参数
        self.read_parameters()

        # 获取当前模式状态（但不更新UI下拉框，避免循环调用）
        self.on_get_mode(update_ui=False)

    def on_switch_mode(self):
        """切换音效模式（只发送0x8A命令）"""
        if self.device_combo.currentData() is not None:
            device_info = self.device_combo.currentData()
            log_message(LOG_LEVEL_DEBUG, f"正在打开设备: VID=0x{device_info['vendor_id']:04x}, PID=0x{device_info['product_id']:04x}", self.log_level)

            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x8A  # 命令码 (SET_EQ_MODE)
            data[2] = self.current_mode  # 模式值
            # 其余字节保持为0
            log_message(LOG_LEVEL_DEBUG, f"切换音效模式到模式 {self.current_mode}", self.log_level)
            log_message(LOG_LEVEL_DEBUG, f"数据包: {' '.join(f'0x{byte:02x}' for byte in data[:20])}...", self.log_level)

            # 发送命令
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)
            h.write([0x01] + list(data))  # 0x01是report_id

            # 等待设备响应（可选：读取响应确认切换成功）
            import time
            time.sleep(0.05)  # 等待50ms让设备处理命令

            h.close()
            log_message(LOG_LEVEL_INFO, f"切换音效模式到模式 {self.current_mode} 成功", self.log_level)

    def on_get_mode(self, update_ui=True, mode=0xFF):
        """获取音效模式（只处理0x8B命令）
        Args:
            update_ui: 是否更新UI下拉框，False时只更新增益和名称，避免循环调用
            mode: 模式值，0xFF表示获取当前模式信息，0-9表示获取指定模式信息
        """
        if self.device_combo.currentData() is not None:
            device_info = self.device_combo.currentData()
            log_message(LOG_LEVEL_DEBUG, f"正在打开设备: VID=0x{device_info['vendor_id']:04x}, PID=0x{device_info['product_id']:04x}", self.log_level)

            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x8B  # 命令码 (GET_EQ_MODE)
            data[2] = mode  # 改动原因：添加mode参数，0xFF表示获取当前模式，0-9表示获取指定模式信息
            # 其余字节保持为0

            log_message(LOG_LEVEL_DEBUG, f"发送0x8B命令，mode=0x{mode:02x} ({'当前模式' if mode == 0xFF else f'模式{mode}'})", self.log_level)
            h.write([0x01] + list(data))  # 0x01是report_id

            # 读取0x8B的响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x8B:
                    # 调试：打印原始响应数据
                    log_message(LOG_LEVEL_DEBUG, f"0x8B响应原始数据: {' '.join(f'0x{byte:02x}' for byte in reply[:16])}", self.log_level)

                    # 解析响应数据 - 根据简化协议
                    current_mode = reply[3]  # 模式值
                    # 解析整体增益 (4字节，位置4-7)
                    post_gain = int.from_bytes(reply[4:8], 'little', signed=True)

                    # 解析模式名称 (16字节，位置8-23)
                    mode_name_bytes = bytes(reply[8:24])
                    mode_name = mode_name_bytes.decode('utf-8', errors='ignore').rstrip('\x00')

                    # 调试：打印解析的字节
                    log_message(LOG_LEVEL_DEBUG, f"整体增益原始字节: 0x{reply[4]:02x} 0x{reply[5]:02x} 0x{reply[6]:02x} 0x{reply[7]:02x}", self.log_level)
                    log_message(LOG_LEVEL_DEBUG, f"模式名称原始字节: {' '.join(f'0x{byte:02x}' for byte in reply[8:24])}", self.log_level)

                    log_message(LOG_LEVEL_INFO, f"获取到当前EQ模式: {current_mode}, 整体增益: {post_gain}dB, 模式名称: '{mode_name}'", self.log_level)

                    # 更新UI - 图像显示固定使用48000Hz
                    self.sample_rate.setCurrentText("48000")

                    # 只有在update_ui=True时才更新下拉框，避免循环调用
                    if update_ui:
                        # 设置标志防止循环调用
                        self._mode_changing = True
                        try:
                            if current_mode == EQ_DISABLED_MODE:
                                self.mode_combo.setCurrentIndex(NUM_EQ_MODES)  # 禁用EQ选项
                            else:
                                self.mode_combo.setCurrentIndex(current_mode)
                        finally:
                            self._mode_changing = False

                    # 更新当前模式值（但不触发UI更新）
                    self.current_mode = current_mode
                    # 更新整体增益显示
                    self.post_gain_spin.setValue(post_gain)
                    # 更新模式名称显示
                    self.mode_name_edit.setText(mode_name)
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x8B响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x8B响应数据", self.log_level)

            h.close()


    def update_eq_curve(self):
        """更新EQ响应曲线"""
        try:
            fs = float(self.sample_rate.currentText())

            # 创建频率点
            f = np.logspace(np.log10(20), np.log10(fs/2), 1000)

            # 计算总响应
            h_total = np.ones_like(f, dtype=complex)

            # 计算每个EQ的响应
            responses = []
            enabled_eqs = []  # 记录启用的EQ索引

            for i, band in enumerate(self.bands):
                if band.enabled.isChecked():
                    # 获取滤波器系数
                    coeffs = band.calculate_coefficients(fs)
                    if coeffs is not None:
                        # 计算频率响应
                        w = 2 * np.pi * f / fs
                        z = np.exp(-1j * w)
                        h = (coeffs[0] + coeffs[1] * z + coeffs[2] * z**2) / \
                            (1 + coeffs[3] * z + coeffs[4] * z**2)
                        responses.append(h)
                        enabled_eqs.append(i)
                        h_total *= h

            # 更新图形
            self.plot.update_plot(f, h_total, fs)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"更新EQ曲线时出错: {str(e)}", self.log_level)

    def save_parameters(self):
        """保存参数到JSON文件和头文件"""
        try:
            fs = float(self.sample_rate.currentText())
            current_mode = self.current_mode

            # 生成JSON文件名
            json_filename = f"eq_parameters_{int(fs)}_mode{current_mode}.json"

            # 读取现有的JSON文件（如果存在）
            all_params = {}
            if os.path.exists(json_filename):
                with open(json_filename, "r", encoding='utf-8') as f:
                    all_params = json.load(f)

            # 计算总的bshift
            total_bshift = sum(band.get_bshift() for band in self.bands)

            # 更新当前mode的参数
            params = {
                "sample_rate": fs,
                "mode": current_mode,  # 保存当前模式
                "total_bshift": total_bshift,
                "post_gain_db": int(self.post_gain_spin.value()),
                "bands": []
            }

            for band in self.bands:
                band_params = band.get_parameters()
                if band_params["enabled"]:
                    # 计算浮点系数
                    coeffs = band.calculate_coefficients(fs)
                    if coeffs is not None:
                        # 反转 b1 和 b2 的符号
                        coeffs = np.array([
                            coeffs[0],  # a0
                            coeffs[1],  # a1
                            coeffs[2],  # a2
                            -coeffs[3], # b1
                            -coeffs[4]  # b2
                        ])
                        band_scale_factor = band.get_scale_factor()
                        # 只对 a0, a1, a2 乘以 scale_factor
                        coeffs_scaled = np.array([
                            coeffs[0] * band_scale_factor,
                            coeffs[1] * band_scale_factor,
                            coeffs[2] * band_scale_factor,
                            coeffs[3],
                            coeffs[4]
                        ])
                        coeffs_q30 = (coeffs_scaled * 2**30).astype(np.int32)
                        band_params["coefficients"] = {
                            "float": coeffs.tolist(),
                            "q30": [f"0x{int(x):08x}" if x >= 0 else f"-0x{int(-x):08x}" for x in coeffs_q30]
                        }

                params["bands"].append(band_params)

            # 保存当前mode的参数
            all_params[f"mode_{current_mode}"] = params

            # 保存JSON文件
            with open(json_filename, "w", encoding='utf-8') as f:
                json.dump(all_params, f, indent=2, ensure_ascii=False)

            # 为当前模式生成头文件
            header_filename = f"eq_params_{int(fs)}_mode{current_mode}.h"
            with open(header_filename, "w", encoding='utf-8') as f:
                f.write(f"""// EQ参数头文件 - {int(fs)}Hz
// 自动生成，请勿手动修改

#include "eq.h"

// Directly declare sEQ_data_{int(fs)}HZ, including coefficients
eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{
""")

                # 写入当前模式的EQ参数
                f.write(f"    [{current_mode}][0] = {{\n")  # 左通道
                f.write(f"        .sample_rate = {int(fs)},\n")
                f.write(f"        .total_bshift = {total_bshift},\n")
                f.write(f"        .post_gain_db = {int(self.post_gain_spin.value())},  //  {self.post_gain_spin.value()} dB\n")
                f.write("        .bands = {\n")

                # 写入每个滤波器的参数
                for i, band in enumerate(self.bands):
                    params = band.get_parameters()
                    if i < 8:  # 最多8个滤波器
                        f.write("            {\n")
                        f.write(f"                .index = {i},\n")

                        # 写入滤波器类型
                        type_map = {
                            "bypass滤波器": "FILTER_TYPE_BYPASS",
                            "全通滤波器": "FILTER_TYPE_ALLPASS",
                            "峰值滤波器": "FILTER_TYPE_PEAKING",
                            "低通滤波器": "FILTER_TYPE_LOWPASS",
                            "高通滤波器": "FILTER_TYPE_HIGHPASS",
                            "带通滤波器": "FILTER_TYPE_BANDPASS",
                            "带阻滤波器": "FILTER_TYPE_BANDSTOP",
                            "陷波滤波器": "FILTER_TYPE_NOTCH",
                            "恒定Q值滤波器": "FILTER_TYPE_CONST_Q",
                            "低架滤波器": "FILTER_TYPE_LOWSHELF",
                            "高架滤波器": "FILTER_TYPE_HIGHSHELF"
                        }
                        f.write(f"                .type = {type_map.get(params['type'], 'FILTER_TYPE_ALLPASS')},\n")

                        # 写入参数 - 直接写入float值
                        f.write(f"                .fc = {params['fc']:.2f}f,\n")
                        f.write(f"                .q = {params['Q']:.4f}f,\n")
                        f.write(f"                .bw = {params['bw']:.2f}f,\n")
                        f.write(f"                .gain = {params['gain']:.2f}f,\n")
                        f.write("            },\n")

                f.write("        },\n")

                # 写入filter结构
                f.write("        .filter = {\n")
                f.write("            .biquad_count = MAX_EQ_BANDS,\n")
                f.write("            .state = {{0}},\n")
                f.write("            .coef = {\n")

                # 写入系数数组
                coeffs_all = []
                for band in self.bands:
                    coeffs = band.calculate_coefficients(fs)
                    if coeffs is not None:
                        coeffs = np.array([
                            coeffs[0],  # a0
                            coeffs[1],  # a1
                            coeffs[2],  # a2
                            -coeffs[3], # b1
                            -coeffs[4]  # b2
                        ])
                        band_scale_factor = band.get_scale_factor()
                        coeffs_scaled = np.array([
                            coeffs[0] * band_scale_factor,
                            coeffs[1] * band_scale_factor,
                            coeffs[2] * band_scale_factor,
                            coeffs[3],
                            coeffs[4]
                        ])
                        coeffs_q30 = (coeffs_scaled * 2**30).astype(np.int32)
                        coeffs_all.extend(coeffs_q30)
                    else:
                        coeffs_all.extend([0] * 5)

                # 补足MAX_EQ_BANDS个band的系数
                while len(coeffs_all) < MAX_EQ_BANDS * 5:
                    coeffs_all.append(0)

                # 按5个系数一组写入
                for i in range(5):
                    f.write("                {")
                    for j in range(MAX_EQ_BANDS):
                        coeff = coeffs_all[j*5 + i]  # 注意：i为系数序号，j为band序号
                        f.write(f"{f'0x{coeff:08x}' if coeff >= 0 else f'-0x{-coeff:08x}'}")
                        if j < MAX_EQ_BANDS - 1:
                            f.write(", ")
                    f.write("},\n")

                f.write("            },\n")
                f.write("        }\n")
                f.write("    },\n")

                # 右通道与左通道相同
                f.write(f"    [{current_mode}][1] = {{\n")  # 右通道
                f.write(f"        .sample_rate = {int(fs)},\n")
                f.write(f"        .total_bshift = {total_bshift},\n")
                f.write(f"        .post_gain_db = {int(self.post_gain_spin.value())},  //  {self.post_gain_spin.value()} dB\n")
                f.write("        .bands = {\n")

                # 写入每个滤波器的参数
                for i, band in enumerate(self.bands):
                    params = band.get_parameters()
                    if i < MAX_EQ_BANDS:  # 最多MAX_EQ_BANDS个滤波器
                        f.write("            {\n")
                        f.write(f"                .index = {i},\n")
                        f.write(f"                .type = {type_map.get(params['type'], 'FILTER_TYPE_ALLPASS')},\n")
                        f.write(f"                .fc = {params['fc']:.2f}f,\n")
                        f.write(f"                .q = {params['Q']:.4f}f,\n")
                        f.write(f"                .bw = {params['bw']:.2f}f,\n")
                        f.write(f"                .gain = {params['gain']:.2f}f,\n")
                        f.write("            },\n")

                f.write("        },\n")
                f.write("        .filter = {\n")
                f.write("            .biquad_count = MAX_EQ_BANDS,\n")
                f.write("            .state = {{0}},\n")
                f.write("            .coef = {\n")

                # 写入系数数组
                for i in range(5):
                    f.write("                {")
                    for j in range(MAX_EQ_BANDS):
                        coeff = coeffs_all[j*5 + i]  # 注意：i为系数序号，j为band序号
                        f.write(f"{f'0x{coeff:08x}' if coeff >= 0 else f'-0x{-coeff:08x}'}")
                        if j < MAX_EQ_BANDS - 1:
                            f.write(", ")
                    f.write("},\n")

                f.write("            },\n")
                f.write("        }\n")
                f.write("    },\n")

                f.write("};\n")

            log_message(LOG_LEVEL_INFO, f"参数已保存到 {json_filename}", self.log_level)
            log_message(LOG_LEVEL_INFO, f"头文件已生成: {header_filename}", self.log_level)

            # 检查是否所有模式都已生成
            all_modes_generated = True
            for mode in range(NUM_EQ_MODES):
                mode_file = f"eq_params_{int(fs)}_mode{mode}.h"
                if not os.path.exists(mode_file):
                    all_modes_generated = False
                    break

            # 如果所有模式都已生成，合并成一个完整的头文件
            if all_modes_generated:
                self.merge_mode_files(fs)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"保存参数时出错: {str(e)}", self.log_level)

    def merge_mode_files(self, fs):
        """合并所有模式的头文件"""
        try:
            # 创建完整的头文件
            header_filename = f"eq_params_{int(fs)}.h"
            with open(header_filename, "w", encoding='utf-8') as f:
                # 写入头文件头部
                f.write(f"""// EQ参数头文件 - {int(fs)}Hz
// 自动生成，请勿手动修改

#include "eq.h"

// Directly declare sEQ_data_{int(fs)}HZ, including coefficients
eq_mode_data_t sEQ_data_{int(fs)}HZ[NUM_EQ_MODES][NUM_EQ_CHANS] = {{
""")

                # 合并所有模式的文件
                for mode in range(NUM_EQ_MODES):
                    mode_file = f"eq_params_{int(fs)}_mode{mode}.h"
                    with open(mode_file, "r", encoding='utf-8') as mf:
                        content = mf.readlines()
                        # 截取第8行到第171行
                        mode_data = content[7:171]
                        # 写入模式数据
                        f.writelines(mode_data)

                # 写入结尾
                f.write("};\n")

            log_message(LOG_LEVEL_INFO, f"已合并所有模式生成完整头文件: {header_filename}", self.log_level)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"合并头文件时出错: {str(e)}", self.log_level)

    def load_parameters(self):
        """从JSON文件加载参数"""
        try:
            fs = float(self.sample_rate.currentText())
            current_mode = self.current_mode
            json_filename = f"eq_parameters_{int(fs)}_mode{current_mode}.json"

            if not os.path.exists(json_filename):
                log_message(LOG_LEVEL_ERROR, f"未找到配置文件: {json_filename}", self.log_level)
                return

            with open(json_filename, "r", encoding='utf-8') as f:
                all_params = json.load(f)

            # 获取当前mode的参数
            mode_key = f"mode_{current_mode}"
            if mode_key not in all_params:
                log_message(LOG_LEVEL_ERROR, f"未找到模式 {current_mode} 的参数", self.log_level)
                return

            params = all_params[mode_key]

            # 清除现有滤波器
            for band in self.bands:
                self.bands_layout.removeWidget(band)
                band.deleteLater()
            self.bands.clear()

            # 添加新的滤波器
            for band_params in params["bands"]:
                band = BandFilter(len(self.bands), self)
                band.set_parameters(band_params)
                self.bands.append(band)
                self.bands_layout.addWidget(band)

            self.visible_bands = len(self.bands)
            self.update_eq_curve()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"加载参数时出错: {str(e)}", self.log_level)

    def send_parameters(self):
        """发送参数到设备"""
        try:
            if not self.device_combo.currentData():
                log_message(LOG_LEVEL_ERROR, "未选择设备", self.log_level)
                return

            # 计算总的bshift
            total_bshift = sum(band.get_bshift() for band in self.bands)

            device_info = self.device_combo.currentData()
            log_message(LOG_LEVEL_DEBUG, f"正在打开设备: VID=0x{device_info['vendor_id']:04x}, PID=0x{device_info['product_id']:04x}", self.log_level)

            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])

            # 设置非阻塞模式
            h.set_nonblocking(1)

            # 发送所有MAX_EQ_BANDS个滤波器的参数 - 自动应用到所有采样率
            for i in range(MAX_EQ_BANDS):
                # 确保有足够的滤波器对象
                while len(self.bands) <= i:
                    self.add_filter()

                band = self.bands[i]
                params = band.get_parameters()
                log_message(LOG_LEVEL_DEBUG, f"正在处理滤波器 {i+1} 的参数:", self.log_level)
                log_message(LOG_LEVEL_DEBUG, f"  类型: {params['type']}", self.log_level)
                log_message(LOG_LEVEL_DEBUG, f"  频率: {params['fc']} Hz", self.log_level)
                log_message(LOG_LEVEL_DEBUG, f"  Q值: {params['Q']}", self.log_level)
                log_message(LOG_LEVEL_DEBUG, f"  带宽: {params['bw']}", self.log_level)
                log_message(LOG_LEVEL_DEBUG, f"  增益: {params['gain']} dB", self.log_level)

                # 构建HID数据包 (根据简化协议格式)
                data = bytearray(63)  # 63字节数据包

                # 头部 (2字节)
                data[0] = 0x77  # 同步头1
                data[1] = 0x8D  # 命令码 (SET_EQ_PARAMS)

                # 模式值 (1字节)
                mode = self.current_mode
                data[2] = mode
                log_message(LOG_LEVEL_VERBOSE, f"  模式值: {mode}", self.log_level)

                # 滤波器索引 (1字节)
                data[3] = i
                log_message(LOG_LEVEL_VERBOSE, f"  滤波器索引: {i}", self.log_level)

                # 滤波器类型 (1字节)
                type_map = {
                    "bypass滤波器": 0x00,
                    "全通滤波器": 0x01,
                    "峰值滤波器": 0x02,
                    "低通滤波器": 0x03,
                    "高通滤波器": 0x04,
                    "带通滤波器": 0x05,
                    "带阻滤波器": 0x06,
                    "陷波滤波器": 0x07,
                    "恒定Q值滤波器": 0x08,
                    "低架滤波器": 0x09,
                    "高架滤波器": 0x0A
                }
                data[4] = type_map.get(params["type"], 0x01)
                log_message(LOG_LEVEL_VERBOSE, f"  滤波器类型码: 0x{data[4]:02x}", self.log_level)

                # 参数直接作为float发送
                # fc (4字节) - 位置5-8
                fc = float(params["fc"])
                data[5:9] = struct.pack('<f', fc)
                log_message(LOG_LEVEL_VERBOSE, f"  fc: {fc} Hz", self.log_level)
                log_message(LOG_LEVEL_VERBOSE, f"  fc bytes: {data[5:9].hex()}", self.log_level)

                # Q值 (4字节) - 位置9-12
                q = float(params["Q"])
                data[9:13] = struct.pack('<f', q)
                log_message(LOG_LEVEL_VERBOSE, f"  Q值: {q}", self.log_level)
                log_message(LOG_LEVEL_VERBOSE, f"  Q bytes: {data[9:13].hex()}", self.log_level)

                # 带宽 (4字节) - 位置13-16
                bw = float(params["bw"])
                data[13:17] = struct.pack('<f', bw)
                log_message(LOG_LEVEL_VERBOSE, f"  带宽: {bw} Hz", self.log_level)
                log_message(LOG_LEVEL_VERBOSE, f"  bw bytes: {data[13:17].hex()}", self.log_level)

                # 增益 (4字节) - 位置17-20
                gain = float(params["gain"])
                data[17:21] = struct.pack('<f', gain)
                log_message(LOG_LEVEL_VERBOSE, f"  增益: {gain} dB", self.log_level)
                log_message(LOG_LEVEL_VERBOSE, f"  gain bytes: {data[17:21].hex()}", self.log_level)

                # 打印完整数据包 - 一个字节一个字节打印
                log_message(LOG_LEVEL_VERBOSE, "  完整数据包:", self.log_level)
                for i in range(len(data)):
                    log_message(LOG_LEVEL_VERBOSE, f"    {i:02d}: {data[i]:02x}", self.log_level)

                # 发送数据包
                log_message(LOG_LEVEL_DEBUG, "  正在发送数据包...", self.log_level)
                h.write([0x01] + list(data))  # 0x01是report_id
                log_message(LOG_LEVEL_DEBUG, "  数据包发送完成", self.log_level)

            h.close()
            log_message(LOG_LEVEL_INFO, "所有参数发送完成", self.log_level)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"发送参数时出错: {str(e)}", self.log_level)
            import traceback
            log_message(LOG_LEVEL_ERROR, "详细错误信息:", self.log_level)
            traceback.print_exc()

    def read_parameters(self):
        """从设备读取参数"""
        try:
            if not self.device_combo.currentData():
                log_message(LOG_LEVEL_ERROR, "未选择设备", self.log_level)
                return

            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])

            # 设置非阻塞模式
            h.set_nonblocking(1)

            # 图像显示固定使用48000Hz
            fs = 48000
            log_message(LOG_LEVEL_DEBUG, f"  图像显示采样率: {fs} Hz", self.log_level)

            mode = self.current_mode

            # 读取所有MAX_EQ_BANDS个EQ参数
            for eq_index in range(MAX_EQ_BANDS):
                # 发送读取请求
                data = bytearray(63)
                data[0] = 0x77  # 同步头1
                data[1] = 0x8E  # 命令码 (GET_EQ_PARAMS)
                data[2] = mode  # 模式值
                data[3] = eq_index  # EQ索引

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在读取EQ {eq_index} 的参数...", self.log_level)
                h.write([0x01] + list(data))

                # 等待设备响应
                import time
                time.sleep(0.05)  # 等待50ms让设备处理请求

                # 使用get_input_report读取响应
                reply = h.get_input_report(0x1, 64)  # 读取63字节（1字节report_id + 62字节数据）
                log_message(LOG_LEVEL_DEBUG, "读取到的响应数据:", self.log_level)
                log_message(LOG_LEVEL_DEBUG, " ".join(f"0x{byte:02x}" for byte in reply), self.log_level)

                if reply:
                    # 检查report ID
                    if reply[0] != 0x01:
                        log_message(LOG_LEVEL_ERROR, f"错误: 无效的report ID: 0x{reply[0]:02x}", self.log_level)
                        continue

                    # 解析响应数据
                    if reply[1] != 0x77 or reply[2] != 0x8E:  # 检查同步头
                        log_message(LOG_LEVEL_ERROR, f"错误: 无效的响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
                        continue

                    # 模式值
                    mode = reply[3]
                    log_message(LOG_LEVEL_DEBUG, f"  模式值: {mode}", self.log_level)

                    # EQ索引
                    eq_idx = reply[4]
                    log_message(LOG_LEVEL_DEBUG, f"  EQ索引: {eq_idx}", self.log_level)

                    band = self.bands[eq_index]
                    band.enabled.setChecked(True)  # 设置为启用状态

                    # 解析滤波器类型 (1字节)
                    type_map = {
                        0x00: "bypass滤波器",
                        0x01: "全通滤波器",
                        0x02: "峰值滤波器",
                        0x03: "低通滤波器",
                        0x04: "高通滤波器",
                        0x05: "带通滤波器",
                        0x06: "带阻滤波器",
                        0x07: "陷波滤波器",
                        0x08: "恒定Q值滤波器",
                        0x09: "低架滤波器",
                        0x0A: "高架滤波器"
                    }
                    filter_type = type_map.get(reply[5], "bypass滤波器")
                    band.filter_type.setCurrentText(filter_type)
                    log_message(LOG_LEVEL_DEBUG, f"  滤波器类型: {filter_type} (0x{reply[5]:02x})", self.log_level)

                    # 解析参数 - 根据简化协议格式读取float值
                    # fc (4字节) - 位置6-9
                    fc = struct.unpack('<f', bytes(reply[6:10]))[0]
                    log_message(LOG_LEVEL_DEBUG, f"  fc: {fc:.2f} Hz", self.log_level)

                    # Q值 (4字节) - 位置10-13
                    q = struct.unpack('<f', bytes(reply[10:14]))[0]
                    log_message(LOG_LEVEL_DEBUG, f"  Q值: {q:.4f}", self.log_level)

                    # 带宽 (4字节) - 位置14-17
                    bw = struct.unpack('<f', bytes(reply[14:18]))[0]
                    log_message(LOG_LEVEL_DEBUG, f"  带宽: {bw:.2f} Hz", self.log_level)

                    # 增益 (4字节) - 位置18-21
                    gain = struct.unpack('<f', bytes(reply[18:22]))[0]
                    log_message(LOG_LEVEL_DEBUG, f"  增益: {gain:.2f} dB", self.log_level)

                    # 更新UI显示
                    if (eq_idx == eq_index):
                        band.fc.setValue(fc)
                        band.Q.setValue(q)
                        band.bw.setValue(bw)
                        band.gain.setValue(gain)

                        # 系数会在update_eq_curve中通过参数重新计算

                        # 打印读取到的参数
                        log_message(LOG_LEVEL_INFO, f"成功读取EQ {eq_index} 的参数:", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  模式值: {mode}", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  类型: {filter_type}", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  频率: {fc} Hz", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  Q值: {q:.2f}", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  带宽: {bw} Hz", self.log_level)
                        log_message(LOG_LEVEL_INFO, f"  增益: {gain:.2f} dB", self.log_level)
                    else:
                        log_message(LOG_LEVEL_ERROR, f"  读取到EQ {eq_idx} 的参数, 与当前EQ {eq_index} 不匹配", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, "错误: 未收到响应数据", self.log_level)

            h.close()
            self.update_eq_curve()
            log_message(LOG_LEVEL_INFO, "所有参数读取完成", self.log_level)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取参数时出错: {str(e)}", self.log_level)

    def refresh_devices(self):
        """刷新USB设备列表"""
        self.device_combo.clear()
        for device in hid.enumerate():
            # 添加usage page信息
            usage_page = device.get('usage_page', 0)
            pid = device.get('product_id', 0)
            vid = device.get('vendor_id', 0)
            if ((usage_page == 0xff82 or usage_page == 0xff83) or (vid == 0x20b1 and pid == 0x0016)):
                info = f"{device.get('product_string', 'Unknown')}"
                self.device_combo.addItem(info, device)
                self.on_switch_mode()
                self.on_get_mode()
                self.read_parameters()
                log_message(LOG_LEVEL_DEBUG, f"找到设备: {info}", self.log_level)
                # 启动HID通知轮询，接收设备主动上报（0x9F采样率变化等）
                self._notification_timer.start(200)

    def on_debug_changed(self, state):
        """当调试开关状态改变时更新所有滤波器"""
        self.show_qshift_debug = state == Qt.Checked
        for band in self.bands:
            band.show_qshift_debug = self.show_qshift_debug
            if self.show_qshift_debug:
                band.update_coefficients()

    def apply_to_all_sample_rates(self):
        """将当前EQ参数应用到所有采样率
        改动原因：现在所有采样率共用44100Hz参数，此函数只需保存44100Hz参数即可
        """
        try:
            # 改动原因：所有采样率共用44100Hz参数，直接保存当前参数即可
            log_message(LOG_LEVEL_INFO, "现在所有采样率共用44100Hz参数，直接保存当前参数", self.log_level)
            self.save_parameters()  # 直接保存当前参数（44100Hz）
            log_message(LOG_LEVEL_INFO, "参数已保存（适用于所有采样率）", self.log_level)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"保存参数时出错: {str(e)}", self.log_level)
            import traceback
            log_message(LOG_LEVEL_ERROR, "详细错误信息:", self.log_level)
            traceback.print_exc()

    def on_config_params(self):
        """配置音效参数（发送8个0x8D命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 弹出确认对话框
        sample_rate = self.sample_rate.currentText()
        mode_name = self.mode_combo.currentText()
        mode_index = self.mode_combo.currentIndex()

        reply = QMessageBox.question(
            self,
            "确认配置音效参数",
            f"将要配置以下音效参数：\n\n"
            f"模式: {mode_name} \n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            self.send_parameters()

    def on_read_params(self):
        """读取音效参数（发送8个0x8E命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 弹出确认对话框
        sample_rate = self.sample_rate.currentText()
        mode_name = self.mode_combo.currentText()
        mode_index = self.mode_combo.currentIndex()

        reply = QMessageBox.question(
            self,
            "确认读取音效参数",
            f"将要读取以下音效参数：\n\n"
            f"采样率: {sample_rate} Hz\n"
            f"模式: {mode_name} (索引: {mode_index})\n\n"
            f"这将发送8个0x8E命令到设备。\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            self.read_parameters()

    def on_set_gain(self):
        """设置整体增益（发送0x8C命令到所有采样率）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 弹出确认对话框
        sample_rate = self.sample_rate.currentText()
        mode_name = self.mode_combo.currentText()
        mode_index = self.mode_combo.currentIndex()
        gain_value = self.post_gain_spin.value()

        reply = QMessageBox.question(
            self,
            "确认设置整体增益",
            f"将要设置以下整体增益：\n\n"
            f"模式: {mode_name} \n"
            f"增益值: {gain_value} dB\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            self.send_gain_to_all_sample_rates(mode_index, gain_value)

    def send_gain_to_all_sample_rates(self, mode, gain_value):
        """发送整体增益和名称到所有采样率"""
        try:
            # 获取当前模式名称
            mode_name = self.mode_name_edit.text()
            if not mode_name:
                mode_name = f"模式{mode}"

            log_message(LOG_LEVEL_INFO, f"正在设置模式 {mode} 整体增益为 {gain_value}dB, 名称为 '{mode_name}' (自动应用到所有采样率)", self.log_level)

            # 使用新的0x8C命令发送模式增益和名称
            self.send_mode_gain_and_name(mode, gain_value, mode_name)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"设置整体增益时出错: {str(e)}", self.log_level)
            import traceback
            log_message(LOG_LEVEL_ERROR, "详细错误信息:", self.log_level)
            traceback.print_exc()

    def on_get_device_info(self):
        """获取设备信息（发送0x8F命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x8F数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x8F  # 命令码 (GET_DEVICE_INFO)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取设备信息...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x8F:
                    # 解析设备信息
                    pid = int.from_bytes(reply[3:5], 'little')
                    vid = int.from_bytes(reply[5:7], 'little')
                    product_string = bytes(reply[7:23]).decode('utf-8', errors='ignore').rstrip('\x00')
                    vendor_string = bytes(reply[23:39]).decode('utf-8', errors='ignore').rstrip('\x00')
                    serial_string = bytes(reply[39:55]).decode('utf-8', errors='ignore').rstrip('\x00')

                    # 更新UI显示
                    self.device_pid_label.setText(f"PID: 0x{pid:04x}")
                    self.device_vid_label.setText(f"VID: 0x{vid:04x}")
                    self.device_product_label.setText(f"产品: {product_string}")
                    self.device_vendor_label.setText(f"厂商: {vendor_string}")
                    self.device_serial_label.setText(f"序列号: {serial_string}")

                    log_message(LOG_LEVEL_INFO, f"设备信息获取成功:", self.log_level)
                    log_message(LOG_LEVEL_INFO, f"  PID: 0x{pid:04x}", self.log_level)
                    log_message(LOG_LEVEL_INFO, f"  VID: 0x{vid:04x}", self.log_level)
                    log_message(LOG_LEVEL_INFO, f"  产品: {product_string}", self.log_level)
                    log_message(LOG_LEVEL_INFO, f"  厂商: {vendor_string}", self.log_level)
                    log_message(LOG_LEVEL_INFO, f"  序列号: {serial_string}", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x8F响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x8F响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取设备信息时出错: {str(e)}", self.log_level)

    def on_mode_name_changed(self):
        """模式名称改变时的处理"""
        mode_name = self.mode_name_edit.text()
        log_message(LOG_LEVEL_DEBUG, f"模式名称已更改为: '{mode_name}'", self.log_level)

    def send_mode_gain_and_name(self, mode, gain, name):
        """发送模式增益和名称（0x8C命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x8C数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x8C  # 命令码 (SET_MODE_GAIN_AND_NAME)

            # 模式值 (1字节)
            data[2] = mode
            log_message(LOG_LEVEL_VERBOSE, f"  模式值: {mode}", self.log_level)

            # 增益值 (4字节，范围0到-50dB)
            data[3:7] = gain.to_bytes(4, 'little', signed=True)
            log_message(LOG_LEVEL_VERBOSE, f"  增益值: {gain} dB", self.log_level)

            # 模式名称 (16字节，UTF-8编码)
            name_bytes = name.encode('utf-8')[:15]  # 最多15字节
            data[7:7+len(name_bytes)] = name_bytes
            # 剩余字节保持为0

            log_message(LOG_LEVEL_VERBOSE, f"  模式名称: '{name}'", self.log_level)

            # 发送数据包
            log_message(LOG_LEVEL_DEBUG, f"  正在发送数据包...", self.log_level)
            h.write([0x01] + list(data))  # 0x01是report_id
            log_message(LOG_LEVEL_DEBUG, f"  数据包发送完成", self.log_level)

            h.close()
            log_message(LOG_LEVEL_INFO, f"模式增益和名称设置完成 (模式 {mode}, 增益 {gain}dB, 名称 '{name}')", self.log_level)

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"设置模式增益和名称时出错: {str(e)}", self.log_level)

    def on_reset_eq_params(self):
        """复位EQ参数（发送0x90命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 弹出确认对话框
        mode_name = self.mode_combo.currentText()
        mode_index = self.mode_combo.currentIndex()

        reply = QMessageBox.question(
            self,
            "确认复位EQ参数",
            f"将要复位以下EQ参数：\n\n"
            f"模式: {mode_name} (索引: {mode_index})\n\n"
            f"这将删除Flash中存储的EQ参数并恢复头文件预设参数。\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            self.send_reset_eq_params(mode_index)

    def send_reset_eq_params(self, mode):
        """发送复位EQ参数命令（0x90命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x90数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x90  # 命令码 (RESET_EQ_PARAMS)

            # 模式值 (1字节，0-9表示指定模式，0xFF表示所有模式)
            data[2] = mode
            log_message(LOG_LEVEL_VERBOSE, f"  复位模式: {mode}", self.log_level)

            # 发送数据包
            log_message(LOG_LEVEL_INFO, f"正在复位EQ参数 (模式 {mode})...", self.log_level)
            h.write([0x01] + list(data))  # 0x01是report_id

            # 读取响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x90:
                    # 解析状态码
                    status = reply[3]
                    if status == 0x00:
                        log_message(LOG_LEVEL_INFO, f"EQ参数复位成功 (模式 {mode})", self.log_level)
                        # 复位成功后重新读取当前模式信息
                        self.on_get_mode()
                        self.read_parameters()
                    else:
                        log_message(LOG_LEVEL_ERROR, f"EQ参数复位失败 (模式 {mode}, 状态码: 0x{status:02x})", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x90响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x90响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"复位EQ参数时出错: {str(e)}", self.log_level)

    def on_factory_reset(self):
        """恢复出厂默认（发送0x84命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        reply = QMessageBox.warning(
            self,
            "确认恢复出厂默认",
            "这将删除所有已保存的参数（音效模式、麦克风增益、监听音量、脚步声增强、EQ预设），\n"
            "恢复出厂默认值，并重启设备。\n\n"
            "出厂默认值：\n"
            "  · 音效模式: AI7.1 (3)\n"
            "  · 脚步声增强: 12dB (全亮)\n"
            "  · 麦克风增益: 21dB\n"
            "  · 监听音量: -14dB\n"
            "  · AI降噪: 开启\n"
            "  · EQ预设: 头文件默认值\n\n"
            "是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            self.send_factory_reset()

    def send_factory_reset(self):
        """发送恢复出厂默认命令（0x84命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x84数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x84  # 命令码 (FACTORY_RESET)
            # data[2..62] 保留，保持为0

            log_message(LOG_LEVEL_INFO, "正在发送恢复出厂默认命令...", self.log_level)
            h.write([0x01] + list(data))  # 0x01是report_id

            # 读取响应（设备重启前会先回复）
            reply = h.get_input_report(0x1, 64)
            if reply and len(reply) == 64:
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x84:
                    status = reply[3]
                    if status == 0x00:
                        log_message(LOG_LEVEL_INFO, "恢复出厂默认成功，设备即将重启...", self.log_level)
                        QMessageBox.information(
                            self,
                            "恢复出厂默认",
                            "命令已发送，设备将在500ms后重启。\n请稍候重新连接设备。"
                        )
                    else:
                        log_message(LOG_LEVEL_ERROR, f"恢复出厂默认失败 (状态码: 0x{status:02x})", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x84响应: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                # 设备可能已开始重启，视为成功
                log_message(LOG_LEVEL_INFO, "未收到响应，设备可能已开始重启", self.log_level)
                QMessageBox.information(
                    self,
                    "恢复出厂默认",
                    "命令已发送，设备将重启。\n请稍候重新连接设备。"
                )

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"恢复出厂默认时出错: {str(e)}", self.log_level)

    def on_get_mode_count(self):
        """获取EQ模式总数（发送0x91命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x91数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x91  # 命令码 (GET_EQ_MODE_COUNT)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取EQ模式总数...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x91:
                    # 解析模式总数
                    mode_count = reply[3]
                    # 解析预定义模式数量
                    preset_mode_count = reply[4]
                    log_message(LOG_LEVEL_INFO, f"EQ模式总数: {mode_count}, 预定义模式数量: {preset_mode_count}", self.log_level)
                    QMessageBox.information(
                        self,
                        "EQ模式总数",
                        f"当前EQ模式总数为: {mode_count}\n"
                        f"预定义模式数量: {preset_mode_count}\n\n"
                        f"（包含预定义模式和用户模式，不包含禁用模式）"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x91响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x91响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取EQ模式总数时出错: {str(e)}", self.log_level)

    def on_set_and_save_mode(self):
        """设置并保存EQ模式（发送0x92命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取当前选择的模式
        mode_index = self.mode_combo.currentIndex()
        if mode_index == NUM_EQ_MODES:  # 禁用EQ选项
            mode = EQ_DISABLED_MODE
        else:
            mode = mode_index

        # 验证模式范围
        if mode > 9:
            QMessageBox.warning(
                self,
                "错误",
                f"无效的模式值: {mode}\n\n"
                f"有效范围: 0-9 (0-5预设模式, 6-8用户模式, 9: bypass)"
            )
            return

        # 弹出确认对话框
        mode_name = self.mode_combo.currentText()
        reply = QMessageBox.question(
            self,
            "确认设置并保存EQ模式",
            f"将要设置并保存以下EQ模式：\n\n"
            f"模式: {mode_name} (索引: {mode})\n\n"
            f"该模式将被保存到Flash，开机时自动恢复。\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0x92数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0x92  # 命令码 (SET_AND_SAVE_EQ_MODE)
                data[2] = mode  # 模式值
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置并保存EQ模式到 {mode}...", self.log_level)
                h.write([0x01] + list(data))

                # 读取响应
                import time
                time.sleep(0.05)  # 等待设备响应
                reply_data = h.get_input_report(0x1, 64)  # 64字节响应
                if reply_data and len(reply_data) == 64:
                    # 检查Report ID和同步头
                    if reply_data[0] == 0x01 and reply_data[1] == 0x77 and reply_data[2] == 0x92:
                        # 解析状态码
                        status = reply_data[3]
                        if status == 0x00:
                            log_message(LOG_LEVEL_INFO, f"EQ模式 {mode} 设置并保存成功", self.log_level)
                            QMessageBox.information(
                                self,
                                "成功",
                                f"EQ模式 {mode} ({mode_name}) 已成功设置并保存到Flash。\n\n"
                                f"设备开机时将自动恢复该模式。"
                            )
                            # 更新当前模式
                            self.current_mode = mode
                        else:
                            log_message(LOG_LEVEL_ERROR, f"EQ模式设置并保存失败 (状态码: 0x{status:02x})", self.log_level)
                            QMessageBox.warning(
                                self,
                                "失败",
                                f"EQ模式设置并保存失败。\n\n"
                                f"状态码: 0x{status:02x}"
                            )
                    else:
                        log_message(LOG_LEVEL_ERROR, f"无效的0x92响应同步头: 0x{reply_data[1]:02x} 0x{reply_data[2]:02x}", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, "未收到0x92响应数据", self.log_level)

                h.close()

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置并保存EQ模式时出错: {str(e)}", self.log_level)

    def on_set_volume(self):
        """设置监听音量级别（发送0x93命令，范围0-48，按SY102dac对应表映射）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        volume_level = self.volume_spin.value()

        reply = QMessageBox.question(
            self,
            "确认设置监听音量",
            f"将要设置监听音量级别为: {volume_level}\n\n"
            f"音量范围: 0-48 (0=静音, 1=-66dB, ..., 47=-0.5dB, 48=0dB；按SY102dac对应表)\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                data = bytearray(63)
                data[0] = 0x77
                data[1] = 0x93  # SET_VOLUME
                data[2] = volume_level  # 0-48

                log_message(LOG_LEVEL_INFO, f"正在设置监听音量级别为 {volume_level}...", self.log_level)
                h.write([0x01] + list(data))
                h.close()

                log_message(LOG_LEVEL_INFO, f"监听音量级别 {volume_level} 设置成功", self.log_level)
                QMessageBox.information(self, "成功", f"监听音量级别已设置为: {volume_level}")

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置监听音量时出错: {str(e)}", self.log_level)

    def on_get_volume(self):
        """读取监听音量级别（发送0x94命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77
            data[1] = 0x94  # GET_VOLUME

            log_message(LOG_LEVEL_INFO, "正在读取监听音量级别...", self.log_level)
            h.write([0x01] + list(data))

            import time
            time.sleep(0.05)
            reply = h.get_input_report(0x1, 64)
            if reply and len(reply) == 64:
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x94:
                    volume_level = reply[3]
                    self.volume_spin.setValue(volume_level)
                    log_message(LOG_LEVEL_INFO, f"当前监听音量级别: {volume_level}", self.log_level)
                    QMessageBox.information(
                        self, "监听音量级别",
                        f"当前监听音量级别: {volume_level}\n\n"
                        f"音量范围: 0-48 (0=静音, 1=-66dB, ..., 47=-0.5dB, 48=0dB；按SY102dac对应表)"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x94响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x94响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取监听音量时出错: {str(e)}", self.log_level)

    def on_set_mic_volume(self):
        """设置麦克风增益级别（发送0x82命令，范围0-48，2级=1dB，48=24dB）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        mic_level = self.mic_volume_spin.value()

        reply = QMessageBox.question(
            self,
            "确认设置麦克风增益",
            f"将要设置麦克风增益级别为: {mic_level}\n\n"
            f"增益范围: 0-48 (0=静音, 2级=1dB, ..., 48=24dB)\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                data = bytearray(63)
                data[0] = 0x77
                data[1] = 0x82  # SET_MIC_VOLUME
                data[2] = mic_level  # 0-48

                log_message(LOG_LEVEL_INFO, f"正在设置麦克风增益级别为 {mic_level}...", self.log_level)
                h.write([0x01] + list(data))
                h.close()

                log_message(LOG_LEVEL_INFO, f"麦克风增益级别 {mic_level} 设置成功", self.log_level)
                QMessageBox.information(self, "成功", f"麦克风增益级别已设置为: {mic_level}")

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置麦克风增益时出错: {str(e)}", self.log_level)

    def on_get_mic_volume(self):
        """读取麦克风增益级别（发送0x83命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77
            data[1] = 0x83  # GET_MIC_VOLUME

            log_message(LOG_LEVEL_INFO, "正在读取麦克风增益级别...", self.log_level)
            h.write([0x01] + list(data))

            import time
            time.sleep(0.05)
            reply = h.get_input_report(0x1, 64)
            if reply and len(reply) == 64:
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x83:
                    mic_level = reply[3]
                    self.mic_volume_spin.setValue(mic_level)
                    log_message(LOG_LEVEL_INFO, f"当前麦克风增益级别: {mic_level}", self.log_level)
                    QMessageBox.information(
                        self, "麦克风增益级别",
                        f"当前麦克风增益级别: {mic_level}\n\n"
                        f"增益范围: 0-48 (0=静音, 2级=1dB, ..., 48=24dB)"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x83响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x83响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取麦克风增益时出错: {str(e)}", self.log_level)

    def on_set_noise_strength(self):
        """设置AI降噪强度（发送0x85命令，0=关闭，2-100偶数=强度，步进2）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        strength = self.noise_strength_spin.value()
        # 强制对齐到偶数（SpinBox步进2但手动输入可能为奇数）
        if strength != 0 and strength % 2 != 0:
            strength -= 1
            self.noise_strength_spin.setValue(strength)

        desc = "关闭AI降噪" if strength == 0 else f"强度{strength}（内部{-(strength*2)}dB）"
        reply = QMessageBox.question(
            self,
            "确认设置AI降噪强度",
            f"将要设置AI降噪强度为: {strength}\n{desc}\n\n是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                data = bytearray(63)
                data[0] = 0x77
                data[1] = 0x85  # SET_AI_NOISE_STRENGTH
                data[2] = strength

                log_message(LOG_LEVEL_INFO, f"正在设置AI降噪强度为 {strength}...", self.log_level)
                h.write([0x01] + list(data))
                h.close()

                log_message(LOG_LEVEL_INFO, f"AI降噪强度 {strength} 设置成功", self.log_level)
                QMessageBox.information(self, "成功", f"AI降噪强度已设置为: {strength}\n{desc}")

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置AI降噪强度时出错: {str(e)}", self.log_level)

    def on_get_noise_strength(self):
        """读取AI降噪强度（发送0x86命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77
            data[1] = 0x86  # GET_AI_NOISE_STRENGTH

            log_message(LOG_LEVEL_INFO, "正在读取AI降噪强度...", self.log_level)
            h.write([0x01] + list(data))

            import time
            time.sleep(0.05)
            reply = h.get_input_report(0x1, 64)
            if reply and len(reply) == 64:
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x86:
                    strength = reply[3]
                    self.noise_strength_spin.setValue(strength)
                    desc = "关闭" if strength == 0 else f"强度{strength}（{-(strength*2)}dB）"
                    log_message(LOG_LEVEL_INFO, f"当前AI降噪强度: {strength}", self.log_level)
                    QMessageBox.information(
                        self, "AI降噪强度",
                        f"当前AI降噪强度: {strength}\n{desc}\n\n"
                        f"范围: 0=关闭, 2-100偶数=强度级别, 步进2"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x86响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x86响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取AI降噪强度时出错: {str(e)}", self.log_level)

    def on_set_monitor_switch(self):
        """设置耳返开关（发送0x87命令，0=关闭，1=开启）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        mon_sw = self.monitor_switch_combo.currentIndex()  # 0=关闭, 1=开启
        desc = "关闭耳返" if mon_sw == 0 else "开启耳返（ADC路由到耳机输出）"
        reply = QMessageBox.question(
            self,
            "确认设置耳返开关",
            f"将要设置耳返开关为: {'开启' if mon_sw else '关闭'}\n{desc}\n\n是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                data = bytearray(63)
                data[0] = 0x77
                data[1] = 0x87  # SET_MONITOR_SWITCH
                data[2] = mon_sw

                log_message(LOG_LEVEL_INFO, f"正在设置耳返开关为 {'开启' if mon_sw else '关闭'}...", self.log_level)
                h.write([0x01] + list(data))

                import time
                time.sleep(0.05)
                reply_data = h.get_input_report(0x1, 64)
                if reply_data and len(reply_data) == 64:
                    if reply_data[0] == 0x01 and reply_data[1] == 0x77 and reply_data[2] == 0x88:
                        cur_val = reply_data[3]
                        self.monitor_switch_combo.setCurrentIndex(cur_val)
                        log_message(LOG_LEVEL_INFO, f"耳返开关已设置为: {'开启' if cur_val else '关闭'}", self.log_level)
                        QMessageBox.information(self, "成功", f"耳返开关已设置为: {'开启' if cur_val else '关闭'}")
                    else:
                        log_message(LOG_LEVEL_INFO, f"耳返开关设置成功（无响应确认）", self.log_level)
                        QMessageBox.information(self, "成功", f"耳返开关已设置为: {'开启' if mon_sw else '关闭'}")
                else:
                    log_message(LOG_LEVEL_INFO, f"耳返开关设置命令已发送", self.log_level)
                    QMessageBox.information(self, "成功", f"耳返开关已设置为: {'开启' if mon_sw else '关闭'}")

                h.close()

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置耳返开关时出错: {str(e)}", self.log_level)

    def on_get_monitor_switch(self):
        """读取耳返开关状态（发送0x88命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77
            data[1] = 0x88  # GET_MONITOR_SWITCH

            log_message(LOG_LEVEL_INFO, "正在读取耳返开关状态...", self.log_level)
            h.write([0x01] + list(data))

            import time
            time.sleep(0.05)
            reply = h.get_input_report(0x1, 64)
            if reply and len(reply) == 64:
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x88:
                    mon_sw = reply[3]
                    # 更新UI下拉框（index直接等于值：0=关闭, 1=开启）
                    if mon_sw <= 1:
                        self.monitor_switch_combo.setCurrentIndex(mon_sw)
                    log_message(LOG_LEVEL_INFO, f"当前耳返开关状态: {'开启' if mon_sw else '关闭'}", self.log_level)
                    QMessageBox.information(
                        self, "耳返开关状态",
                        f"当前耳返开关: {'开启 (1)' if mon_sw else '关闭 (0)'}\n\n"
                        f"说明: 开启时ADC数据实时路由到耳机输出（需麦克风未静音）"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x88响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x88响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取耳返开关状态时出错: {str(e)}", self.log_level)

    def on_get_sample_format(self):
        """读取采样率和格式信息（发送0x9F命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x9F数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x9F  # 命令码 (GET_SAMPLE_FORMAT)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在读取采样率和格式信息...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x9F:
                    # 解析采样率和格式信息（小端序）
                    # 响应格式：reply[0]=Report ID, reply[1]=0x77, reply[2]=0x9F
                    # reply[3-6]=采样率(32位小端序), reply[7]=DSD模式
                    samfreq = reply[3] | (reply[4] << 8) | (reply[5] << 16) | (reply[6] << 24)
                    dsd_mode = reply[7]

                    # 格式化显示信息
                    format_type = "PCM" if dsd_mode == 0 else f"DSD (模式 {dsd_mode})"

                    log_message(LOG_LEVEL_INFO,
                               f"采样率和格式信息: 采样率={samfreq}Hz, 格式={format_type}",
                               self.log_level)
                    self._update_sample_format_status(samfreq, dsd_mode)
                    QMessageBox.information(
                        self,
                        "采样率和格式信息",
                        f"采样率: {samfreq} Hz\n"
                        f"格式: {format_type}\n"
                        f"DSD模式: {dsd_mode} (0=PCM, >0=DSD)"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x9F响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x9F响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取采样率和格式时出错: {str(e)}", self.log_level)

    def _update_sample_format_status(self, samfreq, dsd_mode):
        """更新采样率状态标签，并同步EQ采样率下拉框"""
        format_type = "PCM" if dsd_mode == 0 else f"DSD (模式 {dsd_mode})"
        self.sample_format_status_label.setText(f"{samfreq} Hz  {format_type}")
        # 同步EQ计算用采样率下拉框
        idx = self.sample_rate.findText(str(samfreq))
        if idx >= 0:
            self.sample_rate.blockSignals(True)
            self.sample_rate.setCurrentIndex(idx)
            self.sample_rate.blockSignals(False)

    def _poll_hid_notification(self):
        """轮询HID输入报告，处理设备主动上报的0x9F采样率变化通知"""
        if self.device_combo.currentData() is None:
            return
        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)
            data = h.read(64)
            h.close()
            if data and len(data) >= 8:
                # 0x9F: 采样率变化主动上报 [Report_ID=0x01, 0x77, 0x9F, samfreq(4B LE), dsd_mode]
                if data[0] == 0x01 and data[1] == 0x77 and data[2] == 0x9F:
                    samfreq = data[3] | (data[4] << 8) | (data[5] << 16) | (data[6] << 24)
                    dsd_mode = data[7]
                    self._update_sample_format_status(samfreq, dsd_mode)
                    log_message(LOG_LEVEL_INFO,
                                f"[0x9F通知] 采样率变化: {samfreq} Hz, "
                                f"{'PCM' if dsd_mode == 0 else f'DSD({dsd_mode})'}",
                                self.log_level)
        except Exception:
            pass

    def on_get_led_info(self):
        """获取LED信息（发送0x95命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x95数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x95  # 命令码 (GET_LED_INFO)
            data[2] = self.led_index_spin.value()  # LED索引
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, f"正在获取LED信息 (索引: {self.led_index_spin.value()})...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x95:
                    # 解析LED信息
                    led_index = reply[3]
                    led_name_bytes = bytes(reply[4:20])
                    led_name = led_name_bytes.decode('utf-8', errors='ignore').rstrip('\x00')

                    # 更新UI显示
                    self.led_info_label.setText(f"LED信息: 索引={led_index}, 名称='{led_name}'")

                    log_message(LOG_LEVEL_INFO, f"LED信息获取成功: 索引={led_index}, 名称='{led_name}'", self.log_level)
                    QMessageBox.information(
                        self,
                        "LED信息",
                        f"LED索引: {led_index}\n"
                        f"LED名称: {led_name}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x95响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x95响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取LED信息时出错: {str(e)}", self.log_level)

    def on_set_led_switch(self):
        """设置LED开关（发送0x96命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取LED开关值
        led_switch = self.led_switch_combo.currentIndex()  # 0=OFF, 1=ON
        led_index = self.led_index_spin.value()

        # 弹出确认对话框
        reply = QMessageBox.question(
            self,
            "确认设置LED开关",
            f"将要设置LED开关为: {'ON (开启)' if led_switch == 1 else 'OFF (关闭)'}\n\n"
            f"LED索引: {led_index}\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0x96数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0x96  # 命令码 (SET_LED_SWITCH)
                data[2] = led_index  # LED索引
                data[3] = led_switch  # LED开关 (0=OFF, 1=ON)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置LED开关为 {'ON' if led_switch == 1 else 'OFF'} (索引: {led_index})...", self.log_level)
                h.write([0x01] + list(data))

                # 读取响应
                import time
                time.sleep(0.05)  # 等待设备响应
                reply_data = h.get_input_report(0x1, 64)  # 64字节响应
                if reply_data and len(reply_data) == 64:
                    # 检查Report ID和同步头
                    if reply_data[0] == 0x01 and reply_data[1] == 0x77 and reply_data[2] == 0x96:
                        # 解析状态码（根据协议，位置3是状态码）
                        status = reply_data[3]
                        if status == 0x00:
                            log_message(LOG_LEVEL_INFO, f"LED开关设置成功", self.log_level)
                            QMessageBox.information(
                                self,
                                "成功",
                                f"LED开关已设置为: {'ON (开启)' if led_switch == 1 else 'OFF (关闭)'}\n\n"
                                f"LED索引: {led_index}"
                            )
                        else:
                            log_message(LOG_LEVEL_ERROR, f"LED开关设置失败 (状态码: 0x{status:02x})", self.log_level)
                            QMessageBox.warning(
                                self,
                                "失败",
                                f"LED开关设置失败。\n\n"
                                f"状态码: 0x{status:02x}"
                            )
                    else:
                        log_message(LOG_LEVEL_ERROR, f"无效的0x96响应同步头: 0x{reply_data[1]:02x} 0x{reply_data[2]:02x}", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, "未收到0x96响应数据", self.log_level)

                h.close()

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置LED开关时出错: {str(e)}", self.log_level)

    def on_get_led_switch(self):
        """获取LED开关（发送0x97命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x97数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x97  # 命令码 (GET_LED_SWITCH)
            data[2] = self.led_index_spin.value()  # LED索引
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, f"正在获取LED开关状态 (索引: {self.led_index_spin.value()})...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x97:
                    # 解析LED开关状态
                    led_index = reply[3]
                    led_switch = reply[4]  # 0=OFF, 1=ON

                    # 更新UI显示
                    self.led_switch_combo.setCurrentIndex(led_switch)

                    log_message(LOG_LEVEL_INFO, f"当前LED开关状态: {'ON (开启)' if led_switch == 1 else 'OFF (关闭)'} (索引: {led_index})", self.log_level)
                    QMessageBox.information(
                        self,
                        "LED开关状态",
                        f"LED索引: {led_index}\n"
                        f"LED开关: {'ON (开启)' if led_switch == 1 else 'OFF (关闭)'}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x97响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x97响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取LED开关时出错: {str(e)}", self.log_level)

    def on_get_led_status(self):
        """获取LED状态（发送0x98命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x98数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x98  # 命令码 (GET_LED_STATUS)
            data[2] = self.led_index_spin.value()  # LED索引
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, f"正在获取LED状态 (索引: {self.led_index_spin.value()})...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            log_message(LOG_LEVEL_INFO, f"LED状态响应: {reply}", self.log_level)
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x98:
                    # 解析LED状态
                    led_index = reply[3]
                    r = reply[4]  # R颜色值
                    g = reply[5]  # G颜色值
                    b = reply[6]  # B颜色值
                    led_status = reply[7]  # LED状态

                    # LED状态名称映射
                    status_names = {
                        0: "熄灭",
                        1: "常亮",
                        2: "慢闪",
                        3: "快闪",
                        4: "呼吸"
                    }
                    status_name = status_names.get(led_status, f"未知({led_status})")

                    # 更新UI显示
                    self.led_status_label.setText(
                        f"LED状态: RGB=({r},{g},{b}), 状态={status_name}"
                    )

                    log_message(LOG_LEVEL_INFO,
                               f"LED状态 - 索引: {led_index}, RGB: ({r},{g},{b}), 状态: {status_name}",
                               self.log_level)
                    QMessageBox.information(
                        self,
                        "LED状态",
                        f"LED索引: {led_index}\n"
                        f"RGB颜色: ({r}, {g}, {b})\n"
                        f"状态: {status_name}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x98响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x98响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取LED状态时出错: {str(e)}", self.log_level)

    def on_get_led_count(self):
        """获取LED总数（发送0x99命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x99数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x99  # 命令码 (GET_LED_COUNT)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取LED总数...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x99:
                    # 解析LED总数
                    led_count = reply[3]

                    log_message(LOG_LEVEL_INFO, f"设备支持的LED总数: {led_count}", self.log_level)
                    QMessageBox.information(
                        self,
                        "LED总数",
                        f"设备支持的LED总数: {led_count}\n\n"
                        f"LED索引范围: 0 到 {led_count - 1}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x99响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x99响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取LED总数时出错: {str(e)}", self.log_level)

    def on_get_uac_mode_info(self):
        """获取UAC模式信息（发送0x9A命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x9A数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x9A  # 命令码 (GET_UAC_MODE_INFO)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取UAC模式信息...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x9A:
                    # 解析UAC模式信息
                    uac_mode_count = reply[3]
                    mode0_name_bytes = bytes(reply[4:12])
                    mode1_name_bytes = bytes(reply[12:20])
                    mode0_name = mode0_name_bytes.decode('utf-8', errors='ignore').rstrip('\x00')
                    mode1_name = mode1_name_bytes.decode('utf-8', errors='ignore').rstrip('\x00')

                    log_message(LOG_LEVEL_INFO, f"UAC模式信息获取成功: 总数={uac_mode_count}, 模式0='{mode0_name}', 模式1='{mode1_name}'", self.log_level)
                    QMessageBox.information(
                        self,
                        "UAC模式信息",
                        f"UAC模式总数: {uac_mode_count}\n\n"
                        f"模式0: {mode0_name}\n"
                        f"模式1: {mode1_name}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x9A响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x9A响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取UAC模式信息时出错: {str(e)}", self.log_level)

    def on_set_uac_mode(self):
        """设置UAC模式（发送0x9B命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取UAC模式值
        uac_mode_index = self.uac_mode_combo.currentIndex()  # 0=UAC2.0, 1=UAC1.0
        uac_mode_name = self.uac_mode_combo.currentText()

        # 弹出确认对话框
        reply = QMessageBox.question(
            self,
            "确认设置UAC模式",
            f"将要设置UAC模式为: {uac_mode_name}\n\n"
            f"注意：切换到UAC1.0模式时，设备将自动重启。\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0x9B数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0x9B  # 命令码 (SET_UAC_MODE)
                data[2] = uac_mode_index  # UAC模式值 (0=UAC2.0, 1=UAC1.0)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置UAC模式为 {uac_mode_name}...", self.log_level)
                h.write([0x01] + list(data))

                # 改动原因：SET_UAC_MODE命令不返回响应
                # 如果设置为UAC1.0，设备会重启；如果设置为UAC2.0，设备保持当前状态
                if uac_mode_index == 1:  # UAC1.0 - 设备会重启
                    log_message(LOG_LEVEL_INFO, f"UAC模式设置为 {uac_mode_name}，设备将重启...", self.log_level)
                    QMessageBox.information(
                        self,
                        "设备重启",
                        f"UAC模式已设置为: {uac_mode_name}\n\n"
                        f"设备正在重启，请稍候重新连接设备。"
                    )
                else:  # UAC2.0
                    log_message(LOG_LEVEL_INFO, f"UAC模式设置为 {uac_mode_name} 成功", self.log_level)
                    QMessageBox.information(
                        self,
                        "成功",
                        f"UAC模式已设置为: {uac_mode_name}"
                    )

                h.close()

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置UAC模式时出错: {str(e)}", self.log_level)

    def on_get_current_uac_mode(self):
        """获取当前UAC模式（发送0x9C命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x9C数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x9C  # 命令码 (GET_CURRENT_UAC_MODE)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取当前UAC模式...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x9C:
                    # 解析当前UAC模式
                    uac_mode_value = reply[3]
                    uac_mode_name_bytes = bytes(reply[4:12])
                    uac_mode_name = uac_mode_name_bytes.decode('utf-8', errors='ignore').rstrip('\x00')

                    # 更新UI显示
                    self.current_uac_mode_label.setText(f"当前UAC模式: {uac_mode_name}")
                    # 更新下拉框选择
                    self.uac_mode_combo.setCurrentIndex(uac_mode_value)

                    log_message(LOG_LEVEL_INFO, f"当前UAC模式: {uac_mode_name} (值: {uac_mode_value})", self.log_level)
                    QMessageBox.information(
                        self,
                        "当前UAC模式",
                        f"当前UAC模式: {uac_mode_name}\n\n"
                        f"模式值: {uac_mode_value} (0=UAC2.0, 1=UAC1.0)"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x9C响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x9C响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取当前UAC模式时出错: {str(e)}", self.log_level)

    def on_set_eq_enable(self):
        """设置EQ使能开关（发送0x9D命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取EQ使能开关值
        eq_enable_index = self.eq_enable_combo.currentIndex()  # 0=OFF, 1=ON
        eq_enable_name = self.eq_enable_combo.currentText()

        # 弹出确认对话框
        reply = QMessageBox.question(
            self,
            "确认设置EQ使能开关",
            f"将要设置EQ使能开关为: {eq_enable_name}\n\n"
            f"注意：eq_enable是全局开关，所有模式共用一个开关。\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0x9D数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0x9D  # 命令码 (SET_EQ_ENABLE)
                data[2] = eq_enable_index  # EQ使能开关 (0=OFF, 1=ON)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置EQ使能开关为 {eq_enable_name}...", self.log_level)
                h.write([0x01] + list(data))

                # 读取响应
                import time
                time.sleep(0.05)  # 等待设备响应
                reply_data = h.get_input_report(0x1, 64)  # 64字节响应
                if reply_data and len(reply_data) == 64:
                    # 检查Report ID和同步头
                    if reply_data[0] == 0x01 and reply_data[1] == 0x77 and reply_data[2] == 0x9D:
                        # 解析状态码和使能状态（改动原因：0x9D只返回状态码和使能状态，不返回模式信息）
                        status = reply_data[3]
                        current_enable = reply_data[4]

                        if status == 0x00:
                            # 更新UI显示
                            self.eq_enable_combo.setCurrentIndex(current_enable)

                            log_message(LOG_LEVEL_INFO, f"EQ使能开关设置成功 - 状态: {current_enable} (全局开关)", self.log_level)
                            QMessageBox.information(
                                self,
                                "成功",
                                f"EQ使能开关已设置为: {eq_enable_name}\n\n"
                                f"当前状态: {'ON (启用)' if current_enable == 1 else 'OFF (禁用)'}"
                            )
                        else:
                            log_message(LOG_LEVEL_ERROR, f"EQ使能开关设置失败 (状态码: 0x{status:02x})", self.log_level)
                            QMessageBox.warning(
                                self,
                                "失败",
                                f"EQ使能开关设置失败。\n\n"
                                f"状态码: 0x{status:02x}"
                            )
                    else:
                        log_message(LOG_LEVEL_ERROR, f"无效的0x9D响应同步头: 0x{reply_data[1]:02x} 0x{reply_data[2]:02x}", self.log_level)
                else:
                    log_message(LOG_LEVEL_ERROR, "未收到0x9D响应数据", self.log_level)

                h.close()

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置EQ使能开关时出错: {str(e)}", self.log_level)

    def on_get_eq_enable(self):
        """获取EQ使能开关（发送0x9E命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0x9E数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0x9E  # 命令码 (GET_EQ_ENABLE)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在获取EQ使能开关状态...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0x9E:
                    # 解析EQ使能开关状态（改动原因：0x9E只返回使能状态，不返回模式信息）
                    eq_enable = reply[3]

                    # 更新UI显示
                    self.eq_enable_combo.setCurrentIndex(eq_enable)

                    log_message(LOG_LEVEL_INFO, f"当前EQ使能开关状态: {'ON (启用)' if eq_enable == 1 else 'OFF (禁用)'} (全局开关)", self.log_level)
                    QMessageBox.information(
                        self,
                        "EQ使能开关状态",
                        f"EQ使能开关: {'ON (启用)' if eq_enable == 1 else 'OFF (禁用)'}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0x9E响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0x9E响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"获取EQ使能开关时出错: {str(e)}", self.log_level)

    def on_set_gain_mode(self):
        """设置增益模式（发送0xA0命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取增益模式值
        gain_mode = self.gain_mode_combo.currentData()

        # 弹出确认对话框
        reply = QMessageBox.question(
            self,
            "确认设置增益模式",
            f"将要设置增益模式为: {self.gain_mode_combo.currentText()}\n\n"
            f"增益模式: 0=低阻, 1=高阻\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0xA0数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0xA0  # 命令码 (SET_GAIN_MODE)
                data[2] = gain_mode  # 增益模式值 (0-1)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置增益模式为 {gain_mode}...", self.log_level)
                h.write([0x01] + list(data))

                h.close()
                log_message(LOG_LEVEL_INFO, f"增益模式 {gain_mode} 设置成功", self.log_level)
                QMessageBox.information(
                    self,
                    "成功",
                    f"增益模式已设置为: {self.gain_mode_combo.currentText()}"
                )

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置增益模式时出错: {str(e)}", self.log_level)

    def on_get_gain_mode(self):
        """读取增益模式（发送0xA1命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xA1数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xA1  # 命令码 (GET_GAIN_MODE)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在读取增益模式...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xA1:
                    # 解析增益模式
                    gain_mode = reply[3]

                    # 更新UI显示
                    index = self.gain_mode_combo.findData(gain_mode)
                    if index >= 0:
                        self.gain_mode_combo.setCurrentIndex(index)

                    log_message(LOG_LEVEL_INFO, f"当前增益模式: {gain_mode} ({'低阻' if gain_mode == 0 else '高阻'})", self.log_level)
                    QMessageBox.information(
                        self,
                        "增益模式",
                        f"当前增益模式: {gain_mode}\n\n"
                        f"{'低阻模式' if gain_mode == 0 else '高阻模式'}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xA1响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xA1响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取增益模式时出错: {str(e)}", self.log_level)

    def on_set_filter_mode(self):
        """设置滤波器模式（发送0xA2命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取滤波器模式值
        filter_mode = self.filter_mode_combo.currentData()

        # 弹出确认对话框
        # 改动原因：更新滤波器模式范围为0-7（8种模式）
        reply = QMessageBox.question(
            self,
            "确认设置滤波器模式",
            f"将要设置滤波器模式为: {self.filter_mode_combo.currentText()}\n\n"
            f"滤波器模式范围: 0-7 (8种模式)\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0xA2数据包
                # 改动原因：更新滤波器模式范围为0-7（8种模式）
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0xA2  # 命令码 (SET_FILTER_MODE)
                data[2] = filter_mode  # 滤波器模式值 (0-7)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置滤波器模式为 {filter_mode}...", self.log_level)
                h.write([0x01] + list(data))

                h.close()
                log_message(LOG_LEVEL_INFO, f"滤波器模式 {filter_mode} 设置成功", self.log_level)
                QMessageBox.information(
                    self,
                    "成功",
                    f"滤波器模式已设置为: {self.filter_mode_combo.currentText()}"
                )

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置滤波器模式时出错: {str(e)}", self.log_level)

    def on_get_filter_mode(self):
        """读取滤波器模式（发送0xA3命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xA3数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xA3  # 命令码 (GET_FILTER_MODE)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在读取滤波器模式...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xA3:
                    # 解析滤波器模式
                    filter_mode = reply[3]

                    # 更新UI显示
                    index = self.filter_mode_combo.findData(filter_mode)
                    if index >= 0:
                        self.filter_mode_combo.setCurrentIndex(index)

                    log_message(LOG_LEVEL_INFO, f"当前滤波器模式: {filter_mode}", self.log_level)
                    QMessageBox.information(
                        self,
                        "滤波器模式",
                        f"当前滤波器模式: {filter_mode}\n\n"
                        f"{self.filter_mode_combo.currentText()}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xA3响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xA3响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取滤波器模式时出错: {str(e)}", self.log_level)

    def on_set_game_mode(self):
        """设置音效模式（发送0xA4命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        # 获取音效模式值
        game_mode = self.game_mode_combo.currentData()

        # 弹出确认对话框
        reply = QMessageBox.question(
            self,
            "确认设置音效模式",
            f"将要设置音效模式为: {self.game_mode_combo.currentText()}\n\n"
            f"音效模式: 0=无音效, 1=音乐模式, 2=游戏模式, 3=AI7.1模式\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply == QMessageBox.Yes:
            try:
                device_info = self.device_combo.currentData()
                h = hid.device()
                h.open(device_info['vendor_id'], device_info['product_id'])
                h.set_nonblocking(1)

                # 构建0xA4数据包
                data = bytearray(63)  # 63字节数据包
                data[0] = 0x77  # 同步头1
                data[1] = 0xA4  # 命令码 (SET_SOUND_EFFECT_MODE)
                data[2] = game_mode  # 音效模式值 (0-3)
                # 其余字节保持为0

                # 发送请求
                log_message(LOG_LEVEL_INFO, f"正在设置音效模式为 {game_mode}...", self.log_level)
                h.write([0x01] + list(data))

                h.close()
                log_message(LOG_LEVEL_INFO, f"音效模式 {game_mode} 设置成功", self.log_level)
                QMessageBox.information(
                    self,
                    "成功",
                    f"音效模式已设置为: {self.game_mode_combo.currentText()}"
                )

            except Exception as e:
                log_message(LOG_LEVEL_ERROR, f"设置音效模式时出错: {str(e)}", self.log_level)

    def on_get_game_mode(self):
        """读取音效模式（发送0xA5命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xA5数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xA5  # 命令码 (GET_SOUND_EFFECT_MODE)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在读取音效模式...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xA5:
                    # 解析音效模式
                    game_mode = reply[3]

                    # 更新UI显示
                    index = self.game_mode_combo.findData(game_mode)
                    if index >= 0:
                        self.game_mode_combo.setCurrentIndex(index)

                    mode_names = ["无音效", "音乐模式", "游戏模式", "AI7.1模式"]
                    mode_name = mode_names[game_mode] if game_mode < len(mode_names) else "未知"

                    log_message(LOG_LEVEL_INFO, f"当前音效模式: {game_mode} ({mode_name})", self.log_level)
                    QMessageBox.information(
                        self,
                        "音效模式",
                        f"当前音效模式: {game_mode}\n\n"
                        f"{mode_name}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xA5响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xA5响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取音效模式时出错: {str(e)}", self.log_level)
    def on_set_log_switch(self):
        """设置log打印开关（发送0x70命令），0=关闭 1=开启"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        value = self.log_switch_combo.currentData()
        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77
            data[1] = 0x70  # SET_LOG_SWITCH
            data[2] = value  # 0=关闭 1=开启
            log_message(LOG_LEVEL_INFO, f"正在设置log开关为 {value} (0=关闭 1=开启)...", self.log_level)
            h.write([0x01] + list(data))
            h.close()
            state_name = "关闭" if value == 0 else "开启"
            log_message(LOG_LEVEL_INFO, f"log开关已设置为 {state_name}", self.log_level)
            QMessageBox.information(
                self,
                "Log控制",
                f"log打印已设置为: {self.log_switch_combo.currentText()}\n\n{state_name}"
            )
        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"设置log开关时出错: {str(e)}", self.log_level)

    def on_get_firmware_version(self):
        """读取固件版本（发送0xA6命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xA6数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xA6  # 命令码 (GET_FIRMWARE_VERSION)
            # 其余字节保持为0

            # 发送请求
            log_message(LOG_LEVEL_INFO, "正在读取固件版本...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xA6:
                    # 解析固件版本（BCD格式）
                    major_version = reply[3]  # 主版本号
                    minor_version = reply[4]  # 次版本号
                    revision_version = reply[5]  # 修订版本号

                    # 格式化版本字符串
                    version_str = f"{major_version}.{minor_version}.{revision_version}"

                    log_message(LOG_LEVEL_INFO, f"固件版本: {version_str} (BCD: 0x{major_version:02X} 0x{minor_version:02X} 0x{revision_version:02X})", self.log_level)
                    QMessageBox.information(
                        self,
                        "固件版本",
                        f"固件版本: {version_str}\n\n"
                        f"主版本号: {major_version}\n"
                        f"次版本号: {minor_version}\n"
                        f"修订版本号: {revision_version}\n\n"
                        f"BCD格式: 0x{major_version:02X} 0x{minor_version:02X} 0x{revision_version:02X}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xA6响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xA6响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取固件版本时出错: {str(e)}", self.log_level)

    def on_browse_firmware(self):
        """浏览选择固件文件"""
        # 改动原因：打开文件对话框选择固件文件
        filename, _ = QFileDialog.getOpenFileName(
            self,
            "选择固件文件",
            "",
            "Firmware Files (*.bin *.xe);;All Files (*.*)"
        )
        if filename:
            self.firmware_file_edit.setText(filename)
            log_message(LOG_LEVEL_INFO, f"已选择固件文件: {filename}", self.log_level)

    def calculate_checksum(self, data):
        """计算checksum（参考user_func.c实现）"""
        # 改动原因：实现checksum算法，与设备端check_sum函数一致
        sum_value = 0
        for byte in data:
            sum_value += byte
        return sum_value % 256

    def debug_print_hex(self, data, prefix="", max_bytes=64):
        """以16进制形式打印数据包
        改动原因：添加调试打印功能，方便查看发送的命令和收到的响应
        参数:
            data: 要打印的数据（bytes或bytearray）
            prefix: 前缀字符串（如"发送"、"接收"）
            max_bytes: 最大打印字节数
        """
        if not data:
            log_message(LOG_LEVEL_DEBUG, f"{prefix}: (空数据)", self.log_level)
            return

        # 限制打印长度
        print_data = data[:max_bytes] if len(data) > max_bytes else data

        # 转换为16进制字符串
        hex_str = ' '.join([f'{b:02X}' for b in print_data])

        # 如果数据被截断，添加提示
        if len(data) > max_bytes:
            hex_str += f" ... (共{len(data)}字节，仅显示前{max_bytes}字节)"

        log_message(LOG_LEVEL_DEBUG, f"{prefix}: {hex_str}", self.log_level)

    def wait_for_hid_response(self, h, expected_cmd, timeout=180.0, max_retries=5000):
        """等待设备主动上报的HID响应
        改动原因：实现可靠的响应等待机制，确保每个命令发送后都等待设备响应后再继续
        参数:
            h: HID设备对象
            expected_cmd: 期望的命令码（如0xA7, 0xA8等）
            timeout: 超时时间（秒）
            max_retries: 最大重试次数
        返回:
            响应数据包（64字节），如果超时返回None
        """
        import time
        start_time = time.time()
        retry_count = 0

        while retry_count < max_retries:
            # 检查超时
            if time.time() - start_time > timeout:
                log_message(LOG_LEVEL_ERROR, f"等待命令0x{expected_cmd:02x}响应超时（{timeout}秒）", self.log_level)
                return None

            # 改动原因：使用中断端点读取主动上报数据，避免HID_GET_REPORT返回非预期数据
            # 使用hid.read读取中断IN端点数据（长度64，单位ms超时）
            reply = h.read(64, 50)

            if reply and len(reply) == 64:
                # 改动原因：添加调试打印，显示所有收到的HID报告（用于调试）
                if retry_count % 10 == 0:  # 每10次重试打印一次，避免输出过多
                    self.debug_print_hex(reply, f"[等待响应] 收到HID报告 (期望命令0x{expected_cmd:02x})", max_bytes=64)

                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77:
                    # 检查命令码
                    if reply[2] == expected_cmd:
                        log_message(LOG_LEVEL_DEBUG, f"收到命令0x{expected_cmd:02x}的响应", self.log_level)
                        return reply
                    else:
                        # 收到其他命令的响应，继续等待
                        log_message(LOG_LEVEL_DEBUG, f"收到其他命令0x{reply[2]:02x}的响应，继续等待0x{expected_cmd:02x}", self.log_level)

            # 短暂延迟后重试
            time.sleep(0.05)  # 50ms延迟
            retry_count += 1

        log_message(LOG_LEVEL_ERROR, f"等待命令0x{expected_cmd:02x}响应失败，已重试{max_retries}次", self.log_level)
        return None

    def on_erase_upgrade_image(self):
        """擦除现有升级镜像（发送FIRMWARE_UPGRADE_ERASE命令）"""
        # 改动原因：擦除Flash中的现有upgrade image，为新固件腾出空间
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        # 确认对话框
        reply = QMessageBox.question(
            self,
            "确认擦除",
            "确定要擦除现有的升级镜像吗？\n\n"
            "这将删除Flash中的upgrade image，\n"
            "但不会影响当前运行的固件。\n\n"
            "是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply != QMessageBox.Yes:
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建FIRMWARE_UPGRADE_ERASE命令数据包
            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = FIRMWARE_UPGRADE_ERASE  # 命令码

            log_message(LOG_LEVEL_INFO, "正在擦除现有升级镜像...", self.log_level)

            # 改动原因：添加调试打印，显示发送的ERASE命令
            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] ERASE命令 (FIRMWARE_UPGRADE_ERASE)", max_bytes=64)

            h.write(send_packet)

            # 改动原因：等待设备主动上报ERASE命令响应
            reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_ERASE, timeout=180.0)

            # 改动原因：添加调试打印，显示收到的ERASE响应
            if reply:
                self.debug_print_hex(reply, f"[接收] ERASE响应 (FIRMWARE_UPGRADE_ERASE)", max_bytes=64)
            if reply:
                status = reply[3]
                if status == 0x00:
                    log_message(LOG_LEVEL_INFO, "升级镜像擦除成功", self.log_level)
                    QMessageBox.information(self, "成功", "升级镜像已擦除")
                else:
                    log_message(LOG_LEVEL_ERROR, f"升级镜像擦除失败，状态码: 0x{status:02x}", self.log_level)
                    QMessageBox.warning(self, "失败", f"升级镜像擦除失败\n状态码: 0x{status:02x}")
            else:
                log_message(LOG_LEVEL_ERROR, "ERASE命令响应超时", self.log_level)
                QMessageBox.warning(self, "超时", "擦除命令响应超时")

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"擦除升级镜像时出错: {str(e)}", self.log_level)
            QMessageBox.critical(self, "错误", f"擦除升级镜像时出错:\n{str(e)}")

    def on_start_firmware_upgrade(self):
        """开始固件升级流程"""
        # 改动原因：实现HID固件升级的完整流程，包括START、循环DATA、END命令
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        firmware_file = self.firmware_file_edit.text()
        if not firmware_file or not os.path.exists(firmware_file):
            log_message(LOG_LEVEL_ERROR, "请选择有效的固件文件", self.log_level)
            QMessageBox.warning(self, "错误", "请选择有效的固件文件")
            return

        # 确认对话框
        reply = QMessageBox.question(
            self,
            "确认升级",
            f"确定要升级固件吗？\n\n"
            f"固件文件: {firmware_file}\n"
            f"警告：升级过程中请勿断电或拔出设备！\n\n"
            f"是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply != QMessageBox.Yes:
            return

        try:
            import time

            # 读取固件文件
            with open(firmware_file, 'rb') as f:
                firmware_data = f.read()

            file_size = len(firmware_data)
            # 页对齐到256字节
            page_size = 256
            file_size_padded = ((file_size + page_size - 1) // page_size) * page_size

            log_message(LOG_LEVEL_INFO, f"固件文件大小: {file_size} 字节", self.log_level)
            log_message(LOG_LEVEL_INFO, f"页对齐后大小: {file_size_padded} 字节", self.log_level)

            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 步骤1: 发送START命令 (FIRMWARE_UPGRADE_START)
            log_message(LOG_LEVEL_INFO, "发送START命令...", self.log_level)
            self.firmware_status_label.setText("状态: 初始化中...")
            QApplication.processEvents()

            # 改动原因：去掉固件版本号和升级标志位，所有实现都是强制升级方式
            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = FIRMWARE_UPGRADE_START  # 命令码
            # 固件大小（4字节小端序）
            data[2] = (file_size_padded >> 0) & 0xFF
            data[3] = (file_size_padded >> 8) & 0xFF
            data[4] = (file_size_padded >> 16) & 0xFF
            data[5] = (file_size_padded >> 24) & 0xFF
            # 其余字节为0（保留字节）

            # 改动原因：添加调试打印，显示发送的START命令
            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] START命令 (FIRMWARE_UPGRADE_START)", max_bytes=64)

            h.write(send_packet)

            # 改动原因：等待设备主动上报START命令响应，确保收到响应后再继续
            log_message(LOG_LEVEL_DEBUG, "等待START命令响应...", self.log_level)
            reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_START, timeout=180.0)

            # 改动原因：添加调试打印，显示收到的START响应
            if reply:
                self.debug_print_hex(reply, f"[接收] START响应 (0xA7)", max_bytes=64)
            if not reply:
                raise Exception("START命令响应超时或无效")

            status = reply[3]
            if status != 0x00:
                error_msgs = {
                    0x01: "初始化失败",
                    0x02: "Flash空间不足",
                    0x03: "固件大小无效"
                }
                raise Exception(f"START命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}")

            # 改动原因：修正总块数读取位置，根据协议响应格式：
            # reply[0]=Report ID, reply[1]=0x77, reply[2]=FIRMWARE_UPGRADE_START, reply[3]=状态码
            # reply[4-7]=aligned_size(4字节), reply[8-9]=total_blocks(2字节)
            total_blocks = reply[8] | (reply[9] << 8)
            log_message(LOG_LEVEL_INFO, f"升级初始化成功，总块数: {total_blocks}", self.log_level)

            # 步骤2: 循环发送DATA命令
            # 改动原因：HID数据包只有63字节（不包括Report ID），需要为Checksum留出空间
            # 数据包格式：同步头(1) + 命令码(1) + 块序号(2) + 数据长度(1) + 数据(N) + Checksum(1)
            # 所以最大数据长度 = 63 - 5(头部) - 1(Checksum) = 57字节
            max_data_block_size = 57  # 最大数据块大小（考虑Checksum空间）
            block_number = 0
            sent_bytes = 0

            self.firmware_status_label.setText("状态: 传输中...")
            QApplication.processEvents()

            while sent_bytes < file_size_padded:
                # 改动原因：数据长度固定为57字节（最后一个块可能不足57字节，用0填充）
                remaining = file_size_padded - sent_bytes
                # 数据长度固定为57字节
                data_len = max_data_block_size  # 固定57字节

                # 读取实际数据（如果还有数据的话）
                if sent_bytes < file_size:
                    actual_read_len = min(data_len, file_size - sent_bytes)
                    block_data = firmware_data[sent_bytes:sent_bytes + actual_read_len]
                    # 如果实际读取的数据不足57字节，用0填充到57字节
                    if actual_read_len < data_len:
                        block_data += bytes(data_len - actual_read_len)  # 填充0
                else:
                    # 如果已经超过实际文件大小，全部填充0
                    block_data = bytes(data_len)  # 全部填充0

                # 构建DATA命令数据包
                data = bytearray(63)
                data[0] = 0x77  # 同步头
                data[1] = FIRMWARE_UPGRADE_DATA  # 命令码
                data[2] = (block_number >> 0) & 0xFF  # 块序号（小端序）
                data[3] = (block_number >> 8) & 0xFF
                data[4] = data_len  # 数据长度
                data[5:5+data_len] = block_data  # 固件数据

                # 计算Checksum（同步头+命令码+块序号+数据长度+数据）
                checksum_data = data[0:5+data_len]
                checksum = self.calculate_checksum(checksum_data)
                data[5+data_len] = checksum  # Checksum位置：5 + data_len（最大62，不会越界）

                # 改动原因：添加调试打印，显示发送的DATA命令（每10个块打印一次，避免输出过多）
                send_packet = [0x01] + list(data)
                if block_number % 10 == 0 or block_number < 3:
                    self.debug_print_hex(send_packet, f"[发送] DATA命令 (FIRMWARE_UPGRADE_DATA) 块{block_number}", max_bytes=64)

                # 发送DATA命令
                h.write(send_packet)

                # 改动原因：等待设备主动上报DATA命令响应，确保收到响应后再发送下一个数据块
                reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_DATA, timeout=180.0)

                # 改动原因：添加调试打印，显示收到的DATA响应（每10个块打印一次）
                if reply and (block_number % 10 == 0 or block_number < 3):
                    self.debug_print_hex(reply, f"[接收] DATA响应 (FIRMWARE_UPGRADE_DATA) 块{block_number}", max_bytes=64)
                if not reply:
                    raise Exception(f"DATA命令响应超时（块序号: {block_number}）")

                # 改动原因：响应格式已简化，只包含状态码和已确认的块序号，删除了已写入页数和总页数
                # 验证响应
                status = reply[3]
                confirmed_block = reply[4] | (reply[5] << 8)

                if status != 0x00:
                    error_msgs = {
                        0x01: "写入Flash失败",
                        0x02: f"块序号错误（期望: {confirmed_block}）",
                        0x03: "Checksum校验失败",
                        0x04: "数据长度错误（必须为57字节）"
                    }
                    raise Exception(f"DATA命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}")

                if confirmed_block != block_number:
                    raise Exception(f"块序号不匹配（发送: {block_number}, 确认: {confirmed_block}）")

                log_message(LOG_LEVEL_DEBUG, f"块 {block_number} 传输成功", self.log_level)

                # 更新进度
                sent_bytes += data_len
                block_number += 1
                progress = int((sent_bytes / file_size_padded) * 100)
                self.firmware_progress.setValue(progress)
                self.firmware_status_label.setText(f"状态: 传输中 ({block_number}/{total_blocks})")
                QApplication.processEvents()

                if block_number % 10 == 0:
                    log_message(LOG_LEVEL_INFO, f"已发送 {block_number}/{total_blocks} 块", self.log_level)

            log_message(LOG_LEVEL_INFO, "数据传输完成", self.log_level)

            # 步骤3: 发送END命令
            self.firmware_status_label.setText("状态: 完成中...")
            QApplication.processEvents()

            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = FIRMWARE_UPGRADE_END  # 命令码
            # 固件总大小（4字节小端序）
            data[2] = (file_size_padded >> 0) & 0xFF
            data[3] = (file_size_padded >> 8) & 0xFF
            data[4] = (file_size_padded >> 16) & 0xFF
            data[5] = (file_size_padded >> 24) & 0xFF

            log_message(LOG_LEVEL_INFO, "发送END命令...", self.log_level)

            # 改动原因：添加调试打印，显示发送的END命令
            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] END命令 (FIRMWARE_UPGRADE_END)", max_bytes=64)

            h.write(send_packet)

            # 改动原因：等待设备主动上报END命令响应，END命令需要更长时间（写入最后一页并验证）
            log_message(LOG_LEVEL_DEBUG, "等待END命令响应（可能需要较长时间）...", self.log_level)
            reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_END, timeout=180.0)  # END命令需要更长时间

            # 改动原因：添加调试打印，显示收到的END响应
            if reply:
                self.debug_print_hex(reply, f"[接收] END响应 (FIRMWARE_UPGRADE_END)", max_bytes=64)
            if not reply:
                raise Exception("END命令响应超时或无效")

            # 改动原因：响应格式已简化，只包含状态码，删除了已写入总块数和总页数
            status = reply[3]
            if status != 0x00:
                error_msgs = {
                    0x01: "写入失败",
                    0x02: "数据不完整",
                    0x03: "镜像验证失败",
                    0x04: "版本号无效"
                }
                raise Exception(f"END命令失败: {error_msgs.get(status, f'状态码0x{status:02x}')}")

            log_message(LOG_LEVEL_INFO, "升级完成", self.log_level)

            h.close()

            # 更新UI
            self.firmware_progress.setValue(100)
            self.firmware_status_label.setText("状态: 升级完成")

            QMessageBox.information(
                self,
                "升级成功",
                f"固件升级完成！\n\n"
                f"请点击\"重启设备\"按钮重启设备以加载新固件。"
            )

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"固件升级失败: {str(e)}", self.log_level)
            self.firmware_progress.setValue(0)
            self.firmware_status_label.setText("状态: 升级失败")
            QMessageBox.critical(self, "升级失败", f"固件升级失败:\n{str(e)}")

    def on_verify_firmware_image(self):
        """验证固件镜像（发送0xAD命令）"""
        # 改动原因：验证Flash中已写入的固件镜像完整性
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xAD命令数据包
            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = 0xAD  # 命令码 (FIRMWARE_UPGRADE_VERIFY)
            # 固件大小（0表示不校验大小）
            data[2] = 0x00
            data[3] = 0x00
            data[4] = 0x00
            data[5] = 0x00

            log_message(LOG_LEVEL_INFO, "正在验证固件镜像...", self.log_level)

            # 改动原因：添加调试打印，显示发送的VERIFY命令
            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] VERIFY命令 (0xAD)", max_bytes=64)

            h.write(send_packet)

            # 改动原因：等待设备主动上报VERIFY命令响应
            reply = self.wait_for_hid_response(h, 0xAD, timeout=180.0)

            # 改动原因：添加调试打印，显示收到的VERIFY响应
            if reply:
                self.debug_print_hex(reply, f"[接收] VERIFY响应 (0xAD)", max_bytes=64)
                verify_result = reply[3]
                image_size = reply[4] | (reply[5] << 8) | (reply[6] << 16) | (reply[7] << 24)
                ver_major = reply[13]
                ver_minor = reply[14]
                ver_patch = reply[15]

                if verify_result == 0x00:
                    log_message(LOG_LEVEL_INFO, "固件镜像验证通过", self.log_level)
                    QMessageBox.information(
                        self,
                        "验证成功",
                        f"固件镜像验证通过！\n\n"
                        f"镜像大小: {image_size} 字节\n"
                        f"镜像版本: {ver_major}.{ver_minor}.{ver_patch}"
                    )
                else:
                    error_msgs = {
                        0x01: "镜像验证失败",
                        0x02: "版本号无效",
                        0x03: "大小不匹配"
                    }
                    log_message(LOG_LEVEL_ERROR, f"固件镜像验证失败: {error_msgs.get(verify_result, f'状态码0x{verify_result:02x}')}", self.log_level)
                    QMessageBox.warning(
                        self,
                        "验证失败",
                        f"固件镜像验证失败\n\n{error_msgs.get(verify_result, f'状态码0x{verify_result:02x}')}"
                    )

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"验证固件镜像时出错: {str(e)}", self.log_level)
            QMessageBox.critical(self, "错误", f"验证固件镜像时出错:\n{str(e)}")

    def on_reboot_device(self):
        """重启设备（发送DEVICE_REBOOT命令）"""
        # 改动原因：发送重启命令，设备会重启并加载新的upgrade image
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        # 确认对话框
        reply = QMessageBox.question(
            self,
            "确认重启",
            "确定要重启设备吗？\n\n"
            "设备将重启并加载新的固件\n"
            "（如果upgrade image有效）。\n\n"
            "是否继续？",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )

        if reply != QMessageBox.Yes:
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 改动原因：将固件升级专用的REBOOT命令改为通用的设备重启命令（DEVICE_REBOOT）
            # 构建DEVICE_REBOOT命令数据包（通用设备重启命令）
            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = DEVICE_REBOOT  # 命令码

            log_message(LOG_LEVEL_INFO, "正在重启设备...", self.log_level)

            # 改动原因：添加调试打印，显示发送的DEVICE_REBOOT命令
            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] DEVICE_REBOOT命令 (DEVICE_REBOOT)", max_bytes=64)

            h.write(send_packet)

            # 改动原因：DEVICE_REBOOT不需要返回，发送后设备立即重启
            # 注意：设备不会返回响应，主机不等待

            h.close()

            QMessageBox.information(
                self,
                "重启设备",
                "重启命令已发送\n\n"
                "设备将在几秒内重启。\n"
                "请等待设备重新连接。"
            )

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"重启设备时出错: {str(e)}", self.log_level)
            QMessageBox.critical(self, "错误", f"重启设备时出错:\n{str(e)}")

    def on_get_firmware_upgrade_status(self):
        """查询固件升级状态（发送FIRMWARE_UPGRADE_STATUS命令）"""
        # 改动原因：支持查询升级状态，便于诊断升级进度和错误
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = FIRMWARE_UPGRADE_STATUS  # 命令码

            log_message(LOG_LEVEL_INFO, "正在查询升级状态...", self.log_level)

            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] STATUS命令 (FIRMWARE_UPGRADE_STATUS)", max_bytes=64)

            h.write(send_packet)

            reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_STATUS, timeout=180.0)
            if reply:
                self.debug_print_hex(reply, f"[接收] STATUS响应 (FIRMWARE_UPGRADE_STATUS)", max_bytes=64)
            if not reply:
                raise Exception("STATUS命令响应超时或无效")

            # 响应解析（Report ID + 同步头 + 命令码 + payload）
            state = reply[3]
            received_blocks = reply[4] | (reply[5] << 8)
            total_blocks = reply[6] | (reply[7] << 8)
            written_pages = reply[8] | (reply[9] << 8)
            total_pages = reply[10] | (reply[11] << 8)
            bytes_transferred = reply[12] | (reply[13] << 8) | (reply[14] << 16) | (reply[15] << 24)
            total_bytes = reply[16] | (reply[17] << 8) | (reply[18] << 16) | (reply[19] << 24)
            error_code = reply[20]

            state_map = {
                0x00: "IDLE",
                0x01: "PREPARING",
                0x02: "TRANSFERRING",
                0x03: "COMPLETING",
                0x04: "COMPLETED",
                0x05: "ERROR",
                0x06: "ABORTED"
            }

            msg = (
                f"状态: {state_map.get(state, f'0x{state:02x}')}\n"
                f"块进度: {received_blocks}/{total_blocks}\n"
                f"页进度: {written_pages}/{total_pages}\n"
                f"字节进度: {bytes_transferred}/{total_bytes}\n"
                f"错误码: 0x{error_code:02x}"
            )
            log_message(LOG_LEVEL_INFO, f"升级状态: {msg}", self.log_level)
            QMessageBox.information(self, "升级状态", msg)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"查询升级状态时出错: {str(e)}", self.log_level)
            QMessageBox.critical(self, "错误", f"查询升级状态时出错:\n{str(e)}")

    def on_abort_firmware_upgrade(self):
        """中止固件升级（发送FIRMWARE_UPGRADE_ABORT命令）"""
        # 改动原因：支持中止升级流程，并可选择是否擦除已写入数据
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            QMessageBox.warning(self, "错误", "请先选择设备")
            return

        erase_reply = QMessageBox.question(
            self,
            "中止升级",
            "是否擦除已写入的数据？\n\n"
            "选择“是”：擦除已写入数据\n"
            "选择“否”：保留已写入数据",
            QMessageBox.Yes | QMessageBox.No,
            QMessageBox.No
        )
        erase_flag = 0x01 if erase_reply == QMessageBox.Yes else 0x00

        try:
            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            data = bytearray(63)
            data[0] = 0x77  # 同步头
            data[1] = FIRMWARE_UPGRADE_ABORT  # 命令码
            data[2] = erase_flag  # 清理标志

            log_message(LOG_LEVEL_INFO, "正在中止升级...", self.log_level)

            send_packet = [0x01] + list(data)
            self.debug_print_hex(send_packet, f"[发送] ABORT命令 (FIRMWARE_UPGRADE_ABORT)", max_bytes=64)

            h.write(send_packet)

            reply = self.wait_for_hid_response(h, FIRMWARE_UPGRADE_ABORT, timeout=180.0)
            if reply:
                self.debug_print_hex(reply, f"[接收] ABORT响应 (FIRMWARE_UPGRADE_ABORT)", max_bytes=64)
            if not reply:
                raise Exception("ABORT命令响应超时或无效")

            status = reply[3]
            if status == 0x00:
                log_message(LOG_LEVEL_INFO, "升级已中止", self.log_level)
                QMessageBox.information(self, "成功", "升级已中止")
            else:
                log_message(LOG_LEVEL_ERROR, f"中止升级失败，状态码: 0x{status:02x}", self.log_level)
                QMessageBox.warning(self, "失败", f"中止升级失败\n状态码: 0x{status:02x}")

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"中止升级时出错: {str(e)}", self.log_level)
            QMessageBox.critical(self, "错误", f"中止升级时出错:\n{str(e)}")


    def on_send_ex3d_cmd(self):
        """发送EX3D SET命令（0xB0命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            # 获取命令码和类型
            base_cmd = self.ex3d_cmd_combo.currentData()
            cmd_type = self.ex3d_cmd_type_combo.currentData()

            # 构建EX3D命令码：SET命令清除第9位，GET命令设置第9位
            if cmd_type == "SET":
                ex3d_cmd = base_cmd & ~0x0100  # 清除第9位
            else:
                ex3d_cmd = base_cmd | 0x0100   # 设置第9位
                # GET命令应该使用读取函数
                self.on_get_ex3d_cmd()
                return

            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xB0数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xB0  # 命令码 (SET_EX3D_CMD)

            # EX3D命令码 (4字节，小端序)
            data[2] = (ex3d_cmd >> 0) & 0xFF
            data[3] = (ex3d_cmd >> 8) & 0xFF
            data[4] = (ex3d_cmd >> 16) & 0xFF
            data[5] = (ex3d_cmd >> 24) & 0xFF

            # 参数（最多14个uint32，这里使用前3个）
            param1 = self.ex3d_param1.value()
            param2 = self.ex3d_param2.value()
            param3 = self.ex3d_param3.value()

            # 参数1 (4字节，小端序)
            data[6] = (param1 >> 0) & 0xFF
            data[7] = (param1 >> 8) & 0xFF
            data[8] = (param1 >> 16) & 0xFF
            data[9] = (param1 >> 24) & 0xFF

            # 参数2 (4字节，小端序)
            data[10] = (param2 >> 0) & 0xFF
            data[11] = (param2 >> 8) & 0xFF
            data[12] = (param2 >> 16) & 0xFF
            data[13] = (param2 >> 24) & 0xFF

            # 参数3 (4字节，小端序)
            data[14] = (param3 >> 0) & 0xFF
            data[15] = (param3 >> 8) & 0xFF
            data[16] = (param3 >> 16) & 0xFF
            data[17] = (param3 >> 24) & 0xFF

            # 发送请求
            log_message(LOG_LEVEL_INFO, f"正在发送EX3D SET命令: 0x{ex3d_cmd:04X}...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xB0:
                    # 解析状态码
                    status = reply[3]
                    # 解析回显的命令码
                    echo_cmd = reply[4] | (reply[5] << 8) | (reply[6] << 16) | (reply[7] << 24)
                    # 解析返回值
                    return_value = reply[8] | (reply[9] << 8) | (reply[10] << 16) | (reply[11] << 24)

                    if status == 0x00:
                        log_message(LOG_LEVEL_INFO, f"EX3D SET命令执行成功: 0x{echo_cmd:04X}, 返回值: 0x{return_value:08X}", self.log_level)
                        QMessageBox.information(
                            self,
                            "成功",
                            f"EX3D SET命令执行成功\n\n"
                            f"命令码: 0x{echo_cmd:04X}\n"
                            f"返回值: 0x{return_value:08X}"
                        )
                    else:
                        log_message(LOG_LEVEL_ERROR, f"EX3D SET命令执行失败: 0x{echo_cmd:04X}, 返回值: 0x{return_value:08X}", self.log_level)
                        QMessageBox.warning(
                            self,
                            "失败",
                            f"EX3D SET命令执行失败\n\n"
                            f"命令码: 0x{echo_cmd:04X}\n"
                            f"返回值: 0x{return_value:08X}"
                        )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xB0响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xB0响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"发送EX3D SET命令时出错: {str(e)}", self.log_level)

    def on_get_ex3d_cmd(self):
        """读取EX3D GET命令（0xB1命令）"""
        if self.device_combo.currentData() is None:
            log_message(LOG_LEVEL_ERROR, "请先选择设备", self.log_level)
            return

        try:
            # 获取命令码
            base_cmd = self.ex3d_cmd_combo.currentData()
            cmd_type = self.ex3d_cmd_type_combo.currentData()

            # 构建EX3D命令码：GET命令设置第9位
            if cmd_type == "GET":
                ex3d_cmd = base_cmd | 0x0100   # 设置第9位
            else:
                # SET命令应该使用发送函数，但这里允许GET模式下读取
                ex3d_cmd = base_cmd | 0x0100

            device_info = self.device_combo.currentData()
            h = hid.device()
            h.open(device_info['vendor_id'], device_info['product_id'])
            h.set_nonblocking(1)

            # 构建0xB1数据包
            data = bytearray(63)  # 63字节数据包
            data[0] = 0x77  # 同步头1
            data[1] = 0xB1  # 命令码 (GET_EX3D_CMD)

            # EX3D命令码 (4字节，小端序)
            data[2] = (ex3d_cmd >> 0) & 0xFF
            data[3] = (ex3d_cmd >> 8) & 0xFF
            data[4] = (ex3d_cmd >> 16) & 0xFF
            data[5] = (ex3d_cmd >> 24) & 0xFF

            # 参数（某些GET命令需要参数，如通道号、索引等）
            param1 = self.ex3d_param1.value()

            # 参数1 (4字节，小端序)
            data[6] = (param1 >> 0) & 0xFF
            data[7] = (param1 >> 8) & 0xFF
            data[8] = (param1 >> 16) & 0xFF
            data[9] = (param1 >> 24) & 0xFF

            # 发送请求
            log_message(LOG_LEVEL_INFO, f"正在读取EX3D GET命令: 0x{ex3d_cmd:04X}...", self.log_level)
            h.write([0x01] + list(data))

            # 读取响应
            import time
            time.sleep(0.05)  # 等待设备响应
            reply = h.get_input_report(0x1, 64)  # 64字节响应
            if reply and len(reply) == 64:
                # 检查Report ID和同步头
                if reply[0] == 0x01 and reply[1] == 0x77 and reply[2] == 0xB1:
                    # 解析回显的命令码（位置3-6，4字节小端序）
                    echo_cmd = reply[3] | (reply[4] << 8) | (reply[5] << 16) | (reply[6] << 24)

                    # 解析返回值（从reply[7]开始，最多14个uint32值）
                    return_values = []
                    for i in range(0, 56, 4):  # 最多14个uint32值
                        idx = 7 + i
                        if idx + 3 < 64:
                            val = reply[idx] | (reply[idx + 1] << 8) | (reply[idx + 2] << 16) | (reply[idx + 3] << 24)
                            return_values.append(val)
                            if val == 0 and i > 16:  # 遇到连续0值可能表示数据结束
                                break

                    log_message(LOG_LEVEL_INFO, f"EX3D GET命令响应: 0x{echo_cmd:04X}, 返回值: {return_values}", self.log_level)

                    # 格式化返回值显示
                    values_str = ", ".join([f"0x{val:08X}" for val in return_values[:4]])  # 只显示前4个值
                    if len(return_values) > 4:
                        values_str += f" ... (共{len(return_values)}个值)"

                    QMessageBox.information(
                        self,
                        "EX3D GET命令响应",
                        f"命令码: 0x{echo_cmd:04X}\n\n"
                        f"返回值:\n{values_str}"
                    )
                else:
                    log_message(LOG_LEVEL_ERROR, f"无效的0xB1响应同步头: 0x{reply[1]:02x} 0x{reply[2]:02x}", self.log_level)
            else:
                log_message(LOG_LEVEL_ERROR, "未收到0xB1响应数据", self.log_level)

            h.close()

        except Exception as e:
            log_message(LOG_LEVEL_ERROR, f"读取EX3D GET命令时出错: {str(e)}", self.log_level)

if __name__ == "__main__":
    app = QApplication([])
    window = EQDesigner()
    window.show()
    app.exec()
