From bd7463643ab62f4a3ffb05bfa2ee29cc599d3bf4 Mon Sep 17 00:00:00 2001 From: James Deng Date: Mon, 12 Aug 2024 11:28:53 +0000 Subject: [PATCH 1/2] add LICENSE. Signed-off-by: James Deng --- LICENSE | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..fa45329 --- /dev/null +++ b/LICENSE @@ -0,0 +1,28 @@ +BSD 3-Clause License + +Copyright (c) 2024, SpacemiT + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From bb8f6f2c9292e626a74395a419a574f380b043a7 Mon Sep 17 00:00:00 2001 From: James Deng Date: Sat, 31 Aug 2024 14:23:34 +0800 Subject: [PATCH 2/2] Update for v1.0.14 --- cricket/cricket/lang.py | 2 + cricket/cricket/languages.json | 8 +- cricket/cricket/loggermanager.py | 32 +++ cricket/cricket/macro.py | 58 +++++ cricket/cricket/peripheraltestview.py | 271 ++++++++++++++++++++ cricket/cricket/qtview.py | 347 ++++++++------------------ cricket/cricket/singleton.py | 22 ++ cricket/cricket/statusview.py | 170 +++++++++++++ cricket/cricket/testlogmanager.py | 10 + cricket/cricket/utils.py | 27 ++ cricket/cricket/wifimacview.py | 54 ++++ 11 files changed, 758 insertions(+), 243 deletions(-) create mode 100644 cricket/cricket/loggermanager.py create mode 100644 cricket/cricket/macro.py create mode 100644 cricket/cricket/peripheraltestview.py create mode 100644 cricket/cricket/singleton.py create mode 100644 cricket/cricket/statusview.py create mode 100644 cricket/cricket/testlogmanager.py create mode 100644 cricket/cricket/utils.py create mode 100644 cricket/cricket/wifimacview.py diff --git a/cricket/cricket/lang.py b/cricket/cricket/lang.py index 1f53f83..647b44f 100644 --- a/cricket/cricket/lang.py +++ b/cricket/cricket/lang.py @@ -1,7 +1,9 @@ from typing import Union import os import json +from cricket.singleton import SingletonMeta, singleton +@singleton class SimpleLang(object): def __init__(self, file = None): file = file if file else 'languages.json' diff --git a/cricket/cricket/languages.json b/cricket/cricket/languages.json index cc1a206..5754f11 100644 --- a/cricket/cricket/languages.json +++ b/cricket/cricket/languages.json @@ -37,7 +37,9 @@ "wifi": "WiFi", "wifi_mac": "WiFi Mac", - "cpu_temp": "CPU温度" + "cpu_temp": "CPU温度", + "wifi_signal_init":"正在检测WiFi信号强度", + "peripheral_test":"外设性能测试" }, "en": { "title": "Factory Test", @@ -77,6 +79,8 @@ "wifi": "WiFi", "wifi_mac": "WiFi Mac", - "cpu_temp": "CPU_Temp" + "cpu_temp": "CPU_Temp", + "wifi_signal_init":"Checking WiFi signal strength", + "peripheral_test":"Peripheral performance testing" } } \ No newline at end of file diff --git a/cricket/cricket/loggermanager.py b/cricket/cricket/loggermanager.py new file mode 100644 index 0000000..6cece60 --- /dev/null +++ b/cricket/cricket/loggermanager.py @@ -0,0 +1,32 @@ + +import logging +import os +import inspect +import logging.handlers +import sys + +class LoggerManager: + def __init__(self, name='LoggerManager', console_level=logging.INFO): + self.logger = logging.getLogger(name) + self.logger.setLevel(logging.DEBUG) # Set the root log level to DEBUG to capture all logs + + # Create formatter + formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s [%(filename)s:%(lineno)d]') + + # Create and configure the console processor + self.console_handler = logging.StreamHandler(sys.stdout) + self.console_handler.setLevel(console_level) + self.console_handler.setFormatter(formatter) + + # Add a processor to the logger + self.logger.addHandler(self.console_handler) + + # Creating a file processor + self.file_handler = logging.FileHandler("/root/factorytest.log") + self.file_handler.setLevel(logging.INFO) + self.file_handler.setFormatter(formatter) + + self.logger.addHandler(self.file_handler) + + def get_logger(self): + return self.logger \ No newline at end of file diff --git a/cricket/cricket/macro.py b/cricket/cricket/macro.py new file mode 100644 index 0000000..ef7b05c --- /dev/null +++ b/cricket/cricket/macro.py @@ -0,0 +1,58 @@ + +from cricket.model import TestMethod, TestCase, TestModule + +PASS_COLOR = '#28C025' +FAIL_COLOR = '#E32C2E' + +PASSSTR = "PASS" +FAILSTR = "FAIL" +INITSTR = "NA" + +READ_ERROR = 'read error' + +# Display constants for test status +STATUS = { + TestMethod.STATUS_PASS: { + 'description': u'通过', + 'symbol': u'\u25cf', + 'tag': 'pass', + 'color': PASS_COLOR + }, + TestMethod.STATUS_SKIP: { + 'description': u'Skipped', + 'symbol': u'S', + 'tag': 'skip', + 'color': '#259EBF' + }, + TestMethod.STATUS_FAIL: { + 'description': u'失败', + 'symbol': u'F', + 'tag': 'fail', + 'color': FAIL_COLOR + }, + TestMethod.STATUS_EXPECTED_FAIL: { + 'description': u'Expected\n failure', + 'symbol': u'X', + 'tag': 'expected', + 'color': '#3C25BF' + }, + TestMethod.STATUS_UNEXPECTED_SUCCESS: { + 'description': u'Unexpected\n success', + 'symbol': u'U', + 'tag': 'unexpected', + 'color': '#C82788' + }, + TestMethod.STATUS_ERROR: { + 'description': 'Error', + 'symbol': u'E', + 'tag': 'error', + 'color': '#E4742C' + }, +} + +STATUS_DEFAULT = { + 'description': 'Not\nexecuted', + 'symbol': u'', + 'tag': None, + 'color': '#BFBFBF', +} \ No newline at end of file diff --git a/cricket/cricket/peripheraltestview.py b/cricket/cricket/peripheraltestview.py new file mode 100644 index 0000000..b70e858 --- /dev/null +++ b/cricket/cricket/peripheraltestview.py @@ -0,0 +1,271 @@ +from PyQt5.QtCore import Qt, QTimer, QUrl, QObject, QThread, pyqtSignal +from PyQt5.QtGui import QColor, QPixmap, QImage, QPalette +from PyQt5.QtWidgets import (QWidget, QDialog, QFrame, QHBoxLayout, QVBoxLayout, QLabel, +QPushButton,QLineEdit, QCheckBox,QComboBox, QSizePolicy, QSpacerItem, QTextEdit) + +import subprocess +import re +import os +from typing import Tuple, List +from cricket.lang import SimpleLang +from cricket.loggermanager import LoggerManager +from cricket.macro import * +import time + + +class PeripheralTestWindow(QDialog): + def __init__(self, parent): + super().__init__(parent) + self._parent = parent + self.sl = SimpleLang() + + self.info_msg_index = 0 + + self.log_manager = LoggerManager(name='PeripheralLogger') + self.custom_logger = self.log_manager.get_logger() + + self.name_dict = {'EMMC':"EMMC", "SSD":"SSD", 'TF':'TF Card', 'FlashDrive':'U盘'} + + self.th_dict = {'EMMC':50.0*0.25, 'SSD':380*0.25, 'TF':40.0*0.20, 'FlashDrive':50.0*0.25} # key corresponds to name + self.size_dict = {'EMMC':'1GB'} + + self.test_sequence = [] + self.current_test_index = 0 + + self.initUI() + self.test_sequence.append({"type":"storage", "name": "EMMC", "control":self.emmc_control, "output_file":'/dev/mmcblk2', "seek":0}) + self.test_sequence.append({"type":"storage", "name": "SSD", "control":self.ssd_control, "output_file":'/dev/nvme0n1', "seek":0}) + self.test_sequence.append({"type":"storage", "name": "TF", "control":self.tf_card_control, "output_file":'/dev/mmcblk0', "seek":10240}) + self.test_sequence.append({"type":"storage", "name": "FlashDrive", "control":self.flash_drive_control, "output_file":'/dev/sda', "seek":0}) + + def initUI(self): + font_size = 32 + self.setGeometry(200,200, 800, 800) + self.setStyleSheet(f"font-size: {font_size}px;") + self.setWindowTitle('Peripheral Test') + layout = QVBoxLayout() + self.setLayout(layout) + + # frame, [check_box, commbox, speed_show, result_show] + # EMMC + emmc_frame, self.emmc_control = self.gen_storage_device_testing_ui(self.name_dict['EMMC'], font_size=font_size) + layout.addWidget(emmc_frame) + + # SSD + ssd_frame, self.ssd_control = self.gen_storage_device_testing_ui(self.name_dict['SSD'], font_size=font_size) + layout.addWidget(ssd_frame) + + # TF Card + tf_card_frame, self.tf_card_control = self.gen_storage_device_testing_ui(self.name_dict['TF'], font_size=font_size) + layout.addWidget(tf_card_frame) + + # U盘 + flash_drive_frame, self.flash_drive_control = self.gen_storage_device_testing_ui(self.name_dict['FlashDrive'], font_size=font_size) + layout.addWidget(flash_drive_frame) + + # Create a text edit box that displays the run information + self.text_edit = QTextEdit(self) + self.text_edit.setReadOnly(True) + self.text_edit.setFixedHeight(100) + self.text_edit.setStyleSheet("font-size: 18px;") + layout.addWidget(self.text_edit) + + # call button + self.test_button = QPushButton('一键测试', self) + self.test_button.setStyleSheet(f"font-size: {font_size}px;") + self.test_button.clicked.connect(self.test_all) + layout.addWidget(self.test_button) + + + def gen_storage_device_testing_ui(self, storage_name, font_size=32) -> Tuple[QFrame, List[QWidget]]: + storage_frame = QFrame(self) + # storage_frame.setAutoFillBackground(True) + storage_frame.setPalette(QPalette(QColor('darkgray'))) + storage_layout = QHBoxLayout(storage_frame) + + spacer = QSpacerItem(40, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) + storage_layout.addSpacerItem(spacer) + + # check + check_box_frame = QWidget(storage_frame) + check_box_layout = QHBoxLayout(check_box_frame) + storage_check_box = QCheckBox(storage_frame) + storage_check_box.setStyleSheet(""" + QCheckBox::indicator { + width: 32px; + height: 32px; + } + """) + storage_check_box.setChecked(True) + check_box_layout.addWidget(storage_check_box) + + storage_name_label = QLabel(storage_name, check_box_frame) + storage_name_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + check_box_layout.addWidget(storage_name_label) + + storage_layout.addWidget(check_box_frame) + + spacer = QSpacerItem(60, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) + storage_layout.addSpacerItem(spacer) + + # write size + write_size_frame = QWidget(storage_frame) + write_size_frame_layout = QHBoxLayout(write_size_frame) + write_text_label = QLabel('写入大小:', write_size_frame) + write_text_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + write_text_label.setStyleSheet(f"font-size: {font_size}px;") + write_size_frame_layout.addWidget(write_text_label) + + storage_comm_box = QComboBox(write_size_frame) + storage_comm_box.addItems(['1GB', '2GB', '3GB', '4GB']) + storage_comm_box.setStyleSheet(f"font-size: {font_size}px;") + storage_comm_box.currentText() + write_size_frame_layout.addWidget(storage_comm_box) + storage_layout.addWidget(write_size_frame) + + spacer = QSpacerItem(60, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) + storage_layout.addSpacerItem(spacer) + + # speed result + speed_frame = QWidget(storage_frame) + speed_frame_layout = QHBoxLayout(speed_frame) + speed_text_label = QLabel('速度:', speed_frame) + speed_text_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + speed_text_label.setStyleSheet(f"font-size: {font_size}px;") + speed_frame_layout.addWidget(speed_text_label) + + storage_speed_show = QLabel(' ', speed_frame) + storage_speed_show.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + storage_speed_show.setStyleSheet(f"font-size: {font_size}px;") + speed_frame_layout.addWidget(storage_speed_show) + storage_layout.addWidget(speed_frame) + + spacer = QSpacerItem(32, 20, QSizePolicy.Fixed, QSizePolicy.Minimum) + storage_layout.addSpacerItem(spacer) + # speed result judgment + judgment_frame = QWidget(storage_frame) + judgment_frame_layout = QHBoxLayout(judgment_frame) + judgment_text_label = QLabel('结果:', judgment_frame) + judgment_text_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + judgment_text_label.setStyleSheet(f"font-size: {font_size}px;") + judgment_frame_layout.addWidget(judgment_text_label) + + judgment_show = QLabel('未测', judgment_frame) + judgment_show.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + judgment_show.setStyleSheet(f"font-size: {font_size}px;") + judgment_frame_layout.addWidget(judgment_show) + storage_layout.addWidget(judgment_frame) + + return storage_frame, [storage_check_box, storage_comm_box, storage_speed_show, judgment_show] + + + def test_all(self): + self.test_button.setEnabled(False) + self.text_edit.append(f"{self.info_msg_index} : 开始测试所有被勾选项目................") + self.info_msg_index += 1 + self.run_next_test() + + def run_next_test(self): + if self.current_test_index < len(self.test_sequence): + test_info = self.test_sequence[self.current_test_index] + + if test_info["type"] == "storage": + of = test_info["output_file"] + if test_info["name"] == "FlashDrive": + of = self.find_sda_to_sdf_devices() + self.test_storage_wrapper(test_info["control"], test_info["name"], output_file=of, seek=test_info["seek"]) + elif test_info["type"] == "network": + pass + else: + self.test_button.setEnabled(True) + self.text_edit.append(f"{self.info_msg_index} : 所有测试项目已完成") + self.info_msg_index += 1 + self.current_test_index = 0 + + # storage function + def test_storage_wrapper(self, storage_control, test_name, output_file='/dev/mmcblk2', seek=0): + if storage_control[0].isChecked(): + self.text_edit.append(f"{self.info_msg_index} : 开始测试{self.name_dict[test_name]}写入速度................") + self.info_msg_index += 1 + size = storage_control[1].currentText() + size = int(size[0]) * 1024 + + self.thread_storage = StorageTestThread(output_file=output_file, size=size, seek=seek) + self.thread_storage.task_finished.connect( + lambda msg, storage_control=storage_control, test_name=test_name:self.test_storage_postprocessing(msg, storage_control, test_name)) + self.thread_storage.start() + else: + self.current_test_index += 1 + self.run_next_test() + + def test_storage_postprocessing(self, msg, storage_control, test_name): + ret = msg[0] + speed = msg[1] + time_cst = msg[2] + if type(ret) == str: + storage_control[2].setText(ret) + else: + storage_control[2].setText(f"{speed} MB/S") + th = self.th_dict[test_name] + if speed>=th: + storage_control[3].setText(f"通过") + storage_control[3].setStyleSheet(f"color: {PASS_COLOR};") + else: + storage_control[3].setText(f"失败") + storage_control[3].setStyleSheet(f"color: {FAIL_COLOR};") + self.text_edit.append(f"{self.info_msg_index} : {self.name_dict[test_name]}写入速度测试完成, 耗时为{time_cst}s, 返回:{ret}") + self.info_msg_index += 1 + + test_info = self.test_sequence[self.current_test_index] + if test_info["type"] == "storage": + self.thread_storage.quit() + self.thread_storage.wait() + del self.thread_storage + elif test_info["type"] == "network": + pass + + # Move to the next test item + self.current_test_index += 1 + self.run_next_test() + + def find_sda_to_sdf_devices(self): + try: + file_list = ['/dev/'+sub for sub in ['sda', 'sdb', 'sdc', 'sdd', 'sde', 'sdf']] + for device in file_list: + if os.path.exists(device): + return device + return '/dev/noexist' + except Exception as e: + return '/dev/noexist' + +class StorageTestThread(QThread): + # Defines a signal that notifies the main thread when a thread task has completed + task_finished = pyqtSignal(list) + + def __init__(self, output_file='', size='', seek=''): + super().__init__() + + self.output_file = output_file + self.size = size + self.seek = seek + self.speed = None + self.ret = None + + def run(self): + t_start = time.time() + self.ret = self.test_storage_write_speed(output_file=self.output_file, count=self.size, seek=self.seek) + t_period = time.time() - t_start + speed = float(self.size)/t_period + self.speed = round(speed, 1) + self.task_finished.emit([self.ret, self.speed, round(t_period, 1)]) + + def test_storage_write_speed(self, output_file, bs="1M", count=1024, seek=0): + try: + if not os.path.exists(output_file): + return "设备不存在" + + command = ["dd", f"if=/dev/zero", f"of={output_file}", f"bs={bs}", f"count={count}", "oflag=direct", f"seek={seek}"] + result = subprocess.run(command, stderr=subprocess.PIPE, text=True) + return 0 + except Exception as e: + return f"dd命令执行异常{e}" \ No newline at end of file diff --git a/cricket/cricket/qtview.py b/cricket/cricket/qtview.py index 537f62c..0b1a0aa 100644 --- a/cricket/cricket/qtview.py +++ b/cricket/cricket/qtview.py @@ -40,76 +40,30 @@ from importlib import import_module from cricket.model import TestMethod, TestCase, TestModule from cricket.executor import Executor from cricket.lang import SimpleLang - -PASS_COLOR = '#28C025' -FAIL_COLOR = '#E32C2E' - -# Display constants for test status -STATUS = { - TestMethod.STATUS_PASS: { - 'description': u'通过', - 'symbol': u'\u25cf', - 'tag': 'pass', - 'color': '#28C025', - }, - TestMethod.STATUS_SKIP: { - 'description': u'Skipped', - 'symbol': u'S', - 'tag': 'skip', - 'color': '#259EBF' - }, - TestMethod.STATUS_FAIL: { - 'description': u'失败', - 'symbol': u'F', - 'tag': 'fail', - 'color': '#E32C2E' - }, - TestMethod.STATUS_EXPECTED_FAIL: { - 'description': u'Expected\n failure', - 'symbol': u'X', - 'tag': 'expected', - 'color': '#3C25BF' - }, - TestMethod.STATUS_UNEXPECTED_SUCCESS: { - 'description': u'Unexpected\n success', - 'symbol': u'U', - 'tag': 'unexpected', - 'color': '#C82788' - }, - TestMethod.STATUS_ERROR: { - 'description': 'Error', - 'symbol': u'E', - 'tag': 'error', - 'color': '#E4742C' - }, -} - -STATUS_DEFAULT = { - 'description': 'Not\nexecuted', - 'symbol': u'', - 'tag': None, - 'color': '#BFBFBF', -} +from cricket.macro import * +from cricket.statusview import StatusView +from cricket.peripheraltestview import PeripheralTestWindow +from cricket.wifimacview import WifiMacView -class MainWindow(QMainWindow, SimpleLang): +class MainWindow(QMainWindow): def __init__(self, root): super().__init__() - + self.sl = SimpleLang() self._project = None self.test_table = {} self.test_list = {} self.run_status = {} self.executor = {} - + self.usb_list = [] - + self.set_brightness() - + self.wifi_mac_Ok = False self.root = root - self.setWindowTitle(self.get_text('title')) + self.setWindowTitle(self.sl.get_text('title')) # self.showFullScreen() self.font_size = 14 @@ -117,6 +71,8 @@ class MainWindow(QMainWindow, SimpleLang): # Set up the main content for the window. self._setup_main_content() + self.peripheral_test_view = PeripheralTestWindow(self) + # Set up listeners for runner events. Executor.bind('test_status_update', self.on_executorStatusUpdate) Executor.bind('test_start', self.on_executorTestStart) @@ -127,7 +83,7 @@ class MainWindow(QMainWindow, SimpleLang): ###################################################### # Internal GUI layout methods. ###################################################### - + def _setup_main_content(self): ''' The button toolbar runs as a horizontal area at the top of the GUI. @@ -146,30 +102,30 @@ class MainWindow(QMainWindow, SimpleLang): toolbar = QFrame(self.content) layout = QGridLayout(toolbar) - self.run_all_button = QPushButton(self.get_text('run_all_button'), toolbar) + self.run_all_button = QPushButton(self.sl.get_text('run_all_button'), toolbar) self.run_all_button.clicked.connect(self.cmd_run_all) self.run_all_button.setFocus() layout.addWidget(self.run_all_button, 0, 0) - self.run_selected_button = QPushButton(self.get_text('run_selected_button'), + self.run_selected_button = QPushButton(self.sl.get_text('run_selected_button'), toolbar) self.run_selected_button.setDisabled(True) self.run_selected_button.clicked.connect(self.cmd_run_selected) layout.addWidget(self.run_selected_button, 0, 1) - self.stop_button = QPushButton(self.get_text('stop_button'), toolbar) + self.stop_button = QPushButton(self.sl.get_text('stop_button'), toolbar) self.stop_button.setDisabled(True) self.stop_button.clicked.connect(self.cmd_stop) layout.addWidget(self.stop_button, 0 , 2) - self.reboot_button = QPushButton(self.get_text('reboot_button'), toolbar) + self.reboot_button = QPushButton(self.sl.get_text('reboot_button'), toolbar) self.reboot_button.clicked.connect(self.cmd_reboot) layout.addWidget(self.reboot_button, 0, 3) - self.poweroff_button = QPushButton(self.get_text('poweroff_button'), toolbar) + self.poweroff_button = QPushButton(self.sl.get_text('poweroff_button'), toolbar) self.poweroff_button.clicked.connect(self.cmd_poweroff) layout.addWidget(self.poweroff_button, 0, 4) - + self.content_layout.addWidget(toolbar) # tests @@ -188,7 +144,7 @@ class MainWindow(QMainWindow, SimpleLang): self._setup_usb_frame(5, 0, 1, 1) self._setup_test_table('manual', 6, 0, 4, 1) - camera_box = QGroupBox(self.get_text('camera'), self.tests) + camera_box = QGroupBox(self.sl.get_text('camera'), self.tests) camera_box_layout = QVBoxLayout(camera_box) video_widget = QVideoWidget(camera_box) self.media_player = QMediaPlayer() @@ -220,39 +176,39 @@ class MainWindow(QMainWindow, SimpleLang): # set main content to window self.setCentralWidget(self.content) - + def _setup_info(self): info = QFrame(self.content) info_layout = QGridLayout(info) - cpu_model = QLabel(f'{self.get_text("cpu_model")}: {self._get_CPU_model()}', info) + cpu_model = QLabel(f'{self.sl.get_text("cpu_model")}: {self._get_CPU_model()}', info) info_layout.addWidget(cpu_model, 0, 0) - cpu_freq = QLabel(f'{self.get_text("cpu_freq")}: {self._get_CPU_freq()} GHz', info) + cpu_freq = QLabel(f'{self.sl.get_text("cpu_freq")}: {self._get_CPU_freq()} GHz', info) info_layout.addWidget(cpu_freq, 0, 1) - - self.cpu_temp = QLabel(f'{self.get_text("cpu_temp")}: {self._get_CPU_Temp()} °C', info) + + self.cpu_temp = QLabel(f'{self.sl.get_text("cpu_temp")}: {self._get_CPU_Temp()} °C', info) self._cpu_temp_timer = QTimer(self) self._cpu_temp_timer.timeout.connect(self.on_cpuTempUpdate) - self._cpu_temp_timer.start(1000) + self._cpu_temp_timer.start(1000) info_layout.addWidget(self.cpu_temp, 0, 2) - ddr_size = QLabel(f'{self.get_text("ddr_size")}: {self._get_DDR_size()} GB', info) + ddr_size = QLabel(f'{self.sl.get_text("ddr_size")}: {self._get_DDR_size()} GB', info) info_layout.addWidget(ddr_size, 0, 3) - emmc_size = QLabel(f'{self.get_text("emmc_size")}: {self._get_eMMC_size()} GB', info) + emmc_size = QLabel(f'{self.sl.get_text("emmc_size")}: {self._get_eMMC_size()} GB', info) info_layout.addWidget(emmc_size, 0, 4) - ssd_size = QLabel(f'{self.get_text("ssd_size")}: {self._get_SSD_size()} GB', info) + ssd_size = QLabel(f'{self.sl.get_text("ssd_size")}: {self._get_SSD_size()} GB', info) info_layout.addWidget(ssd_size, 0, 5) - - self.hdmi_model = QLabel(f'{self.get_text("hdmi_model")}: None', info) + + self.hdmi_model = QLabel(f'{self.sl.get_text("hdmi_model")}: None', info) info_layout.addWidget(self.hdmi_model, 0, 6) - product_name = QLabel(f'{self.get_text("product_name")}: {self._get_product_name()}', info) + product_name = QLabel(f'{self.sl.get_text("product_name")}: {self._get_product_name()}', info) info_layout.addWidget(product_name, 0, 7) - fw_version = QLabel(f'{self.get_text("fw_version")}: {self._get_fw_version()}', info) + fw_version = QLabel(f'{self.sl.get_text("fw_version")}: {self._get_fw_version()}', info) info_layout.addWidget(fw_version, 0, 8) self.content_layout.addWidget(info) @@ -260,11 +216,11 @@ class MainWindow(QMainWindow, SimpleLang): def _setup_test_table(self, name, row, column, row_span, column_span): module = import_module(name) - box = QGroupBox(module.MODULE_NAME[self.current_lang], self.tests) + box = QGroupBox(module.MODULE_NAME[self.sl.current_lang], self.tests) box.setStyleSheet("QGroupBox::title { font-weight: bold; }") layout = QVBoxLayout(box) - columns = self.get_text('test_table_head') + columns = self.sl.get_text('test_table_head') table = QTableWidget(box) table.setStyleSheet('QTableWidget { background-color: black; color: white; }') @@ -287,14 +243,14 @@ class MainWindow(QMainWindow, SimpleLang): self.tests_layout.addWidget(box, row, column, row_span, column_span) def _setup_others(self): - self.others_box = QGroupBox(self.get_text('others'), self.tests) + self.others_box = QGroupBox(self.sl.get_text('others'), self.tests) self.others_box_layout = QVBoxLayout(self.others_box) # Comment the code below if you don't need it # item self._setup_others_item() - + # status self._setup_others_status() @@ -304,8 +260,9 @@ class MainWindow(QMainWindow, SimpleLang): self.others_box_layout.addWidget(self.others_item) self._setup_others_test() - - self._setup_wifi_mac() + + self.wifi_mac_view = WifiMacView(self.others_item) + self.others_item_layout.addWidget(self.wifi_mac_view) sn = self._get_sn() if sn: @@ -320,38 +277,49 @@ class MainWindow(QMainWindow, SimpleLang): lcd_frame.setAutoFillBackground(True) lcd_frame.setPalette(QPalette(QColor('darkgray'))) lcd_layout = QHBoxLayout(lcd_frame) - + # lcd_button = QPushButton(self.get_text('lcd'), lcd_frame) # lcd_button.clicked.connect(self.cmd_lcd) # lcd_layout.addWidget(lcd_button) # lcd backlight - backlight_label = QLabel(self.get_text('lcd_backlight')+" :", lcd_frame) + backlight_label = QLabel(self.sl.get_text('lcd_backlight')+" :", lcd_frame) backlight_label.setAlignment( Qt.AlignVCenter) lcd_layout.addWidget(backlight_label) - + # Create a slider lcd_slider = QSlider(Qt.Horizontal, lcd_frame) - lcd_slider.setRange(0, 255) - lcd_slider.setValue(128) + lcd_slider.setRange(0, 255) + lcd_slider.setValue(128) lcd_slider.valueChanged.connect(self.set_brightness) lcd_layout.addWidget(lcd_slider) - + label_brightness = QLabel("128", lcd_frame) label_brightness.setAlignment(Qt.AlignRight | Qt.AlignVCenter) label_brightness.setFixedWidth(24) lcd_slider.valueChanged.connect(lambda value: label_brightness.setText(str(value))) lcd_layout.addWidget(label_brightness) - + others_test_layout.addWidget(lcd_frame) + # peripheral test + peripheral_frame = QFrame(others_test) + peripheral_frame.setAutoFillBackground(True) + peripheral_frame.setPalette(QPalette(QColor('darkgray'))) + peripheral_layout = QHBoxLayout(peripheral_frame) + self.peripheral_test_button = QPushButton(self.sl.get_text('peripheral_test'), peripheral_frame) + self.peripheral_test_button.setAutoFillBackground(True) + self.peripheral_test_button.clicked.connect(self.cmd_peripheral_test) + peripheral_layout.addWidget(self.peripheral_test_button) + others_test_layout.addWidget(peripheral_frame) + # aging test aging_test = QFrame(others_test) aging_test.setAutoFillBackground(True) aging_test.setPalette(QPalette(QColor('darkgray'))) aging_test_layout = QVBoxLayout(aging_test) - self.aging_button = QPushButton(self.get_text('aging_test'), aging_test) + self.aging_button = QPushButton(self.sl.get_text('aging_test'), aging_test) self.aging_button.setAutoFillBackground(True) self.aging_button.clicked.connect(self.cmd_aging) aging_test_layout.addWidget(self.aging_button) @@ -374,7 +342,7 @@ class MainWindow(QMainWindow, SimpleLang): aging_duration = QFrame(aging_test) aging_duration_layout = QHBoxLayout(aging_duration) - aging_duration_label = QLabel(f'{self.get_text("aging_duration")}: ', aging_duration) + aging_duration_label = QLabel(f'{self.sl.get_text("aging_duration")}: ', aging_duration) aging_duration_layout.addWidget(aging_duration_label) self.aging_duration_choice = QComboBox(aging_test) self.aging_duration_choice.addItems(['4', '8', '12', '24']) @@ -383,40 +351,16 @@ class MainWindow(QMainWindow, SimpleLang): aging_test_layout.addWidget(aging_duration) others_test_layout.addWidget(aging_test) - + self.others_item_layout.addWidget(others_test) - - def _setup_wifi_mac(self): - mac = self._get_wifi_mac() - if mac: - self._setup_wifi_mac_qrcode(mac) - else: - QTimer.singleShot(2000, self._setup_wifi_mac) - - # wifi signal part + + + # wifi signal part def _setup_others_status(self): - self.others_status = QFrame(self.others_box) - self.others_status_layout = QHBoxLayout(self.others_status) - self.others_box_layout.addWidget(self.others_status) - self._setup_wifi_view() - def _setup_wifi_view(self): - wifi = QFrame(self.others_status) - wifi_layout = QGridLayout(wifi) + self.others_status_view = StatusView(self.others_box) + self.others_box_layout.addWidget(self.others_status_view) - self.others_status_layout.addWidget(wifi) - - wifi_label = QLabel(f'{self.get_text("wifi")}: ', wifi) - wifi_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) - wifi_layout.addWidget(wifi_label, 0, 0) - - self.wifi_signal_level = QProgressBar(wifi) - self.wifi_signal_level.setAlignment(Qt.AlignLeft) - self.wifi_signal_level.setMaximum(5) - wifi_layout.addWidget(self.wifi_signal_level, 0, 1) - - QTimer.singleShot(3000, lambda: self.on_wifiStatusUpdate()) - # [start] Check the usb to see if the device is inserted def usb_loop(self, label, usb_path): while True: @@ -441,17 +385,17 @@ class MainWindow(QMainWindow, SimpleLang): def _setup_usb_frame(self, row, column, row_span, column_span): self.usb_frame = QFrame(self.tests) self.usb_frame_layout = QGridLayout(self.usb_frame) - + self._add_usb_test('USB A口 (左上) 2.0', 0, 0, '/sys/bus/usb/devices/usb2/2-1/2-1.1/product') self._add_usb_test('USB A口 (左上) 3.0', 1, 0, '/sys/bus/usb/devices/usb3/3-1/3-1.1/product') - + self._add_usb_test('USB A口 (左下) 2.0', 0, 1, '/sys/bus/usb/devices/usb2/2-1/2-1.4/product') self._add_usb_test('USB A口 (左下) 3.0', 1, 1, '/sys/bus/usb/devices/usb3/3-1/3-1.4/product') - + self._add_usb_test('USB A口 (右上) 2.0', 0, 2, '/sys/bus/usb/devices/usb2/2-1/2-1.3/product') self._add_usb_test('USB A口 (右上) 3.0', 1, 2, @@ -461,7 +405,7 @@ class MainWindow(QMainWindow, SimpleLang): '/sys/bus/usb/devices/usb2/2-1/2-1.2/product') self._add_usb_test('USB A口 (右下) 3.0', 1, 3, '/sys/bus/usb/devices/usb3/3-1/3-1.2/product') - + self.tests_layout.addWidget(self.usb_frame, row, column, row_span, column_span) # [end] Check the usb to see if the device is inserted @@ -488,26 +432,13 @@ class MainWindow(QMainWindow, SimpleLang): qr_label.setPixmap(self._create_qrcode(sn)) sn_qrcode_layout.addWidget(qr_label) - sn_label = QLabel(f'{self.get_text("sn")}: {sn}', sn_qrcode) + sn_label = QLabel(f'{self.sl.get_text("sn")}: {sn}', sn_qrcode) sn_label.setAlignment(Qt.AlignTop | Qt.AlignHCenter) sn_qrcode_layout.addWidget(sn_label) self.others_item_layout.addWidget(sn_qrcode) - def _setup_wifi_mac_qrcode(self, mac): - mac_qrcode = QFrame(self.others_item) - mac_qrcode_layout = QVBoxLayout(mac_qrcode) - qr_label = QLabel(mac_qrcode) - qr_label.setAlignment(Qt.AlignCenter) - qr_label.setPixmap(self._create_qrcode(mac)) - mac_qrcode_layout.addWidget(qr_label) - - mac_label = QLabel(f'{self.get_text("wifi_mac")}: {mac}', mac_qrcode) - mac_label.setAlignment(Qt.AlignTop | Qt.AlignHCenter) - mac_qrcode_layout.addWidget(mac_label) - - self.others_item_layout.addWidget(mac_qrcode) ###################################################### # Handlers for setting a new project ###################################################### @@ -521,7 +452,7 @@ class MainWindow(QMainWindow, SimpleLang): testcase = getattr(module, subModuleName) if hasattr(testcase, 'LANGUAGES'): langs = getattr(testcase, 'LANGUAGES') - lang = langs.get(self.current_lang) + lang = langs.get(self.sl.current_lang) if lang is not None: text = lang.get(key) if text is not None: @@ -580,7 +511,7 @@ class MainWindow(QMainWindow, SimpleLang): pipeline = 'gst-pipeline: spacemitsrc location=/opt/factorytest/res/camtest_sensor0_mode0.json close-dmabuf=1 ! videoconvert ! video/x-raw,format=BGRx ! autovideosink sync=0' self.media_player.setMedia(QMediaContent(QUrl(pipeline))) self.media_player.play() - + self.hdmi_thread = threading.Thread(target=self.hdmi_loop) self.hdmi_thread.start() @@ -590,7 +521,7 @@ class MainWindow(QMainWindow, SimpleLang): self.cmd_run_all() self.root.exec_() - + def hdmi_loop(self): card = '/sys/class/drm/card2-HDMI-A-1' if os.path.exists(card): @@ -611,25 +542,25 @@ class MainWindow(QMainWindow, SimpleLang): if line.strip().startswith('Model'): model = line.strip().split(':')[1].strip() - self.hdmi_model.setText(f'{self.get_text("hdmi_model")}: {manufacturer} {model}') + self.hdmi_model.setText(f'{self.sl.get_text("hdmi_model")}: {manufacturer} {model}') def _play_wav(self, device, volume, path): cmd = f'amixer -c 1 cset numid=1,iface=MIXER,name="DAC Playback Volume" {volume}' proc = subprocess.run(cmd, shell=True, capture_output=True, text=True) - print(f'Set playback volume to {volume} return {proc.returncode}') + # print(f'Set playback volume to {volume} return {proc.returncode}') cmd = f'aplay -D{device} -r 48000 -f S16_LE {path}' proc = subprocess.run(cmd, shell=True, capture_output=True, text=True) - print(f'Play {path} on {device} return {proc.returncode}') + # print(f'Play {path} on {device} return {proc.returncode}') def _record_wav(self, device, volume, duration, path): cmd = f'amixer -c 1 cset numid=1,iface=MIXER,name="ADC Capture Volume" {volume},{volume}' proc = subprocess.run(cmd, shell=True, capture_output=True, text=True) - print(f'Set capture volume to {volume} return {proc.returncode}') + # print(f'Set capture volume to {volume} return {proc.returncode}') cmd = f'arecord -D{device} -r 48000 -f S16_LE -d {duration} {path}' proc = subprocess.run(cmd, shell=True, capture_output=True, text=True) - print(f'Record {path} on {device} in {duration}s return {proc.returncode}') + # print(f'Record {path} on {device} in {duration}s return {proc.returncode}') def audio_loop(self): # sleep for a while @@ -661,7 +592,7 @@ class MainWindow(QMainWindow, SimpleLang): # color = self.lcd_color_list[self.lcd_color_index % len(self.lcd_color_list)] # self.lcd_color_index += 1 # self.setPalette(QPalette(QColor(color))) - + def set_brightness(self, brightness = 128): path = '/sys/devices/platform/soc/soc:lcd_backlight/backlight/soc:lcd_backlight/brightness' try: @@ -669,13 +600,13 @@ class MainWindow(QMainWindow, SimpleLang): f.write(f'{brightness}') except: pass - + # def keyPressEvent(self, event): # super().keyPressEvent(event) # def mousePressEvent(self, event): # super().mousePressEvent(event) - + ###################################################### # User commands ###################################################### @@ -684,7 +615,7 @@ class MainWindow(QMainWindow, SimpleLang): # self.content.setVisible(False) # self.lcd_color_index = 0 # self.update_lcd_color() - + def cmd_poweroff(self): self.media_player.stop() self.stop() @@ -727,7 +658,7 @@ class MainWindow(QMainWindow, SimpleLang): if data is not None: labels.append(data) - if labels and (not self.executor[module] or + if labels and (not self.executor[module] or not self.executor[module].is_running): self.run(module, labels=labels) @@ -738,7 +669,7 @@ class MainWindow(QMainWindow, SimpleLang): remaining_seconds = seconds % 60 return hours, minutes, remaining_seconds - + def update_aging_hints(self, error: str = None): hours, minutes, seconds = self._convert_seconds(self.aging_elapse) hints = '正在进行老化测试...\n' @@ -747,7 +678,7 @@ class MainWindow(QMainWindow, SimpleLang): if error: hints += error self.aging_dialog.setLabelText(hints) - + def start_aging_test(self): self.cpu_aging_proc = None self.ddr_aging_proc = None @@ -859,15 +790,15 @@ class MainWindow(QMainWindow, SimpleLang): self.vpu_aging_proc.kill() self.vpu_aging_proc.wait() self.vpu_aging_proc = None - + def cmd_aging(self): self.aging_duration = int(self.aging_duration_choice.currentText()) * 3600 self.aging_elapse = 0 self.aging_pass = True - self.aging_dialog = QProgressDialog('', f'{self.get_text("aging_cancel")}', + self.aging_dialog = QProgressDialog('', f'{self.sl.get_text("aging_cancel")}', 0, self.aging_duration, self) - self.aging_dialog.setWindowTitle(self.get_text('aging_test')) + self.aging_dialog.setWindowTitle(self.sl.get_text('aging_test')) self.update_aging_hints() self.aging_dialog.setValue(0) self.aging_dialog.setAutoClose(True) @@ -893,6 +824,11 @@ class MainWindow(QMainWindow, SimpleLang): self.aging_button.setPalette(QPalette(QColor(PASS_COLOR))) else: self.aging_button.setPalette(QPalette(QColor(FAIL_COLOR))) + + # + def cmd_peripheral_test(self): + self.peripheral_test_view.exec_() + ###################################################### # GUI Callbacks ###################################################### @@ -902,77 +838,6 @@ class MainWindow(QMainWindow, SimpleLang): # update "run selected" button enabled state self.set_selected_button_state() - # wifi signal part - def on_wifiStatusUpdate(self): - self.set_wifi_signal_level() - QTimer.singleShot(3000, lambda: self.on_wifiStatusUpdate()) - - def set_wifi_signal_level(self): - ssid, signal_level = self._get_strongest_wifi() - if not ssid: - return - - if signal_level <= -100: - self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) - value = 1 - elif signal_level <= -88: # (-100, -88] - self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) - value = 2 - elif signal_level <= -77: # (-88, -77] - self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) - value = 3 - elif signal_level <= -55: # (-77, -55] - self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % PASS_COLOR) - value = 4 - else: # > -55 - self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % PASS_COLOR) - value = 5 - - self.wifi_signal_level.setFormat(f'{ssid}: {signal_level}') - self.wifi_signal_level.setValue(value) - - def _get_strongest_wifi(self): - strongest_signal_level = -2147483648 - strongest_ssid = None - - timeout = 10 - cmd = 'wpa_cli scan' - scan = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=timeout) - if scan.returncode != 0: - return strongest_ssid, strongest_signal_level - - cmd = 'wpa_cli scan_results' - proc = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=timeout) - if proc.returncode != 0: - return strongest_ssid, strongest_signal_level - - for line in proc.stdout.splitlines(): - if not line.strip() or line.startswith('Selected interface') or line.startswith('bssid'): - continue - - parts = line.split('\t') - if len(parts) < 5: - continue - - signal_level = int(parts[2]) - ssid = parts[4] - - if signal_level > strongest_signal_level: - strongest_signal_level = signal_level - strongest_ssid = ssid - - return strongest_ssid, strongest_signal_level - - # wifi mac part - def _get_wifi_mac(self): - cmd = 'ifconfig wlan0' - proc = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=2) - - for line in proc.stdout.splitlines(): - pattern = 'HWaddr' - if line.find(pattern) > 0: - return line.split(pattern)[1].strip() - def _get_sn(self): path = '/proc/device-tree/serial-number' if os.path.exists(path): @@ -1002,54 +867,54 @@ class MainWindow(QMainWindow, SimpleLang): if os.path.exists(path): with open(path, 'r') as f: return round(int(f.readline().strip()) / 1000 / 1000 / 2, 1) - + def _get_DDR_size(self): with open('/proc/meminfo', 'r') as f: for line in f.readlines(): if line.startswith('MemTotal:'): return round(int(line.split()[1]) / 1024 / 1024, 0) - + def _get_CPU_model(self): with open('/proc/cpuinfo', 'r') as f: for line in f.readlines(): if line.startswith('model name'): return line.split(':')[1].strip() - + def _get_CPU_freq(self): with open('/sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq', 'r') as f: return round(int(f.readline().strip()) / 1000 / 1000, 1) # cpu temp part def on_cpuTempUpdate(self): - self.cpu_temp.setText(f'{self.get_text("cpu_temp")}: {self._get_CPU_Temp()} °C') - + self.cpu_temp.setText(f'{self.sl.get_text("cpu_temp")}: {self._get_CPU_Temp()} °C') + def _get_CPU_Temp(self): thermal_base_path = "/sys/class/thermal/" ret = "None" - + try: # Traverse the thermal_zone* directory for zone in os.listdir(thermal_base_path): zone_path = os.path.join(thermal_base_path, zone) type_path = os.path.join(zone_path, "type") - + # Check whether the type file exists if os.path.isfile(type_path): with open(type_path, 'r') as type_file: type_content = type_file.read().strip() - + # Check whether the type file content matches if type_content == "cluster0_thermal": temp_path = os.path.join(zone_path, "temp") - + if os.path.isfile(temp_path): with open(temp_path, 'r') as temp_file: temp_content = temp_file.read().strip() - temp_content = int(temp_content)//1000 - ret = str(temp_content) + temp_content = int(temp_content)//1000 + ret = str(temp_content) except Exception as e: print(f"An error occurred when getting cpu temperature: {e}") - + return ret def on_nodeStatusUpdate(self, node): diff --git a/cricket/cricket/singleton.py b/cricket/cricket/singleton.py new file mode 100644 index 0000000..da56bf1 --- /dev/null +++ b/cricket/cricket/singleton.py @@ -0,0 +1,22 @@ + + +def singleton(cls): + instances = {} + def get_instance(*args, **kwargs): + if cls not in instances: + instances[cls] = cls(*args, **kwargs) + return instances[cls] + return get_instance + + +class SingletonMeta(type): + """ + A metaclass that ensures a class follows the Singleton pattern. + All instances of the class and its subclasses share the same instance. + """ + _instances = {} + + def __call__(cls, *args, **kwargs): + if cls not in cls._instances: + cls._instances[cls] = super().__call__(*args, **kwargs) + return cls._instances[cls] \ No newline at end of file diff --git a/cricket/cricket/statusview.py b/cricket/cricket/statusview.py new file mode 100644 index 0000000..3f83964 --- /dev/null +++ b/cricket/cricket/statusview.py @@ -0,0 +1,170 @@ +from PyQt5.QtCore import Qt, QTimer +from PyQt5.QtGui import QColor, QKeyEvent, QMouseEvent, QPixmap, QImage, QPalette +from PyQt5.QtWidgets import QFrame, QHBoxLayout, QVBoxLayout, QGridLayout, QLabel, QProgressBar, QSlider +import subprocess +from cricket.lang import SimpleLang +from cricket.loggermanager import LoggerManager +from cricket.macro import * +from cricket.utils import * + +class StatusView(QFrame): + def __init__(self, parent): + super().__init__(parent) + self._setup_others_status() + + def _setup_others_status(self): + self.others_status_layout = QHBoxLayout(self) + + # wifi signal + self.wifi_signal_view = WifiSignalView(self) + self.others_status_layout.addWidget(self.wifi_signal_view) + self.wifi_signal_view.start_to_scan() + + +class WifiSignalView(QFrame): + def __init__(self, parent): + super().__init__(parent) + self.sl = SimpleLang() + self.log_manager = LoggerManager(name='WiFiSignalView') + self.logger = self.log_manager.get_logger() + + self.wifi_text = self.sl.get_text("wifi") + self.wifi_ready = False + + self.setup_wifi_signal_with_ui() + + def setup_wifi_signal_with_ui(self): + wifi_layout = QGridLayout(self) + + wifi_label = QLabel(f'{self.wifi_text}: ', self) + wifi_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + wifi_layout.addWidget(wifi_label, 0, 0) + + self.wifi_signal_level = QProgressBar(self) + self.wifi_signal_level.setAlignment(Qt.AlignLeft) + self.wifi_signal_level.setMaximum(100) + + wifi_layout.addWidget(self.wifi_signal_level, 0, 1) + + self.logger.info(f'{self.sl.get_text("wifi_signal_init")}') + self.wifi_signal_level.setFormat(f'{self.sl.get_text("wifi_signal_init")}') + self.wifi_signal_level.setValue(0) + + def start_to_scan(self): + self.on_wifiStatusUpdate() + + def on_wifiStatusUpdate(self): + interface = 'wlan0' + status = self.check_interface_status(interface) + wifi_module_name = '8852bs' + kernel_module_wifi_status = self.check_module_loaded(wifi_module_name) + wpa_supplicant_status = self.check_process_running('wpa_supplicant') + if status and kernel_module_wifi_status and wpa_supplicant_status: + self.wifi_ready = True + pr = f"Interface {interface} is {status}" + self.set_wifi_signal_level() + else: + pr = f"fail wifi: {interface}:{status}, {wifi_module_name}:{kernel_module_wifi_status}, wpa_supplicant:{wpa_supplicant_status}" + self.logger.info(pr) + self.wifi_signal_level.setFormat(f'{self.sl.get_text("wifi_signal_init")}') + self.wifi_signal_level.setValue(0) + + QTimer.singleShot(1000, lambda: self.on_wifiStatusUpdate()) + + def check_interface_status(self, interface): + try: + # Execute a command ip link show interface + result = subprocess.run(['ip', 'link', 'show', interface], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + + # Check the return code of the command execution + if result.returncode != 0: + print(f"Error: {result.stderr}") + return None + + # Analytic output + output = result.stdout + if interface in output: + return True + else: + return False + except Exception as e: + print(f"Exception occurred: {e}") + return False + + def check_module_loaded(self, module_name): + """Check whether the kernel module is loaded successfully""" + result = subprocess.run(['lsmod'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if module_name in result.stdout: + return True + return False + + def check_process_running(self, process_name): + """Check whether the process is running""" + result = subprocess.run(['ps', 'aux'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) + if process_name in result.stdout: + return True + return False + + def set_wifi_signal_level(self): + ssid, signal_level = self._get_strongest_wifi() + if not ssid: + self.wifi_signal_level.setFormat(f'{self.sl.get_text("wifi_signal_init")}') + # self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) + self.wifi_signal_level.setValue(0) + return + + if signal_level <= -100: + self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) + value = 20 + elif signal_level <= -88: # (-100, -88] + self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) + value = 40 + elif signal_level <= -55: # (-88, -55] + self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % FAIL_COLOR) + value = 60 + elif signal_level <= -33: # (-33, -55] + self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % PASS_COLOR) + value = 80 + else: # > -33 + self.wifi_signal_level.setStyleSheet('QProgressBar { text-align: center; } QProgressBar::chunk { background-color: %s; }' % PASS_COLOR) + value = 100 + + + # The value is a string encoded in UTF-8 + byte_str = bytes(ssid, 'latin1').decode('unicode_escape').encode('latin1') + decoded_str = byte_str.decode('utf-8') + self.wifi_signal_level.setFormat(f'{decoded_str}: {signal_level}') + self.wifi_signal_level.setValue(value) + + def _get_strongest_wifi(self): + strongest_signal_level = -2147483648 + strongest_ssid = None + + timeout = 5 + cmd = 'wpa_cli scan' + scan = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=timeout) + # if scan.returncode != 0: + # return strongest_ssid, strongest_signal_level + + cmd = 'wpa_cli scan_results' + proc = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=timeout) + if proc.returncode != 0: + return strongest_ssid, strongest_signal_level + + for line in proc.stdout.splitlines(): + if not line.strip() or line.startswith('Selected interface') or line.startswith('bssid'): + continue + + parts = line.split('\t') + if len(parts) < 5: + continue + + signal_level = int(parts[2]) + ssid = parts[4] + + if signal_level > strongest_signal_level: + strongest_signal_level = signal_level + strongest_ssid = ssid + + return strongest_ssid, strongest_signal_level + diff --git a/cricket/cricket/testlogmanager.py b/cricket/cricket/testlogmanager.py new file mode 100644 index 0000000..96477ac --- /dev/null +++ b/cricket/cricket/testlogmanager.py @@ -0,0 +1,10 @@ + +from PyQt5.QtCore import Qt, QTimer, QObject + +class DeviceStatusManager(QObject): + def __init__(self): + super().__init__() + self.time_sync = False + self.wifi_sync = False + self.check_mode = 1 + self.set_file_name_flag = False \ No newline at end of file diff --git a/cricket/cricket/utils.py b/cricket/cricket/utils.py new file mode 100644 index 0000000..c025323 --- /dev/null +++ b/cricket/cricket/utils.py @@ -0,0 +1,27 @@ +from PyQt5.QtGui import QPixmap, QImage +from PyQt5.QtCore import Qt, QTimer, QObject +from PIL.ImageQt import ImageQt +import os +import qrcode + +def get_product_name(): + path = '/proc/device-tree/model' + if os.path.exists(path): + with open(path, 'r') as f: + model = f.readline().strip(b'\0x00'.decode()) + return model.replace('spacemit', '').replace('board', '').strip() + +def create_qrcode(data): + qr = qrcode.QRCode( + version=1, + error_correction=qrcode.constants.ERROR_CORRECT_L, + box_size=8, + border=0, + ) + qr.add_data(data) + qr.make(fit=True) + + img = qr.make_image(fill='black', back_color='white') + qt_image = ImageQt(img).convertToFormat(QImage.Format_RGB32) + return QPixmap.fromImage(qt_image) + diff --git a/cricket/cricket/wifimacview.py b/cricket/cricket/wifimacview.py new file mode 100644 index 0000000..5e14e82 --- /dev/null +++ b/cricket/cricket/wifimacview.py @@ -0,0 +1,54 @@ +from PyQt5.QtCore import Qt, QTimer +from PyQt5.QtWidgets import QFrame,QVBoxLayout,QLabel +import subprocess +from cricket.lang import SimpleLang +from cricket.utils import * +import re + +class WifiMacView(QFrame): + def __init__(self, parent): + super().__init__(parent) + self.sl = SimpleLang() + self.wifi_mac_text = self.sl.get_text("wifi_mac") + + self.setup_wifi_mac_with_ui() + + def setup_wifi_mac_with_ui(self): + mac = self._get_wifi_mac() + if mac: + self._setup_wifi_mac_qrcode(mac) + else: + QTimer.singleShot(2000, self.setup_wifi_mac_with_ui) + + def _get_wifi_mac(self): + cmd = 'ifconfig wlan0' + proc = subprocess.run(cmd, capture_output=True, text=True, shell=True, timeout=1) + + text = proc.stdout + pattern = r"HWaddr\s+([0-9A-Fa-f:]{17})" + + match = re.search(pattern, text) + if match: + mac_address = match.group(1) + return mac_address + else: + return None + + def _setup_wifi_mac_qrcode(self, mac): + mac_qrcode_layout = QVBoxLayout(self) + + qr_label = QLabel(self) + qr_label.setAlignment(Qt.AlignCenter) + qr_label.setPixmap(create_qrcode(mac)) + mac_qrcode_layout.addWidget(qr_label) + + mac_label = QLabel(f'{self.wifi_mac_text}: {mac}', self) + mac_label.setAlignment(Qt.AlignTop | Qt.AlignHCenter) + mac_qrcode_layout.addWidget(mac_label) + + self.mac_layout = mac_qrcode_layout + + # wifi ip + self.wifi_ip = QLabel(' ', self) + self.wifi_ip.setAlignment(Qt.AlignTop | Qt.AlignHCenter) + self.mac_layout.addWidget(self.wifi_ip) \ No newline at end of file