Python+PyQt5 UOS环境实现蓝牙测试

import sys
import threading
import subprocess
import re
import json
import os
import logging
import configparser
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import QWidget, QApplication
from PyQt5.QtCore import pyqtSignal, QCoreApplication

class BluetoothTestWinFrm(QWidget):
    exit_signal = pyqtSignal(int)

    def __init__(self):
        super().__init__()
        self.setupUi()
        self.exit_signal.connect(self.close_application)

        self.testargs = []  # 测试参数信息
        self.readhardware = []  # 读取的硬件信息
        self.devices_info = []  # 硬件设备信息
        self.itemName = ''  # 项目名称
        self.testStandardArgs = ''  # 测试标准参数
        self.config = None  # 配置
        self.logger = None  # 日志
        self.target_device_name = ''  # 蓝牙连接点

        self.loggerrinfo()  # 日志信息
        self.getitemname()  # 获取项目名称
        self.readjsontestargs(self.itemName)  # 读取配置参数

        # 启动蓝牙测试线程
        self.bluetooth_thread = threading.Thread(target=self.run_bluetooth_tests)
        self.bluetooth_thread.start()

    def run_bluetooth_tests(self):
        try:
            if self.open_bluetooth():
                if self.scan_bluetooth():
                    if self.get_local_bluetooth_mac():
                        self.updatejsontestargs(self.itemName, self.readhardware, 'PASS')
                        self.exit_signal.emit(0)
                        return
            self.updatejsontestargs(self.itemName, self.readhardware, 'FAIL')
            self.exit_signal.emit(1)
        except Exception as e:
            self.showlog(f"Error during Bluetooth tests: {e}", False)
            self.exit_signal.emit(1)

    def close_application(self, exit_code):
        QCoreApplication.exit(exit_code)

    def open_bluetooth(self):
        """
        打开蓝牙设备
        """
        self.update_showinfo("蓝牙已开启..", "yellow")
        try:
            result = subprocess.run(['hciconfig', 'hci0', 'up'], capture_output=True, text=True, check=True)
            if result.returncode == 0:
                self.update_ui("蓝牙已开启..", "PASS", "PASS")
                self.readhardware.append({"open_bluetooth": "PASS"})
                return True
            else:
                self.update_ui("开启蓝牙失败", "FAIL", "FAIL")
                return False
        except subprocess.CalledProcessError as e:
            self.update_ui(f"开启蓝牙失败: {e}", "FAIL", "FAIL")
            return False

    def scan_bluetooth(self):
        """
        扫描蓝牙信息
        """
        self.update_showinfo("蓝牙配对与通讯..", "yellow")
        try:
            subprocess.run(['hciconfig', 'hci0', 'up'], check=True)  # 确保蓝牙设备已开启
            result = subprocess.run(['hcitool', 'scan'], capture_output=True, text=True, check=True)

            devices = self.parse_scan_output(result.stdout)
            print("发现的蓝牙设备:")
            for dev in devices:
                print(f"设备 {dev['address']} ({dev['name']})")

            self.devices_info = devices
            if self.devices_info:
                self.update_ui("", "", "PASS")
                self.readhardware.append({"scan_bluetooth": "PASS"})
                return True
            else:
                self.update_ui("", "", "FAIL")
                return False
        except subprocess.CalledProcessError as e:
            self.update_ui(f"扫描蓝牙设备失败: {e}", "", "FAIL")
            return False

    def parse_scan_output(self, output):
        """
        解析蓝牙扫描输出
        """
        devices = []
        lines = output.split('\n')
        for line in lines:
            match = re.search(r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})', line)
            if match:
                address = match.group(0)
                name = line.split(address)[-1].strip()
                devices.append({'address': address, 'name': name})
        return devices

    def get_local_bluetooth_mac(self):
        """
        读取本地蓝牙MAC地址
        """
        try:
            self.update_showinfo("读取MAC信息..", "yellow")
            result = subprocess.run(['bluetoothctl', 'list'], capture_output=True, text=True, check=True)
            print("Local Bluetooth MAC Address:")
            mac_address = self.extract_mac_address(result.stdout)
            if mac_address:
                print(mac_address)
                self.readhardware.append({"mac_address": mac_address})
                self.update_mac_ui(mac_address, "PASS")
                return True
            else:
                self.update_mac_ui("NULL", "FAIL")
                return False
        except (subprocess.CalledProcessError, ValueError) as e:
            self.update_mac_ui("Err", "FAIL")
            return False

    def extract_mac_address(self, text):
        """
        提取并格式化MAC地址
        """
        match = re.search(r'([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})', text)
        if match:
            mac_address = match.group(0)
            mac_address = mac_address.replace(':', '').replace('-', '')
            return mac_address
        return None

    def update_ui(self, info_text, connection_status, communication_status):
        """
        更新UI信息
        """
        self.lbl_showinfo.setText(info_text)
        if connection_status == "PASS":
            self.lbl_connection_status.setText(connection_status)
            self.lbl_connection_status.setStyleSheet("color: green;")
            self.lbl_hardwaretest.setText(connection_status)
            self.lbl_hardwaretest.setStyleSheet("color: green;")
        elif connection_status == "FAIL":
            self.lbl_connection_status.setText(connection_status)
            self.lbl_connection_status.setStyleSheet("color: red;")
            self.lbl_hardwaretest.setText(connection_status)
            self.lbl_hardwaretest.setStyleSheet("color: red;")

        if communication_status == "PASS":
            self.lbl_communication.setText(communication_status)
            self.lbl_communication.setStyleSheet("color: green;")
        elif communication_status == "FAIL":
            self.lbl_communication.setText(communication_status)
            self.lbl_communication.setStyleSheet("color: red;")

    def update_mac_ui(self, mac_address, status):
        """
        更新MAC地址UI信息
        """
        self.lbl_bt_device.setText(mac_address)
        self.lbl_bt_device.setStyleSheet("color: green;" if status == "PASS" else "color: red;")

    def update_showinfo(self, text, color):
        """
        更新显示信息颜色
        """
        self.lbl_showinfo.setText(text)
        self.lbl_showinfo.setStyleSheet(f"color: {color};")

    #保存硬件信息成json文件
    def save_info_to_json(self,info, filename='local_bluetooth_info.json'):
        try:
            if info:
                with open(filename, 'w') as file:
                    json.dump(info, file, indent=4)
                self.showlog(f"Local Bluetooth information saved to {filename}.",True)
                return True
            else:
                self.showlog("No information to save.",False)
                return False
        except Exception as e:
            self.showlog("save info to json Err:" + str(e), False)
            return False

    # 使用hciconfig命令获取本地蓝牙设备信息
    def get_local_bluetooth_info(self):
        try:
            # 使用hciconfig命令获取本地蓝牙设备信息
            result = subprocess.run(['hciconfig'], capture_output=True, text=True)
            if result.returncode != 0:
                print("Failed to fetch Bluetooth configuration.")
                return None

            #devices_info = []
            current_device = {}

            # 解析hciconfig的输出
            for line in result.stdout.split('\n'):
                if line:
                    if '\t' not in line:  # 新设备的开始
                        if current_device:  # 保存前一个设备
                            self.devices_info.append(current_device)
                        current_device = {'name': line.split(':')[0]}
                    else:  # 设备详细信息行
                        key, value = line.strip().split(': ', 1)
                        current_device[key.replace(' ', '_').lower()] = value

            if current_device:  # 保存最后一个设备
                self.devices_info.append(current_device)

            return len(self.devices_info)>0
        except Exception as e:
            self.showlog("get local bluetooth info Err:" + str(e), False)
            return False

    # 截取测试参数
    # argsvalues:参数值
    def intercept_test_parameters(self, argsvalues):
        try:
            testargs = argsvalues.strip()  # 删除参数字符串前后的空格

            # 解析蓝牙连接点名称
            if '=' in testargs:  # 确保字符串中包含 '='
                self.target_device_name = testargs.split('=')[1].strip()  # 拆分字符串并去除结果字符串的前后空格
            else:
                self.showlog("Invalid input format. Expected 'key=value'.",False)
                return False

            self.lbl_bt_device.setText(self.target_device_name)
            print(self.lbl_bt_device)
            # self.showlog(self.testargs,True)
            self.lbl_showinfo.setText('读取测试硬件Bluetooth配置信息完成!!')
            return True
        except Exception as e:
            self.showlog("intercept test parameters Err:" + str(e), False)
            sys.exit(1)

    # 更新测试参数json,itemName:项目名称,readValue:读取值,testResult:测试结果
    def updatejsontestargs(self, itemName, readValue, testResult):
        try:
            self.testArgs = self.readjsoninfo('./Conf/TestArgs.json')
            if self.testArgs is None:
                raise Exception("Failed to read configuration, aborting update.")
                return False

            for js in self.testArgs:
                if itemName in js['ItemScript']:
                    js['Read'] = readValue
                    js['TestResult'] = testResult

            with open("./Conf/TestArgs.json", "w") as write_file:
                json.dump(self.testArgs, write_file, indent=4)  # Use indent for pretty printing

            return True
        except Exception as e:
            self.showlog(f"Update TestArgs.json ItemName: {itemName} Info Err: {str(e)}", False)
            return False

    # 读取json信息,fileName:文件名称
    def readjsoninfo(self, fileName):
        try:
            if os.path.exists(fileName):
                f = open(fileName, 'r', encoding='utf-8')
            return json.loads(f.read())
        except Exception as e:
            self.showlog("Read " + fileName + " Err:" + str(e), False)
            sys.exit(1)

    # 读取项目参数信息
    def readjsontestargs(self, itemName):
        try:
            readresult = False
            # 检查文件是否存在并读取 JSON 内容
            if os.path.exists('./Conf/TestArgs.json'):
                with open('./Conf/TestArgs.json', 'r', encoding='utf-8') as f:
                    self.testArgs = json.loads(f.read())
                    readresult = True
            else:
                self.showlog("Not Find ./Conf/TestArgs.json File", False)
                sys.exit(1)
            if readresult:
                # 查找匹配的项目参数信息
                for js in self.testArgs:
                    # print(itemName, js['ItemScript'])
                    if itemName in js['ItemScript']:
                        self.testStandardArgs = js['Standard']
                        return True
            self.showlog(f"Read ./Conf/TestArgs.json ItemName:{self.itemName} Info is Empty!!", False)
            sys.exit(1)
        except Exception as e:
            self.showlog("Read ./Conf/TestArgs.json or ItemName: " + itemName + " Info Err:" + str(e), False)
            sys.exit(1)

    # 获取项目名称
    def getitemname(self):
        try:
            # 获取当前执行文件的完整路径
            full_path = sys.argv[0]
            # 从完整路径中提取文件名
            file_name = os.path.basename(full_path)
            self.itemName = file_name  # 项目名称
            return True
        except Exception as e:
            self.showlog(f'Get Item Name Err:{str(e)}', False)
            sys.exit(1)

    # 日志信息
    def loggerrinfo(self):
        try:
            # 确保日志文件的目录存在
            log_directory = './log'
            if not os.path.exists(log_directory):
                os.makedirs(log_directory)
            self.config = configparser.ConfigParser()  # 创建配置解析器对象
            # 生成日志信息
            self.logger = logging.getLogger('my_logger')  # 创建日志记录器
            self.logger.setLevel(logging.DEBUG)  # 设置日志级别为DEBUG
            file_handler = logging.FileHandler(f'{log_directory}/log.txt', encoding='utf-8')  # 使用UTF-8编码创建文件处理器
            formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')  # 创建格式化器
            file_handler.setFormatter(formatter)  # 将格式化器设置到文件处理器
            self.logger.addHandler(file_handler)  # 将文件处理器添加到日志记录器
            return True
        except Exception as e:
            self.showlog(f'Create logger_info Error: {str(e)}', False)
            sys.exit(1)

    #窗体初始化
    def setupUi(self):
        self.setObjectName("Form")
        self.resize(771, 197)

        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("Conf/ICON/BluetoothTestWinFrm.png"), QtGui.QIcon.Normal, QtGui.QIcon.Off)
        self.setWindowIcon(icon)

        self.gridLayout = QtWidgets.QGridLayout(self)
        self.gridLayout.setObjectName("gridLayout")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout()
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.lbl_logo = QtWidgets.QLabel(self)
        self.lbl_logo.setText("")
        self.lbl_logo.setPixmap(QtGui.QPixmap("IMAGE/logo.jpg"))
        self.lbl_logo.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_logo.setObjectName("lbl_logo")
        self.verticalLayout_2.addWidget(self.lbl_logo)
        self.lbl_Title = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(20)
        font.setBold(True)
        font.setWeight(75)
        self.lbl_Title.setFont(font)
        self.lbl_Title.setStyleSheet("background-color: rgb(85, 255, 127);\n"
"color: rgb(85, 85, 127);")
        self.lbl_Title.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_Title.setObjectName("lbl_Title")
        self.verticalLayout_2.addWidget(self.lbl_Title)
        self.horizontalLayout.addLayout(self.verticalLayout_2)
        self.verticalLayout_3 = QtWidgets.QVBoxLayout()
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.label_3 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.label_3.setFont(font)
        self.label_3.setStyleSheet("color: rgb(85, 85, 127);")
        self.label_3.setAlignment(QtCore.Qt.AlignCenter)
        self.label_3.setObjectName("label_3")
        self.verticalLayout_3.addWidget(self.label_3)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.label_2 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_2.setFont(font)
        self.label_2.setStyleSheet("background-color: rgb(255, 170, 127);")
        self.label_2.setAlignment(QtCore.Qt.AlignCenter)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout_4.addWidget(self.label_2)
        self.verticalLayout_3.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.lbl_bt_device = QtWidgets.QLabel(self)
        self.lbl_bt_device.setStyleSheet("color: rgb(170, 170, 127);")
        self.lbl_bt_device.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_bt_device.setObjectName("lbl_bt_device")
        self.horizontalLayout_3.addWidget(self.lbl_bt_device)
        self.verticalLayout_3.addLayout(self.horizontalLayout_3)
        self.horizontalLayout.addLayout(self.verticalLayout_3)
        self.verticalLayout_4 = QtWidgets.QVBoxLayout()
        self.verticalLayout_4.setObjectName("verticalLayout_4")
        self.label_4 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.label_4.setFont(font)
        self.label_4.setStyleSheet("color: rgb(85, 85, 127);")
        self.label_4.setAlignment(QtCore.Qt.AlignCenter)
        self.label_4.setObjectName("label_4")
        self.verticalLayout_4.addWidget(self.label_4)
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.label_6 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_6.setFont(font)
        self.label_6.setStyleSheet("background-color: rgb(85, 170, 127);")
        self.label_6.setAlignment(QtCore.Qt.AlignCenter)
        self.label_6.setObjectName("label_6")
        self.horizontalLayout_6.addWidget(self.label_6)
        self.label_11 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_11.setFont(font)
        self.label_11.setStyleSheet("background-color: rgb(85, 170, 127);")
        self.label_11.setAlignment(QtCore.Qt.AlignCenter)
        self.label_11.setObjectName("label_11")
        self.horizontalLayout_6.addWidget(self.label_11)
        self.label_5 = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(11)
        font.setBold(True)
        font.setWeight(75)
        self.label_5.setFont(font)
        self.label_5.setStyleSheet("background-color: rgb(85, 170, 127);")
        self.label_5.setAlignment(QtCore.Qt.AlignCenter)
        self.label_5.setObjectName("label_5")
        self.horizontalLayout_6.addWidget(self.label_5)
        self.verticalLayout_4.addLayout(self.horizontalLayout_6)
        self.horizontalLayout_5 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_5.setObjectName("horizontalLayout_5")
        self.lbl_connection_status = QtWidgets.QLabel(self)
        self.lbl_connection_status.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_connection_status.setObjectName("lbl_connection_status")
        self.horizontalLayout_5.addWidget(self.lbl_connection_status)
        self.lbl_hardwaretest = QtWidgets.QLabel(self)
        self.lbl_hardwaretest.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_hardwaretest.setObjectName("lbl_hardwaretest")
        self.horizontalLayout_5.addWidget(self.lbl_hardwaretest)
        self.lbl_communication = QtWidgets.QLabel(self)
        self.lbl_communication.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_communication.setObjectName("lbl_communication")
        self.horizontalLayout_5.addWidget(self.lbl_communication)
        self.verticalLayout_4.addLayout(self.horizontalLayout_5)
        self.horizontalLayout.addLayout(self.verticalLayout_4)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_2 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_2.setObjectName("horizontalLayout_2")
        self.lbl_showinfo = QtWidgets.QLabel(self)
        font = QtGui.QFont()
        font.setPointSize(14)
        self.lbl_showinfo.setFont(font)
        self.lbl_showinfo.setStyleSheet("background-color: rgb(0, 0, 0);\n"
"color: rgb(255, 255, 127);")
        self.lbl_showinfo.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_showinfo.setObjectName("lbl_showinfo")
        self.horizontalLayout_2.addWidget(self.lbl_showinfo)
        self.lbl_showinfo.setWordWrap(True)  # 启用自动换行
        self.verticalLayout.addLayout(self.horizontalLayout_2)
        self.verticalLayout.setStretch(0, 3)
        self.verticalLayout.setStretch(1, 7)
        self.gridLayout.addLayout(self.verticalLayout, 0, 0, 1, 1)

        self.retranslateUi()
        QtCore.QMetaObject.connectSlotsByName(self)
        self.setWindowFlags(QtCore.Qt.WindowMinimizeButtonHint | QtCore.Qt.WindowCloseButtonHint)  # 只显示最小化按钮和关闭按钮

    def retranslateUi(self):
        _translate = QtCore.QCoreApplication.translate
        self.setWindowTitle(_translate("Form", "Bluetooth硬件检测"))
        self.lbl_Title.setText(_translate("Form", "Bluetooth测试"))
        self.label_3.setText(_translate("Form", "配置信息"))
        self.label_2.setText(_translate("Form", "Bluetooth Device"))
        self.lbl_bt_device.setText(_translate("Form", "N/A"))
        self.label_4.setText(_translate("Form", "检测结果"))
        self.label_6.setText(_translate("Form", "连接"))
        self.label_11.setText(_translate("Form", "硬件检测"))
        self.label_5.setText(_translate("Form", "通讯"))
        self.lbl_connection_status.setText(_translate("Form", "N/A"))
        self.lbl_hardwaretest.setText(_translate("Form", "N/A"))
        self.lbl_communication.setText(_translate("Form", "N/A"))
        self.lbl_showinfo.setText(_translate("Form", "Bluetooth硬件设备检测中.."))

    # 打印的信息
    def showlog(self, log, isPass):
        # Convert list to string if log is a list
        if isinstance(log, list):
            log = ', '.join(str(item) for item in log)

        try:
            if isPass:
                # Set text color to green and keep background black
                self.lbl_showinfo.setStyleSheet("background-color: rgb(0, 0, 0); color: green;")
            else:
                # Set text color to red and keep background black
                self.lbl_showinfo.setStyleSheet("background-color: rgb(0, 0, 0); color: red;")
            self.lbl_showinfo.setText(log)  # Set the log text

            # Log to file
            if isPass:
                self.logger.info(str(log))
            else:
                self.logger.error(str(log))
        except Exception as e:
            print("\033[1;31m" + str(e) + " \033[0m")
            sys.exit(1)

    # 居中
    def center(self):
        # 获取屏幕尺寸
        screen_geometry = app.desktop().availableGeometry()
        # 计算窗口居中位置
        x = (screen_geometry.width() - self.width()) // 2
        y = (screen_geometry.height() - self.height()) // 2
        # 设置窗口位置
        self.move(x, y)

    # 手动关闭窗口
    def closeEvent(self, event):
        # 创建一个消息框,上面有两个按钮:“是”和“否”
        # reply = QMessageBox.question(self, '退出', "你确定要退出吗?", QMessageBox.Yes | QMessageBox.No, QMessageBox.No)

        # 如果用户点击了“是”,则执行event.accept()来关闭窗口
        # if reply == QMessageBox.Yes:
        #    event.accept()
        #    sys.exit(1)
        # else:
        # 如果用户点击了“否”,则忽略event,不关闭窗口
        #    event.ignore()
        sys.exit(1)

if __name__=='__main__':
    app = QApplication(sys.argv)
    win = BluetoothTestWinFrm()
    win.center()  # 居中
    win.show()
    sys.exit(app.exec_())

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值